patternjavaMinor
Dual Analog Stick Controls
Viewed 0 times
dualstickcontrolsanalog
Problem
I started this question to get people to grasp a little bit more on how to develop analog controls for android devices. But my code is a little outdated so if anyone knows how to implement it better please post your answer, as there are a lots of bugs in the code below as you'll see.
Note: this is only if your app requires a custom built in analog controls. If you have a library that could implement it better then I suggest to use it. Also the analog controls developed here are mostly for use in landscape mode, but it can be altered to support portrait. Also be sure to check if your device supports multitouch (though if it has at least 800Mhz processor in 99% it does).
This is how your controls would optimally look like. If this is for a game then Move1 would be for walking forward and backward, move2 to for strafe left and right. Rotate1 for looking up and down and rotate2 for left and right.
First thing you should do is get your device resolution in on create:
Help variables only position your screen analog controllers.
This would be your touch listener:
```
public boolean onTouchEvent(MotionEvent me) {
int pointerCount = me.getPointerCount(),i=0;
int actionCode = me.getAction() & MotionEvent.ACTION_MASK;
while(ihelp1)
{float alo = resolutionx/10;
float hej = (5*resolutiony)/6;
alo=xpos-alo;
hej=ypos-hej;
hej=-(hej/((5*resolutiony)/6));
alo=alo/(resolutionx/10);
move1=hej;
move2=alo;}
if(xpos>help3 && ypos>help1)
{float alo = (resolutionx*9)/10;
float hej = (5*resolutiony)/6;
alo=xpos-alo;
hej=ypos-hej;
he
Note: this is only if your app requires a custom built in analog controls. If you have a library that could implement it better then I suggest to use it. Also the analog controls developed here are mostly for use in landscape mode, but it can be altered to support portrait. Also be sure to check if your device supports multitouch (though if it has at least 800Mhz processor in 99% it does).
This is how your controls would optimally look like. If this is for a game then Move1 would be for walking forward and backward, move2 to for strafe left and right. Rotate1 for looking up and down and rotate2 for left and right.
First thing you should do is get your device resolution in on create:
DisplayMetrics gettotalY = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(gettotalY);
resolutiony= gettotaly.heightPixels;
resolutionx= gettotaly.widthPixels;
help1=(2*resolutiony)/3;
help2=resolutionx/5;
help3=(4*resolutionx)/5;Help variables only position your screen analog controllers.
This would be your touch listener:
```
public boolean onTouchEvent(MotionEvent me) {
int pointerCount = me.getPointerCount(),i=0;
int actionCode = me.getAction() & MotionEvent.ACTION_MASK;
while(ihelp1)
{float alo = resolutionx/10;
float hej = (5*resolutiony)/6;
alo=xpos-alo;
hej=ypos-hej;
hej=-(hej/((5*resolutiony)/6));
alo=alo/(resolutionx/10);
move1=hej;
move2=alo;}
if(xpos>help3 && ypos>help1)
{float alo = (resolutionx*9)/10;
float hej = (5*resolutiony)/6;
alo=xpos-alo;
hej=ypos-hej;
he
Solution
Initial refactoring
Your code was hard to follow, so I applied a few mechanical transformations to clean it up first.
Let's start by renaming a few variables.
In
Your code was hard to follow, so I applied a few mechanical transformations to clean it up first.
Let's start by renaming a few variables.
void init() {
DisplayMetrics screen = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(screen);
// "resolution" to me sounds like it should be in DPI. Rename!
screenHeight = screen.heightPixels;
screenWidth = screen.widthPixels;
horizon = (2 * screenHeight) / 3;
left = (1 * screenWidth) / 5;
right = (4 * screenWidth) / 5;
}In
onTouchEvent(), you have a weaselly i=0, followed two lines later by while (i
alo ranges from 0.0 to 1.0 within each control zone which seems reasonable, but hej ranges from 0.0 to 0.2.
- If a pointer is dragged outside the control zone but not released, the event handler keeps updating
move1, move2, rotate1, and rotate2 to some possibly crazy values. (alo can have values from -8.0 to +8.0; hej can have values from -0.2 to +1.0.)
My recommendations:
- If you want circular-looking controls, then specify each control zone as a center and a radius.
- If a pointer is dragged outside the control zone not not released, I think that a reasonable behaviour might be to report the orientation, but cap the magnitude of the reading at the control zone radius.
Pointer lifecycle
There is another bug: If the pointer is released outside the gray rectangles, then that control will continue on autopilot. I suspect that that is not what you intended.
A pointer starts out with an ACTION_DOWN/ACTION_POINTER_DOWN action, followed by ACTION_MOVE actions, and ends with an ACTION_UP/ACTION_POINTER_UP` action. You want two modes:- For down, if the coordinates are within either control zone, note which control that pointer is associated with (using an instance variable).
- For move, update the coordinates of the control associated with the pointer, if it is associated with a control zone.
- For up, reset the readings of the control to 0, if the pointer is associated with a control zone.
Code Snippets
void init() {
DisplayMetrics screen = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(screen);
// "resolution" to me sounds like it should be in DPI. Rename!
screenHeight = screen.heightPixels;
screenWidth = screen.widthPixels;
horizon = (2 * screenHeight) / 3;
left = (1 * screenWidth) / 5;
right = (4 * screenWidth) / 5;
}public boolean onTouchEvent(MotionEvent me) {
int pointerCount = me.getPointerCount();
for (int p = 0; p < pointerCount; p++) {
int pointerId = me.getPointerId(p);
// Shouldn't xpos and ypos be local?
xpos = me.getX(pointerId);
ypos = me.getY(pointerId);
if (xpos < left && ypos > horizon) { // LHS control
float alo = screenWidth /10;
float hej = (5 * screenHeight) / 6;
alo = xpos - alo;
hej = ypos - hej;
hej = -(hej / ((5 * screenHeight) / 6));
alo = alo / (screenWidth / 10);
move1 = hej;
move2 = alo;
} else if (xpos > right && ypos > horizon) { // RHS control
float alo = (screenWidth * 9) / 10;
float hej = (5 * screenHeight) / 6;
alo = xpos - alo;
hej = ypos - hej;
hej = -(hej / ((5 * screenHeight) / 6));
alo = alo / (screenWidth / 10);
rotate2 = hej;
rotate1 = alo;
}
}
int actionCode = me.getAction() & MotionEvent.ACTION_MASK;
if (me.getActionIndex() == 0 && actionCode == MotionEvent.ACTION_UP)
{
xpos = me.getX(me.getPointerId(0));
ypos = me.getY(me.getPointerId(0));
if (xpos < left && ypos > horizon) {
move1 = move2 = 0;
} else if (xpos > right && ypos > horizon) {
rotate1 = rotate2 = 0;
}
}
if (me.getActionIndex() == 1 && actionCode == MotionEvent.ACTION_POINTER_UP)
{
xpos = me.getX(me.getPointerId(1));
ypos = me.getY(me.getPointerId(1));
if (xpos < left && ypos > horizon) {
move1 = move2 = 0;
} else if (xpos > right && ypos > horizon) {
rotate1 = rotate2 = 0;
}
}
try {
// FIXME: WHY IS THIS DELAY NEEDED???
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}
return super.onTouchEvent(me);
}if (xpos > right && ypos > horizon) { // RHS control
float alo = (screenWidth * 9) / 10;
float hej = (5 * screenHeight) / 6;
alo = xpos - alo;
hej = ypos - hej;
hej = -(hej / ((5 * screenHeight) / 6));
alo = alo / (screenWidth / 10);
rotate2 = hej;
rotate1 = alo;
}Context
StackExchange Code Review Q#25135, answer score: 3
Revisions (0)
No revisions yet.