I am trying to check what is touching the screen whenever it is pressed, a pointer is moved, or a touch is released.
The problem is that when a touch is released, getPointerCount() includes the pointer that just left the screen in its count. And so when I try to gather all the current X and Y values, it includes an x/y pair that has left the screen.
I am currently doing something like:
int count = e.getPointerCount();
for(i = 0; i < count; i++) {
x = e.getX(i);
y = e.getY(i);
/* do stuff with them */
}
Is there a way of getting the X and Y coordinates of that pointer that left the screen, or remove the pointer that left the screen and get only coordinates that are touching the screen?
Thanks
Have you tried using the onTouchEvent in your application?
import android.view.MotionEvent;
...
private int mActivePointerId;
protected void onCreate(Bundle savedInstanceState){
...
}
public boolean onTouchEvent(MotionEvent event){
// Get the pointer ID
mActivePointerId = event.getPointerId(0);
// ... Many touch events later...
// Use the pointer ID to find the index of the active pointer
// and fetch its position
int pointerIndex = event.findPointerIndex(mActivePointerId);
// Get the pointer's current position
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
return true;
}
I think the method you want is getActionIndex(). From the documentation, it seems that when getActionMasked() returns ACTION_POINTER_UP, this method will return the corresponding pointer index and you can then ignore it in your loop (or do something else with it).
Related
I have an if statement which checks for collision as such:
if (BallY == y && BallX == x) // check for collision
{
x = 80; // reset x to initial
y = 240; // reset y to initial
z = 100; //reset z to initial
}
I have a for loop inside this if statement as such:
if (BallY == y && BallX == x) // check for collision
{
x = 80; // reset x to initial
y = 240; // reset y to initial
z = 100; //reset z to initial
for (int i=50; i<width; i+=80)
{
fill(250,0,0);
ellipse(i, 50, 70, 70);
}
}
So the point is to draw a line of circles on the top of the screen once the collision occurs. This code however, only draws them for a split second then they disappear. How would I make them stay given that a collision has occurred?
You might want to use a boolean value that tracks whether the ball has collided. Set it to true when you detect a collision, and then check the value to decide what to draw. Here's a simplified example:
boolean drawRed = false;
void draw() {
if (mouseY > height/2) {
drawRed = true;
}
if (drawRed) {
background(255, 0, 0);
}
}
This code draws a gray background by default, but then turns to red if the mouse goes in the lower half of the window. It stays red even if you move the mouse back to the top part.
This is just a simple example, but the idea is the same: use a variable to track the state of the sketch, set that variable when your condition is met, and check that variable to decide what to draw.
By the way, your collision detection is a little bit suspicious. You probably don't want to check whether the ball is at an exact location. Instead you probably want to check whether the ball overlaps some area. Here is a guide on collision detection in Processing that might be useful.
If you still can't get it working, please narrow your problem down to a MCVE instead of posting disconnected snippets or your full sketch. Good luck!
I'm developing a 2D space shooter game with the player only moving left and right. I've rendered a button on the left and the right side of the screen and constantly check if it's touched. The problem with this is that you have to lift your finger of the button in order to press the button on the other side of the screen. I want the ship to be moving towards the last touched part of the screen(Even when you actually didn't lift the finger of your first-touched button).
public void keyListener(float delta){
//right movement
if(Gdx.input.isKeyPressed(Keys.RIGHT) || (Gdx.input.isTouched() && game.cam.getInputInGameWorld().x >= arrowMoveX - 40) && !isPlayerHit)
x+=SPEED*Gdx.graphics.getDeltaTime();
//left movement
if(Gdx.input.isKeyPressed(Keys.LEFT) || (Gdx.input.isTouched() && game.cam.getInputInGameWorld().x < arrowMoveWhite.getWidth() + 40) && !isPlayerHit)
x-=SPEED*Gdx.graphics.getDeltaTime();
I've tried puting another if statements in these to check for a second movement, but this way it only works on one direction.
Can you please help me?
What you need to know about for this is that Android indexes each separate touch in order of when the screen was touched, the index is known as a "pointer". For example, when you touch the screen with only one finger, the touch pointer is 0, and the second touch pointer is 1. The highest pointer that libGDX registers is 20.
For your particular situation, you want to read only the input that is on the highest pointer that is currently reading a touch, and have an int reading the highest touch. You can loop through the pointers, and set the int to whatever touch event is actually the highest pointer which is referring to the most recent press like this:
int highestpointer = -1; // Setting to -1 because if the pointer is -1 at the end of the loop, then it would be clear that there was no touch
for(int pointer = 0; pointer < 20; pointer++) {
if(Gdx.input.isTouched(pointer)) { // First check if there is a touch in the first place
int x = Gdx.input.getX(pointer); // Get x position of touch in screen coordinates (far left of screen will be 0)
if(x < arrowMoveWhite.getWidth() + 40 || x >= arrowMoveX - 40) {
highestpinter = pointer;
} // Note that if the touch is in neither button, the highestpointer will remain what ever it was previously
}
} // At the end of the loop, the highest pointer int would be the most recent touch, or -1
// And to handle actual movement you need to pass the highest pointer into Gdx.input.getX()
if(!isPlayerHit) { // Minor improvement: only check this once
if(Gdx.input.isKeyPressed(Keys.RIGHT) || (highestpointer > -1 && Gdx.input.getX(highestpointer) >= arrowMoveX - 40)) {
x+=SPEED*Gdx.graphics.getDeltaTime();
} else if(Gdx.input.isKeyPressed(Keys.LEFT) || (highestpointer > -1 && Gdx.input.getX(highestpointer) < arrowMoveWhite.getWidth() + 40)) {
x-=SPEED*Gdx.graphics.getDeltaTime();
}
}
Note that you will probably want a separate camera drawing your buttons (or any hud elements) because you would not need to worry about translating screen coordinates to world coordinates since x goes in the same direction.
Let me know how that works and if you need any changes!
I receive axes position from my bluetooth gamepad controller in dispatchGenericMotionEvent(android.view. MotionEvent) method.
My method:
#Override
public boolean dispatchGenericMotionEvent(final MotionEvent event) {
if( mPadListener==null ||
(event.getSource()&InputDeviceCompat.SOURCE_JOYSTICK)!=InputDeviceCompat.SOURCE_JOYSTICK ){
return super.dispatchGenericMotionEvent(event);
}
int historySize = event.getHistorySize();
for (int i = 0; i < historySize; i++) {
// Process the event at historical position i
Log.d("JOYSTICKMOVE",event.getHistoricalAxisValue(MotionEvent.AXIS_Y,i)+" "+event.getHistoricalAxisValue(MotionEvent.AXIS_Z,i));
}
// Process current position
Log.d("JOYSTICKMOVE",event.getAxisValue(MotionEvent.AXIS_Y)+" "+event.getAxisValue(MotionEvent.AXIS_Z));
return true;
}
The problem is that when I release all joystick axes, I'm not getting last axes values (0,0) in my log. It stops for example in (0.23,0.11) and appropriate values appear in logcat only after next move event. What's more - situation is the same even if I press normal button (button event is catch by completely other method dispatchKeyEvent(android.view.KeyEvent) )
What's going on ?
You get a MotionEvent.ACTION_MOVE event for the zero position, but the value you receive is not necessarily zero. You need to get the flat range of the joystick, which gives you the values at which we should consider the joystick to be at rest (ie. if we are below the flat range, then we're at the zero position). See getCenteredAxis, which corrects for the flat range (https://developer.android.com/training/game-controllers/controller-input.html):
private static float getCenteredAxis(MotionEvent event,
InputDevice device, int axis, int historyPos) {
final InputDevice.MotionRange range =
device.getMotionRange(axis, event.getSource());
// A joystick at rest does not always report an absolute position of
// (0,0). Use the getFlat() method to determine the range of values
// bounding the joystick axis center.
if (range != null) {
final float flat = range.getFlat();
final float value =
historyPos < 0 ? event.getAxisValue(axis):
event.getHistoricalAxisValue(axis, historyPos);
// Ignore axis values that are within the 'flat' region of the
// joystick axis center.
if (Math.abs(value) > flat) {
return value;
}
}
return 0;
}
I have a problem with my Android app that requires touch tracking events (tracking when/where finger goes down, move, up, etc). I have to use event.getX() and event.getY() functions to get the current touch's coordinates.
So from what I've learned in the past few months:
MotionEvent.ACTION_DOWN tracks the first touch down
MotionEvent.ACTION_POINTER_DOWN tracks subsequent touches down
MotionEvent.ACTION_UP tracks the last touch up
MotionEvent.ACTION_POINTER_UP tracks touch that goes up
MotionEvent.ACTION_MOVE tracks any touch movement
In my app, I'm encountering a significant problem when my first touch goes up. Lets say I have five fingers touching my device (lets call these Touch 0, 1, 2, 3, 4). Now, when I lift up Touch 0, MotionEvent.ACTION_POINTER_UP is the action I get. Totally understandable; I get this. However, now these two things will happen:
Move anything from Touches 1-4: Get an IllegalArgumentException telling me the pointerIndex is out of range
Lift up anything from Touches 1-4: Spits back information for a different touch (event.getX() and event.getY() will give me a different finger information)
I'm kind of at my wit's end on how to properly track this information. Any clues on how to properly track the information or offset the touch pointers?
I provided the general layout of what my code is but I feel like I'm not doing anything out of the ordinary (and I'm sure it is close to the example code on the Android examples?):
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getActionMasked();
int ptr_idx = event.getPointerId(event.getActionIndex());
try {
switch (action) {
case MotionEvent.ACTION_MOVE:
handleActionPointerMove(event);
break;
// Order of touch downs doesn't matter to us
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
handleActionPointerDown(event, ptr_idx);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
handleActionPointerUp(event, ptr_idx);
break;
}
}
catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
}
return true;
}
}
public void handleActionPointerMove(MotionEvent event) {
for (int i = 0; i < event.getPointerCount(); i++) {
handleInformation(event.getX(i), event.getY(i));
}
}
public void handleActionPointerDown(MotionEvent event, int ptr_idx) {
handleInformation(event.getX(ptr_idx), event.getY(ptr_idx));
}
public void handleActionPointerUp(MotionEvent event, int ptr_idx) {
handleInformation(event.getX(ptr_idx), event.getY(ptr_idx));
}
public void handleInformation(int x, int y) {
// Figures out what x+y means to us (are we in a certain box within the app? outside in clear zones? etc
}
Well after thinking about it for a night I've gotten it to work doing this before calling MotionEvent.getX() and MotionEvent.getY():
public int getAdjustment(int ptr_idx) {
int adjust = 0;
for (int i = 0; i < event.getPointerCount(); i++) {
// Get Actual Pointer Index of touch
int adjustedPointerIndex = event.getPointerId(i);
// If we've found the current touch's pointer index AND
// the pointer index doesn't equal the sequential event's
// pointers
if ((adjustPointerIndex == ptr_idx) && (i != adjustPointerIndex)) {
adjust = (adjustPointerIndex - i);
break;
}
}
return adjust;
}
// Example Function using getAdjustment(int ptr_idx)
public void handleActionPointerUp(MotionEvent event, int ptr_idx) {
int adjustment = ptr_idx - getAdjustment(ptr_idx);
handleInformation(event.getX(adjustment), event.getY(adjustment));
}
Explanation of the for loop statement:
Basically, lets say we have 4 touches (Touch 0, 1, 2, 3). Now we have lifted up Touch 0 and now we have Touch 1, 2, 3. Android will see these touches as 0, 1, 2 and the actual pointer indices as 1, 2, 3. To get the correct adjustment, I iterate through the MotionEvent's pointers (this is 0, 1, 2 right now in the for loop).
Now, if for some reason Touch 0 or any touch in between is taken out, we must adjust the pointer index as getX() and getY() doesn't understand the touch's actual pointer index. It only takes in indices 0, 1, 2 even though we have pointer indices 1, 2, 3. Thus, if we've reached the correct current touch pointer index BUT the MotionEvent's touch index does not equal correct touch pointer index, we adjust it by doing adjust = adjustPointerIndex-i.
After doing that, just subtract it from the current ptr_idx we're analyzing and we get a value that getX() and getY() can understand without IllegalArgumentException for a pointerIndex out of range.
Will explain more thoroughly if that doesn't make sense and if someone has a way more elegant solution please let me know because I'm sure this is not a great way to handle this. I'd rather mark someone else's answer as the approved answer.
I my code, severely simplified is the following:
if(action == MotionEvent.ACTION_DOWN){
x = me.getX();
y = me.getY();
}
else if(action == MotionEvent.ACTION_UP){
x = me.getX();
y = me.getY();
}
else if(action == MotionEvent.ACTION_MOVE){
x = me.getX();
y = me.getY();
}
if(action == MotionEvent.ACTION_POINTER_2_DOWN){
x2 = me.getX(1);
y2 = me.getY(1);
}
if(action == MotionEvent.ACTION_POINTER_2_UP){
x2 = me.getX(1);
y2 = me.getY(1);
}
I want to add
if(action == MotionEvent.ACTION_POINTER_2_MOVE){
x2 = me.getX(1);
y2 = me.getY(1);
}
But of course
MotionEvent.ACTION_POINTER_2_MOVE
Is not a real methode. I know there has to be a way around this. Any suggestions?
If you don't understand my question, what I am asking is, is there a multi-touch methode that runs when a pointer is moved? BTW googles not being helpful.
I also checked http://developer.android.com/reference/android/view/MotionEvent.html but again nothing useful
From the documentation:
The order in which individual pointers appear within a motion event is
undefined. Thus the pointer index of a pointer can change from one
event to the next but the pointer id of a pointer is guaranteed to
remain constant as long as the pointer remains active. Use the
getPointerId(int) method to obtain the pointer id of a pointer to
track it across all subsequent motion events in a gesture. Then for
successive motion events, use the findPointerIndex(int) method to
obtain the pointer index for a given pointer id in that motion event.
You must get and track the pointer IDs yourself and determine which pointer(s) moved.
http://developer.android.com/reference/android/view/MotionEvent.html