I am trying to implement a touch event on my simple 2d game. I am trying to do something similar to this quick diagram I made in paint.
For e.g, if the user presses on the 'TOP' portion of the screen, the object moves up. If they press right, the object moves right etc.
However, I can't seem to figure out the maths required to select the bottom and the right sections of the screen.
ScreenX and ScreenY variables are the screen size, for reference.
Could somebody check my code and see what I have done wrong?
Java code
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (event.getY() < screenY / 5)
tank.changeDirection('UP');
else if (event.getY() > screenY / 5)
tank.changeDirection('DOWN');
else if (event.getX() < screenX / 2)
tank.changeDirection('LEFT');
else if (event.getX() > screenX / 2)
tank.changeDirection('RIGHT');
break;
case MotionEvent.ACTION_UP:
tank.changeDirection(0);
break;
}
return true;
}
Probably the "down" condition should be
//else if (event.getY() > screenY / 5)
// tank.changeDirection('DOWN');
else if (event.getY() > screenY * 4 / 5)
tank.changeDirection('DOWN');
Related
The following code I wrote does what I want, a touch on the left or right side of the screen to move the sprite left or right and stop at the edge of the phone screen. The issue I'm having is when you do a fast motion of touching the right side of the screen, letting go while using another finger to touch the left side of the screen to change direction will yield a result of the sprite still moving to the right side of the screen despite you wanting to move left. In order to fix this, you need to let go completely for at least 0.5sec then press the other direction to start moving in that direction, which I don't want to have to live with. If anyone has any tips/help for this, please let me know!
MAIN ACTIVITY CLASS METHOD:
public boolean onTouchEvent(MotionEvent event){
int x = (int)event.getX();
switch(event.getAction()) {
case (MotionEvent.ACTION_DOWN):
CharacterSprite.touchedX = x;
break;
case (MotionEvent.ACTION_UP):
CharacterSprite.touchedX = 0;
break;
}
return super.onTouchEvent(event);
}
CHARACTERSPRITE CLASS METHOD:
public void update() {
if (touchedX != 0) {
if (touchedX < screenWidth / 2) {
if (!(xVelocity < 0)) {
xVelocity = xVelocity * -1;
}
if (!(x > 0)) {
touchedX = 0;
return;
}
x += xVelocity;
}
if (touchedX > screenWidth / 2) {
if (!(xVelocity > 0)) {
xVelocity = xVelocity * -1;
}
if (!(x < screenWidth - image.getWidth())) {
touchedX = 0;
return;
}
x += xVelocity;
}
}
}
The way you handling MotionEvent will not work for multitouch events. Each finger (action pointer) data stored as an array and you need to handle each entry separately. Here is the lesson on handling multitouch events:
https://developer.android.com/training/gestures/multi
I'm trying to develop my own game. But I have an issue with the onTouchEvent-Method. Little explanation before showing my code. I want to be able to control a flight with my finger. It Should always follow my finger. For that I implemented the MotionEvent.ACTION_MOVE case. The flight only can move on the left side of the screen. When I touch on the right side of the screen, it should shoot a bullet. And it should be possible to fly around and shoot simultaneously. Here is my Code:
public boolean onTouchEvent(MotionEvent event) {
int maskedaction= event.getActionMasked();
switch (maskedaction) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getX() > screenX / 2 && flight.toShoot == 0) {
flight.toShoot++;
}
break;
case MotionEvent.ACTION_MOVE: {
if (event.getX() < screenX / 2 - 100) {
flight.x = (int) event.getX() - 100;
flight.y = (int) event.getY() - 50;
}
break;
}
}
return true;
}
So that's the code. When I touch the left half of the screen, I can fly around with the plane. When I Touch the right side, I can shoot. But I cannot do it at the same time. I want to touch first the left half and fly around. By clicking on the right side, at the same time as i'm moving my finger on the left side, it should shoot a bullet.
I found out that when I click at the same time on both sides with one finger, then the plane shoots one bullet. If i release now the right finger(the finger that shoots when it touches the screen) I can fly around and while I'm flying around I can shoot simultaneously.
You can detect your touch by detecting IDs, https://developer.android.com/training/gestures/multi
You can do something like this :
int leftSideId = 0;
int rightSideId = 1;
public boolean onTouchEvent(MotionEvent event) {
int maskedaction= event.getActionMasked();
// Get the pointer ID
int mActivePointerId = event.getPointerId(0);
switch (maskedaction) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getX() > screenX / 2) {
rightSideId = mActivePointerId;
if(flight.toShoot == 0){
flight.toShoot++;
}
}else{
leftSideId = mActivePointerId;
}
break;
case MotionEvent.ACTION_MOVE: {
if (event.getX() < screenX / 2 - 100) {
if (mActivePointerId == leftSideId) {
flight.x = (int) event.getX() - 100;
flight.y = (int) event.getY() - 50;
}
}
break;
}
}
return true;
}
So I found a solution by myself.
public boolean onTouchEvent(MotionEvent event){
int amount=event.getPointerCount();
for (int i=0; i<amount; i++) {
float x = event.getX(i);
float y = event.getY(i);
if (x>screenX/2 && flight.toShoot==0){
flight.toShoot++;
}
if (x<screenX/2 -100 && flyingenabled)
{
flight.x = (int) x - 100;
flight.y = (int) y - 50;
}
}
return true;
}
Thats working for my problem. Easier than I thought.
I am trying to simulate a mouse click when a mousepad in the app is pressed and not moved.
if(isConnected && out!=null){
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//save X and Y positions when user touches the TextView
initX =event.getX();
initY =event.getY();
mouseMoved=false;
break;
case MotionEvent.ACTION_MOVE:
disX = event.getX()- initX; //Mouse movement in x direction
disY = event.getY()- initY; //Mouse movement in y direction
/*set init to new position so that continuous mouse movement
is captured*/
initX = event.getX();
initY = event.getY();
if(disX !=0|| disY !=0){
out.println(disX +","+ disY); //send mouse movement to server
}
mouseMoved=true;
break;
case MotionEvent.ACTION_UP:
//consider a tap only if usr did not move mouse after ACTION_DOWN
if(!mouseMoved){
out.println(Constants.MOUSE_LEFT_CLICK);
}
}
}
return true;
}
});
}
I have tried this, but I can't figure out why it won't work. Every time I click on the mousepad the mouse moves. How can I solve this?
try it with an if-else and put the pressing first and the moving in the else-case.
I solved my problem by setting a margin.
int max = 2
int min = -2
case MotionEvent.ACTION_UP:
//consider a tap only if usr did not move mouse after ACTION_DOWN
if(disX <= max && disX >= min && disY <= max && disY >= min){
out.println(Constants.MOUSE_LEFT_CLICK);
mouseMoved=false;
break;
}
}
}
return true;
}
I have custom view control, which looks like this:
Inside my activity I want to be able to move this view around the screen by dragging it on green Arcs (left or right does not matter).
Also want to be able to detect if yellow arc at top is tapped, middle circle or bottom arc.
I'm having trouble to detect where the tap is in which area. This is the code that I'm using inside of my activity:
float dX, dY;
final MyCustomView myCustomView = (MyCustomView)findViewById(R.id.test);
final Boolean[] movable = {false};
myCustomView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
movable[0] = false;
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
int x = (int) event.getX();
int y = (int) event.getY();
if (myCustomView.leftArcRegion.contains(x,y) || myCustomView.rightArcRegion.contains(x,y)){
movable[0] = true;
} else if (myCustomView.topArcRegion.contains(x,y)){
//todo: do something if top arc area is selected
} else if (myCustomView.midRoundedBitmapRegion.contains(x,y)){
//todo: do something if mid bitmap area is selected
} else if (myCustomView.bottomArcRegion.contains(x,y)){
//todo: do something if bottom arc area is selected
}
break;
case MotionEvent.ACTION_MOVE:
if (movable[0]) {
view.animate()
.x(event.getRawX() + dX)
.y(event.getRawY() + dY)
.setDuration(0)
.start();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
default:
return false;
}
return true;
}
});
And these are public fields from my custom view control:
public Region topArcRegion;
private Path topArc;
//topArc is my top arc path
RectF rectFTop = new RectF();
topArc.computeBounds(rectFTop, true);
topArcRegion = new Region();
topArcRegion.setPath(topArc, new Region((int) rectFTop.left, (int) rectFTop.top,
(int) rectFTop.right, (int) rectFTop.bottom));
But it looks like it uses rectangular shapes for these regions, not arcs when checking with this "contains" method. And because of that I'm not getting expected results.
So, how can I detect where is initial tap (top arc, bottom arc, side arcs or middle bitmap) in order to apply my app logic?
Since you're only looking to detect touches inside an arc segment, it should't be too complicated.
Each of your arc segments is defined as the space between two concentric circles and between start and end angles. So all you really want to do is do a little trig to determine the distance from the center of the circles to your touch point and the angle from the center to your touch point.
float x = touchevent.getX();
float y = touchevent.getY();
// Transform relative to arc centers
x -= circle1.x;
y -= circle1.y;
double dist = Math.sqrt(x*x + y*y);
double angle = Math.atan2(y,x) * 180 / Math.PI;
// Given an arc segment defined by circle1, circle2, angle1, angle2:
boolean touch = dist > circle1.radius && dist < circle2.radius &&
angle > angle1 && angle < angle2;
You'll probably have to play around a bit depending on whether angle1 > angle2 or vice versa. If there's any chance that any of the angles cross the zero-degree angle, it gets a little trickier.
Meta: For clarity, I used sqrt() to compute distance, but you can optimize this code by skipping the sqrt() and compare distance² instead:
double dist2 = x*x + y*y;
if (dist2 > circle1.radius * circle1.radius &&
dist2 < circle2.radius * circle2.radius &&
...
One more edit: computing trig functions can be expensive; certainly a lot more expensive than computing distance².
In the interest of optimization, you should check the distance against the circle radii before bothering with the trig:
boolean touch = dist > circle1.radius && dist < circle2.radius;
if (touch) {
// This is only a *possible* touch, check the angles now
double angle = Math.atan2(y,x) * 180 / Math.PI;
touch = angle > angle1 && angle < angle2;
}
The bitmap A has a position which is its X/Y position is static, bitmap B is moving bitmap which moves from the bottom of the screen to where ever the user touches on screen. When the moving bitmap reaches the static bitmap however it is not recognized as a collision, the moving bitmap uses float which i round off and the static bitmap uses int. Below is a snippet of code, I want to figure out what is needed to be changed for my bitmaps crossing paths to be considered a collision
canvas.drawBitmap(rock2, 70, 32, null);
//this is where it checks to see if the moving bitmaps y coordinate is matched to the Y coordinate of the static bitmap
if(canvas.getHeight() - Math.round(animY*-1) == (canvas.getHeight() - (canvas.getHeight()-32))){
Log.d("ALERT", "COLLIDE");
}
//the Y value is *-1 because the moving bitmap is starting from the bottom of the screen so this converts the value to positive value
I wonder if my calculations are off, or I am going about this collision the wrong way. Below is my movement code snippet
//UP Y ANIMATION
if(animY*-1 < canvas.getHeight() && !bumpY){
animY += speedY;
//IF ROCK IS NOT AT EXTREME LEFT OR EXTREME RIGHT KEEP ON TRACK
if(animX < (canvas.getWidth()/2) || animX*-1 < (canvas.getWidth()/2) && !bumpX){
animX += speedX;
//IF ROCK HITS EDGE LEFT/RIGHT RETURN TO SENDER (INDICATE BUMP)
if(animX*-1 > (canvas.getWidth()/2) - rock.getWidth()/2 || animX > (canvas.getWidth()/2) - rock.getWidth()/2){
bumpX = true;
bumpY = true;
}
}
//IF Y HITS TOP OF SCREEN
if(animY*-1 > canvas.getHeight()){
bumpY = true;
}
}
//DOWN Y ANIMATION
if(animY < 0 && bumpY){
//REVERSE DIRECTION OF Y
animY -= speedY;
//IF ROCK HITS TOP OR SIDE REVERSE X DIRECTION
if(bumpX || bumpY)
animX -= speedX;
//IF AT STARTING POINT
if(animY > 0){
bumpY = false;
bumpX = false;
}
}
//in an ontouch method where the X and Y values are calculated
case MotionEvent.ACTION_UP:
finalX = event.getX() - (cross.getWidth() / 2);
finalY = event.getY() - (cross.getHeight() / 2);
moveToX = finalX - startX;
moveToY = finalY - startY;
speedX = moveToX / 50;
speedY = moveToY / 50;
break;
Any tips would be appreciated, thank you.
sorry if this is a dumb answer, but should your check be:
if(canvas.getHeight() - Math.round(animY*-1) >= (canvas.getHeight() - (canvas.getHeight()-32))){
instead? (change the "==" to ">=" ) otherwise you are only checking for when the rock lands EXACTLY on (canvas.getheight -32), rather than checking past it