fix analogstick, add minimum range and press deadzone, add movement touch to digital buttons depending on layers

This commit is contained in:
Karim Mreisi
2015-02-03 21:51:27 +01:00
26 changed files with 1069 additions and 481 deletions

View File

@@ -6,7 +6,6 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
@@ -15,25 +14,27 @@ import java.util.List;
*/
public class AnalogStick extends VirtualControllerElement
{
protected static boolean _PRINT_DEBUG_INFORMATION = true;
float radius_complete = 0;
float radius_minimum = 0;
float radius_dead_zone = 0;
float radius_analog_stick = 0;
float radius_complete = 0;
float radius_dead_zone = 0;
float radius_analog_stick = 0;
float position_pressed_x = 0;
float position_pressed_y = 0;
float position_pressed_x = 0;
float position_pressed_y = 0;
float position_moved_x = 0;
float position_moved_y = 0;
float position_stick_x = 0;
float position_stick_y = 0;
float position_stick_x = 0;
float position_stick_y = 0;
Paint paint = new Paint();
boolean viewPressed = false;
boolean analogStickActive = false;
_STICK_STATE stick_state = _STICK_STATE.NO_MOVEMENT;
_CLICK_STATE click_state = _CLICK_STATE.SINGLE;
List<AnalogStickListener> listeners = new ArrayList<AnalogStickListener>();
List<AnalogStickListener> listeners = new ArrayList<>();
OnTouchListener onTouchListener = null;
private long timeoutDoubleClick = 250;
private long timeLastClick = 0;
@@ -80,9 +81,10 @@ public class AnalogStick extends VirtualControllerElement
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
radius_complete = getPercent(getCorrectWidth() / 2, 40);
radius_dead_zone = getPercent(getCorrectWidth() / 2, 20);
radius_analog_stick = getPercent(getCorrectWidth() / 2, 20);
radius_complete = getPercent(getCorrectWidth() / 2, 90);
radius_minimum = getPercent(getCorrectWidth() / 2, 30);
radius_dead_zone = getPercent(getCorrectWidth() / 2, 10);
radius_analog_stick = getPercent(getCorrectWidth() / 2, 20);
super.onSizeChanged(w, h, oldw, oldh);
}
@@ -93,12 +95,11 @@ public class AnalogStick extends VirtualControllerElement
// set transparent background
canvas.drawColor(Color.TRANSPARENT);
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(getPercent(getCorrectWidth() / 2, 2));
// draw outer circle
if (!viewPressed || click_state == _CLICK_STATE.SINGLE)
if (!isPressed() || click_state == _CLICK_STATE.SINGLE)
{
paint.setColor(normalColor);
}
@@ -107,47 +108,36 @@ public class AnalogStick extends VirtualControllerElement
paint.setColor(pressedColor);
}
canvas.drawRect(0, 0,
getWidth(), getHeight(),
paint);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius_complete, paint);
paint.setColor(normalColor);
// draw dead zone
if (analogStickActive)
{
canvas.drawCircle(position_pressed_x, position_pressed_y, radius_dead_zone, paint);
}
else
{
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius_dead_zone, paint);
}
// draw minimum
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius_minimum, paint);
// draw stick depending on state (no movement, moved, active(out of dead zone))
if (analogStickActive)
switch (stick_state)
{
switch (stick_state)
case NO_MOVEMENT:
{
case NO_MOVEMENT:
{
paint.setColor(normalColor);
canvas.drawCircle(position_stick_x, position_stick_y, radius_analog_stick, paint);
paint.setColor(normalColor);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius_analog_stick, paint);
break;
}
case MOVED:
{
paint.setColor(pressedColor);
canvas.drawCircle(position_stick_x, position_stick_y, radius_analog_stick, paint);
break;
}
break;
}
case MOVED_IN_DEAD_ZONE:
{
paint.setColor(normalColor);
canvas.drawCircle(position_stick_x, position_stick_y, radius_analog_stick, paint);
break;
}
case MOVED_ACTIVE:
{
paint.setColor(pressedColor);
canvas.drawCircle(position_stick_x, position_stick_y, radius_analog_stick, paint);
break;
}
}
else
{
paint.setColor(normalColor);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius_analog_stick, paint);
}
super.onDraw(canvas);
@@ -256,116 +246,79 @@ public class AnalogStick extends VirtualControllerElement
}
}
private void updatePosition(float x, float y)
private void updatePosition()
{
float way_x;
float way_y;
// get real way for each axis
float way_center_x = -(getWidth() / 2 - position_moved_x);
float way_center_y = -(getHeight() / 2 - position_moved_y);
if (x > position_pressed_x)
{
way_x = x - position_pressed_x;
// get radius and angel of movement from center
double movement_radius = getMovementRadius(way_center_x, way_center_y);
double movement_angle = getAngle(way_center_x, way_center_y);
if (way_x > radius_complete)
{
way_x = radius_complete;
}
}
else
{
way_x = -(position_pressed_x - x);
// get dead zone way for each axis
float way_pressed_x = position_pressed_x - position_moved_x;
float way_pressed_y = position_pressed_y - position_moved_y;
if (way_x < -radius_complete)
{
way_x = -radius_complete;
}
}
// get radius and angel from pressed position
double movement_dead_zone_radius = getMovementRadius(way_pressed_x, way_pressed_y);
if (y > position_pressed_y)
{
way_y = y - position_pressed_y;
if (way_y > radius_complete)
{
way_y = radius_complete;
}
}
else
{
way_y = -(position_pressed_y - y);
if (way_y < -radius_complete)
{
way_y = -radius_complete;
}
}
float movement_x = 0;
float movement_y = 0;
double movement_radius = getMovementRadius(way_x, way_y);
//double movement_angle = getAngle(way_x, way_y);
/*
// chop radius if out of outer circle
if (movement_radius > (radius_complete - radius_analog_stick))
{
movement_radius = radius_complete - radius_analog_stick;
}
// calculate new positions
float correlated_y =
(float) (Math.sin(Math.PI / 2 - movement_angle) * (movement_radius));
float correlated_x =
(float) (Math.cos(Math.PI / 2 - movement_angle) * (movement_radius));
float complete = (radius_complete - radius_analog_stick);
float complete = (radius_complete - radius_analog_stick - radius_minimum);
movement_x = -(1 / complete) * correlated_x;
movement_y = (1 / complete) * correlated_y;
float movement_x;
float movement_y;
*/
movement_x = -(1 / complete) * (correlated_x - (correlated_x > 0 ? radius_minimum : -radius_minimum));
movement_y = (1 / complete) * (correlated_y - (correlated_y > 0 ? radius_minimum : -radius_minimum));
movement_x = (1 / radius_complete) * way_x;
movement_y = -(1 / radius_complete) * way_y;
position_stick_x = getWidth() / 2 - correlated_x;
position_stick_y = getHeight() / 2 - correlated_y;
position_stick_x = position_pressed_x + way_x;
position_stick_y = position_pressed_y + way_y;
// check if analog stick is outside of dead zone and minimum
if (movement_radius > radius_minimum && movement_dead_zone_radius > radius_dead_zone)
{
// set active
stick_state = _STICK_STATE.MOVED_ACTIVE;
}
// check if analog stick is outside of dead zone
if (movement_radius > radius_dead_zone)
if (stick_state == _STICK_STATE.MOVED_ACTIVE)
{
moveActionCallback(movement_x, movement_y);
stick_state = _STICK_STATE.MOVED;
}
else
{
stick_state = _STICK_STATE.NO_MOVEMENT;
}
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
if (onTouchListener != null)
{
return onTouchListener.onTouch(this, event);
}
// get masked (not specific to a pointer) action
int action = event.getActionMasked();
_CLICK_STATE lastClickState = click_state;
boolean wasActive = analogStickActive;
position_moved_x = event.getX();
position_moved_y = event.getY();
switch (action)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
{
position_pressed_x = event.getX();
position_pressed_y = event.getY();
setPressed(true);
position_pressed_x = position_moved_x;
position_pressed_y = position_moved_y;
stick_state = _STICK_STATE.MOVED_IN_DEAD_ZONE;
analogStickActive = true;
viewPressed = true;
// check for double click
if (lastClickState == _CLICK_STATE.SINGLE && timeLastClick + timeoutDoubleClick > System.currentTimeMillis())
{
@@ -384,15 +337,11 @@ public class AnalogStick extends VirtualControllerElement
break;
}
case MotionEvent.ACTION_MOVE:
{
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
{
analogStickActive = false;
viewPressed = false;
setPressed(false);
stick_state = _STICK_STATE.NO_MOVEMENT;
revokeActionCallback();
@@ -400,13 +349,12 @@ public class AnalogStick extends VirtualControllerElement
}
}
// no longer pressed reset movement
if (analogStickActive)
if (isPressed())
{ // when is pressed calculate new positions (will trigger movement if necessary)
updatePosition(event.getX(), event.getY());
updatePosition();
}
else if (wasActive)
{
else
{ // not longer pressed reset analog stick
moveActionCallback(0, 0);
}
@@ -419,7 +367,8 @@ public class AnalogStick extends VirtualControllerElement
private enum _STICK_STATE
{
NO_MOVEMENT,
MOVED
MOVED_IN_DEAD_ZONE,
MOVED_ACTIVE
}

View File

@@ -17,19 +17,97 @@ import java.util.TimerTask;
*/
public class DigitalButton extends VirtualControllerElement
{
List<DigitalButtonListener> listeners = new ArrayList<DigitalButtonListener>();
static List<DigitalButton> allButtonsList = new ArrayList<>();
List<DigitalButtonListener> listeners = new ArrayList<>();
OnTouchListener onTouchListener = null;
boolean clicked;
private String text = "";
private int icon = -1;
private long timerLongClickTimeout = 3000;
private Timer timerLongClick = null;
private TimerLongClickTimerTask longClickTimerTask = null;
public DigitalButton(Context context)
private int layer;
private DigitalButton movingButton = null;
boolean inRange(float x, float y)
{
return (this.getX() < x && this.getX() + this.getWidth() > x) &&
(this.getY() < y && this.getY() + this.getHeight() > y);
}
public boolean checkMovement(float x, float y, DigitalButton movingButton)
{
// check if the movement happened in the same layer
if (movingButton.layer != this.layer)
{
return false;
}
// save current pressed state
boolean wasPressed = isPressed();
// check if the movement directly happened on the button
if ((this.movingButton == null || movingButton == this.movingButton)
&& this.inRange(x, y))
{
// set button pressed state depending on moving button pressed state
if (this.isPressed() != movingButton.isPressed())
{
this.setPressed(movingButton.isPressed());
}
}
// check if the movement is outside of the range and the movement button
// is saved moving button
else if (movingButton == this.movingButton)
{
this.setPressed(false);
}
// check if a change occurred
if (wasPressed != isPressed())
{
if (isPressed())
{ // is pressed set moving button and emit click event
this.movingButton = movingButton;
onClickCallback();
}
else
{ // no longer pressed reset moving button and emit release event
this.movingButton = null;
onReleaseCallback();
}
invalidate();
return true;
}
return false;
}
private void checkMovementForAllButtons(float x, float y)
{
for (DigitalButton button : allButtonsList)
{
if (button != this)
{
button.checkMovement(x, y, this);
}
}
}
public DigitalButton(int layer, Context context)
{
super(context);
clicked = false;
this.layer = layer;
allButtonsList.add(this);
}
public void addDigitalButtonListener(DigitalButtonListener listener)
@@ -66,10 +144,10 @@ public class DigitalButton extends VirtualControllerElement
paint.setTextAlign(Paint.Align.CENTER);
paint.setStrokeWidth(3);
paint.setColor(clicked ? pressedColor : normalColor);
paint.setColor(isPressed() ? pressedColor : normalColor);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(
1, 1,
1, 1,
getWidth() - 1, getHeight() - 1,
paint
);
@@ -142,6 +220,9 @@ public class DigitalButton extends VirtualControllerElement
}
*/
// get masked (not specific to a pointer) action
float x = getX() + event.getX();
float y = getY() + event.getY();
int action = event.getActionMasked();
switch (action)
@@ -149,20 +230,29 @@ public class DigitalButton extends VirtualControllerElement
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
{
clicked = true;
movingButton = null;
setPressed(true);
onClickCallback();
invalidate();
return true;
}
case MotionEvent.ACTION_MOVE:
{
checkMovementForAllButtons(x, y);
return true;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
{
clicked = false;
setPressed(false);
onReleaseCallback();
checkMovementForAllButtons(x, y);
invalidate();
return true;

View File

@@ -10,6 +10,9 @@ import com.limelight.R;
import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.ControllerPacket;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Karim Mreisi on 30.11.2014.
*/
@@ -135,7 +138,7 @@ public class VirtualController
buttonA = createDigitalButton("A", ControllerPacket.A_FLAG, context);
buttonB = createDigitalButton("B", ControllerPacket.B_FLAG, context);
buttonLT = new DigitalButton(context);
buttonLT = new DigitalButton(2, context);
buttonLT.setText("LT");
buttonLT.addDigitalButtonListener(new DigitalButton.DigitalButtonListener()
{
@@ -162,7 +165,7 @@ public class VirtualController
}
});
buttonRT = new DigitalButton(context);
buttonRT = new DigitalButton(2, context);
buttonRT.setText("RT");
buttonRT.addDigitalButtonListener(new DigitalButton.DigitalButtonListener()
{
@@ -267,7 +270,7 @@ public class VirtualController
buttonSelect =
createDigitalButton("SELECT", ControllerPacket.SPECIAL_BUTTON_FLAG, context);
buttonConfigure = new DigitalButton(context);
buttonConfigure = new DigitalButton(1, context);
buttonConfigure.setIcon(R.drawable.settings);
buttonConfigure.addDigitalButtonListener(new DigitalButton.DigitalButtonListener()
{
@@ -412,7 +415,7 @@ public class VirtualController
private DigitalButton createDigitalButton(String text, final int key, Context context)
{
DigitalButton button = new DigitalButton(context);
DigitalButton button = new DigitalButton(1, context);
button.setText(text);
button.addDigitalButtonListener(new DigitalButton.DigitalButtonListener()
{

View File

@@ -21,7 +21,7 @@ public abstract class VirtualControllerElement extends View
{
if (_PRINT_DEBUG_INFORMATION)
{
System.out.println("DigitalButton: " + text);
System.out.println(text);
}
}