onTouch method of my View:
public boolean onTouch(View view, MotionEvent event) {
Log.d("Touch", "Touch");
int mNewX = (int) Math.floor(event.getX());
int mNewY = (int) Math.floor(event.getY());
boolean isPositionFree = isPositionFree(mNewX, mNewY);
if (!isPositionFree) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int i = 0;
for (Point point : points) {
if (point.spotted) {
points.remove(i);
invalidate();
break;
}
i++;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
int i = 0;
for (Point point : points) {
if (point.spotted) {
points.remove(i);
Point p = new Point(mNewX, mNewY, point.TYPE);
points.add(i, p);
invalidate();
break;
}
i++;
}
}
}
}
There are multiple items in the canvas. Their positions are saved in "points". They get drawn to the canvas in a onDraw method via the position of those "points", means Point point.x and point.y.
Now, when I click an item (a point on the canvas), it should disappear.
Then, when the MotionEvent.ACTION_MOVE is true, I want to move the point, depending on the event.getX() and event.getY().
the method "isPositionFree(newX,newY)" checks if the point.x and point.y equals newX and newY (the position I just touched on the screen).
if the position is taken (means, there is an item where I just clicked), I'll get to the motionevent-IFs.
Here comes the problem:
my code removes the point before I can actually move it. I didnt find any way I could fix this problem for hours. :/ I find it difficult, since the onTouch is always called from the beginning, means ACTION_DOWN and ACTION_MOVE never take place at the same time.
Do you know any fix for this?
thanks in advance, Sebastian
Got it to work!
For everbody having the same issue, feel free to take this sample code as a help :)
Stuff I declared in the beginning
Vector<Point> points = new Vector<Point>();
Bitmap[] monsterTypes = new Bitmap[3];
Vector<Integer> distanceMovedX = new Vector<Integer>();
Vector<Integer> distanceMovedY = new Vector<Integer>();
int mNewX = -1;
int mNewY = -1;
OnTouch-Method
public boolean onTouch(View view, MotionEvent event) {
mNewX = (int) FloatMath.floor(event.getX());
mNewY = (int) FloatMath.floor(event.getY());
boolean touchedPoint = touchedPoint(mNewX, mNewY);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
distanceMovedX.add(mNewX);
distanceMovedY.add(mNewY);
break;
case MotionEvent.ACTION_UP:
isMoveEvent = isMoveEvent();
if (isMoveEvent) {
for (Point point : points) {
if (point.spotted) {
// Your code
}
i++;
}
} else {
if (touchedPoint) {
for (Point point : points) {
if (point.spotted) {
// Your code
}
}
}
}
distanceMovedX.clear();
distanceMovedY.clear();
return true;
}
return true;
}
touchedPoint-Method
public boolean touchedPoint(int mNewX, int mNewY) {
boolean touchedPoint = false;
int height = 0;
int width = 0;
for (Point point : points) {
height = monsterTypes[point.TYPE - 1].getHeight();
width = monsterTypes[point.TYPE - 1].getWidth();
if (point.x + width < mNewX || point.x > mNewX + width
|| point.y + height < mNewY || point.y > mNewY + height) {
touchedPoint = false;
point.spotted = false;
} else {
touchedPoint = true;
point.spotted = true;
return touchedPoint;
}
}
return touchedPoint;
}
isMoveEvent-Method
public boolean isMoveEvent() {
boolean isMoveEvent = false;
boolean isMoveEventX = false;
boolean isMoveEventY = false;
for (int i = 0; i <= (points.size() -1); i++) {
Log.d("point", "for loop entered");
if (!distanceMovedY.isEmpty()) {
Log.d("point.x", "distanceMovedY is not empty");
int dMY = distanceMovedY.get(distanceMovedY.size() - 1) - distanceMovedY.get(0);
if ((dMY > 50 || dMY <= 0) && dMY != 0) {
Log.d("point.y", "is move event");
Log.d("point.y", "dMY: " + dMY);
isMoveEventY = true;
} else {
Log.d("point.x", "is no move event");
Log.d("point.x", "dMY: " + dMY);
isMoveEvent = false;
return isMoveEvent;
}
}
if (!distanceMovedX.isEmpty()) {
Log.d("point.x", "distanceMovedX is not empty");
int dMX = distanceMovedX.get(distanceMovedX.size() - 1) - distanceMovedX.get(0);
if (dMX <= 50 && dMX >= -50 && dMX != 0) {
Log.d("point.x", "is move event");
Log.d("point.x", "dMX: " + dMX);
isMoveEventX = true;
} else {
Log.d("point.x", "is no move event");
Log.d("point.x", "dMX: " + dMX);
isMoveEvent = false;
return isMoveEvent;
}
}
if (isMoveEventX && isMoveEventY) {
Log.d("point", "is move event");
isMoveEvent = true;
return isMoveEvent;
}
}
Log.d("point", "is no move event");
return isMoveEvent;
}
Point Class
class Point {
int x, y;
int TYPE;
boolean spotted;
boolean halfSpotted;
public Point() {
}
public Point(int x, int y, int t) {
this.x = x;
this.y = y;
this.TYPE = t;
}
#Override
public String toString() {
return x + ", " + y;
}
}
EXPLANATION:
Point:
we got a class Point. All those points declared in the Vector are x- and y-coordinates on your canvas. They help us to check the position we clicked.
monsterTypes:
its the different graphics I use. If you only use one graphic that you draw onto the canvas, change it to your needs
distanceMovedX & Y:
saves all the X and Y Coordinates of your "ACTION_MOVE". From pos 0 (the first touched point) to pos Z (the last touched point, where ACTION_UP occurs). Though its not the original X and Y position. Its the result of posZ - pos0.
With these values you can determine, after what distance travelled you wanna invoke "onMove" and BELOW which distance "onClick" should be invoked.
mNewX & Y:
the currently position of your onTouch-Method. Everytime you move your finger, newX & Y become overwritten.
Methods:
onTouch():
First, we'll overwrite mNewX and Y to the current position touched. Then we check if we clicked on an existing spot (in my case some 48px*48px area)
Next we record the taken distance in ACTION_MOVE.
After that we continue with ACTION_UP, where we check if we just performed some moveEvent or clickEvent.
touchedPoint():
calculates if we touched some existing point on the canvas, or not. returns true or false
isMoveEvent():
checks if we moved the certain distance. in my case i wanna move down, 50px or more. though im not allowed to move sidewards -50px or +50px. If its NOT a move event, the last spot touched still has to be on the in the giving distance (in my case in the 48px*48px range of the point).
Thats it. took me days to only figure out that option ;/ ashamed of that ... though I coded it pretty fast, what makes me feeling better again :D
I'm just suggesting some walk-around :
Instead of removing the point when clicking it,
make a privata Point in your class, where you currently remove the point, just set your new Variable Point to the Point you would remove...
Then, after using it in action or action move, the last place you would use it,
check if your private variable is not null, if so, remove it, then, set it to null.
try using a switch case statement:
switch finger_action:
case(ACTION_MOVE)
{
//move code
return false;
}
case(ACTION_TOUCH)
{
//dissappear code
return false;
}
note the up above code is pseudo code, but what it does it checks to see if you are moving the dot before just touching it. this way the dot will attempt a move first instead of being removed first.
thanks,
Alex
Related
I'm working on a simple cooperative game for two players running on a widescreen touch display. I use libGDX as a base for the game and have Windows as a target platform. The problem is that libGDX doesn't support multi-touch for Windows, so its GestureListener doesn't work there. I tried to do a workaround and found a multi-touch library called JWinPointer, which bridges Java with Windows .NET API. I managed to get multi-touch working but dragging my game actor causes a delay in his movement (the longer I drag him, the slower he moves following the path I "drew" with my finger). Dragging multiple actors (shapes) at once makes delays even longer. From what I know, my code should be OK and the delay is probably caused by the library itself. It looks like the JWinPointer listener reports a new touch pointer for every pixel or game frame, which could be also a part of the problems, as my application can't keep up (I execute the movement method for each reported pointer which has a different position than the previous one).
Each detected pointer is saved into an ArrayList and only updated if detected pointerID already exists in the list. I also use HashMap to save a pointer touching the certain actor and use it in my drag() method to decide which actor should be moved. I also use another HashMap to know if each actor has already been moved in the current frame (this was just an attempt to get rid of the delay and didn't help). I know my code probably isn't optimal and is maybe confusing but I've been working on finding a solution for this for almost a week and trying lots of things. Sorry for that.
Here is the JWinPointer listener method:
public void pointerXYEvent(int deviceType, int pointerID, int eventType, boolean inverted, int x, int y, int pressure) {
pointerInfoArray.updatePointer( deviceType, pointerID, inverted, x, y, pressure );
System.out.println("PointerXYEvent: " + deviceType + " " + pointerID + " " + eventType);
takeAction(deviceType, pointerID, eventType, x, y);
}
This is where I decide which action should be taken based on the touch input:
private void takeAction(int deviceType, int pointerID, int eventType, int x, int y) {
Vector3 touchPos = new Vector3(x, y, 0);
camera.unproject(touchPos);
switch (eventType) {
case EVENT_TYPE_DRAG :
System.out.println("POINTER EVENT DRAG");
for (int i=0; i<pointerInfoArray.array.size(); i++) {
if (pointerInfoArray.array.get(i).pointerID == pointerID) {
PointerInfo currentPointer = pointerInfoArray.array.get(i);
if (currentPointer.previousX == -1 && currentPointer.previousY == -1) {
currentPointer.previousX = x;
currentPointer.previousY = y;
break;
}
int deltaX, deltaY;
if (currentPointer.previousX - x != 0 || currentPointer.previousY - y != 0) {
deltaX = x - currentPointer.previousX;
deltaY = y - currentPointer.previousY;
currentPointer.previousX = x;
currentPointer.previousY = y;
drag(pointerID, (float) x, (float) y, (float) deltaX, (float) deltaY);
}
}
}
break;
case EVENT_TYPE_HOVER :
break;
case EVENT_TYPE_DOWN :
System.out.println("POINTER EVENT TOUCHDOWN");
for (GameShape shape : shapeList) {
if (!(shape instanceof GameCircle)) {
if (shape.getPolygonBounds().contains(touchPos.x, touchPos.y)) {
System.out.println("TOUCHING " + shape + " " + pointerID);
hashMap.put(shape, pointerID);
}
}
else {
if (shape.getCircle().contains(touchPos.x, touchPos.y)) {
System.out.println("TOUCHING " + shape + " " + pointerID);
hashMap.put(shape, pointerID);
}
}
}
touchDown(x, y, pointerID, 0);
break;
case EVENT_TYPE_UP :
System.out.println("POINTER EVENT TOUCHUP");
for (GameShape shape : shapeList) {
if (!(shape instanceof GameCircle)) {
if (shape.getPolygonBounds().contains(touchPos.x, touchPos.y)) {
System.out.println("REMOVING " + shape + " " + pointerID);
hashMap.remove(shape);
}
}
else {
if (shape.getCircle().contains(touchPos.x, touchPos.y)) {
System.out.println("REMOVING " + shape + " " + pointerID);
hashMap.remove(shape);
}
}
}
pointerInfoArray.removePointer(deviceType, pointerID);
ip.touchUp(x, y, pointerID, 0);
for (int i=0; i<pointerInfoArray.array.size(); i++) {
if (pointerInfoArray.array.get(i).pointerID == pointerID) {
pointerInfoArray.array.get(i).previousX = -1;
pointerInfoArray.array.get(i).previousY = -1;
}
}
break;
case EVENT_TYPE_BUTTON_DOWN :
break;
case EVENT_TYPE_BUTTON_UP :
break;
case EVENT_TYPE_IN_RANGE :
break;
case EVENT_TYPE_OUT_OF_RANGE :
break;
default:
break;
}
}
And this is where I actually move the actor (shape):
public void drag(int pointer, float x, float y, float deltaX, float deltaY) {
Vector3 panPos = new Vector3(x, y, 0);
camera.unproject(panPos);
Iterator it = hashMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
if (pair.getValue().equals(pointer)) {
GameShape shape = (GameShape) pair.getKey();
if (!frameMap.get(shape)) {
if (!(shape instanceof GameCircle)) {
if ((shape.getPolygonBounds().contains(panPos.x, panPos.y) && shape.getTouched()) || shape.getTouched()) {
shape.getPolygonBounds().setPosition(shape.getPolygonBounds().getX() + deltaX, shape.getPolygonBounds().getY() - deltaY);
shape.getPolygonSprite().setPosition(shape.getPolygonSprite().getX() + deltaX, shape.getPolygonSprite().getY() - deltaY);
frameMap.put(shape, true);
}
} else {
if ((shape.getCircle().contains(panPos.x, panPos.y) && shape.getTouched()) || shape.getTouched()) {
shape.getCircle().setPosition(shape.getCircle().x + deltaX, shape.getCircle().y - deltaY);
shape.getCircleSprite().setPosition(shape.getCircleSprite().getX() + deltaX, shape.getCircleSprite().getY() - deltaY);
frameMap.put(shape, true);
}
}
}
}
}
}
Do you see anything wrong with my code that should possibly cause the movement delay? Also, if you know about another way or library to do this, let me know, please (as far as I know, JavaFX isn't an option for me). I'm out of ideas. Thank you.
EDIT
I managed to solve the problem by setting libGDX to use non-continuous rendering and calling the render after each actor's position change. However, this solved the problem only for running the application in fullscreen mode. There is still delay in windowed mode.
I'm making a 2D platformer and just added gravity & jumping.
It works like it should, however, if the space bar is still held down after the player finishes a jump, the player just keeps jumping while in mid-air.
I know I need to check for whether or not the player is actually on the ground, but when I do it always returns as 'false', as described in my jumping method:
// Controls both falling and the jumping action.
// The MapObject list is a collection of every object the player can
// collide with on that map. Currently it only contains a single 'ground' object.
public void fall(ArrayList<MapObject> objects)
{
int distance = 0;
if (jumpTicks > 0)
{
float jumpHeight = jumpSpeed * (jumpTicks / maxJumpTicks);
System.out.println(jumpTicks + "/" + maxJumpTicks + " = " + jumpHeight);
y -= jumpHeight;
jumpTicks--;
}
else
{
for (MapObject obj : objects)
{
if (this.y + this.height >= obj.y)
{
// This cancels falling if the player's feet are on top
// of the ground, but for some reason setting an 'isOnGround'
// boolean to 'true' here and checking for it in the 'jump()'
// method does not work, it's always 'false'.
return;
}
distance = obj.y - (this.y + this.height);
}
if (distance > fallSpeed)
{
y += fallSpeed;
}
else
{
y += distance;
}
}
}
// This doesn't make the player jump, it just adds jump time to the player
// if it's not already jumping.
public void jump()
{
if (jumpTicks > 0)
{
return;
}
this.jumpTicks = maxJumpTicks;
this.jumpSpeed = 10;
}
Before you change his speed, check his y value for the floor
function jump() {
if (jumpTicks > 0) {
return;
}
if (this.y === floorY) {
this.jumpTicks = maxJumpTicks;
this.jumpSpeed = 10;
}
}
this problem is driving me up the wall. I'm sure I'm doing something wrong but I can't figure it out.
I have a static OnTouchListener which I attach to the TextView of a number of identical compound view objects contained within a linearlayout. This listener is in a helper class.
public static View.OnTouchListener getValueTouchListener() {
return new View.OnTouchListener() {
private int lastY;
private int lastX;
#Override
public boolean onTouch(View v, MotionEvent event) {
TextView view = (TextView)v;
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
view.setBackgroundResource(R.drawable.cog_pressed);
lastY = (int)event.getY();
lastX = (int)event.getX();
return true;
case MotionEvent.ACTION_MOVE:
//calc size of movement
int deltaY = lastY - (int)event.getY();
int deltaX = lastX - (int)event.getX();
// pass gesture up to parent if it's a big x movement - assumes user wishes to scroll parent
if (Math.abs(deltaX) > TOUCH_SLOP *2) {
return false;
}
lastX = (int)event.getX();
//process movement if larger than a touch slop
if (StrictMath.abs(deltaY) > TOUCH_SLOP) {
// reset last touch position
lastY = (int)event.getY();
//get direction of movement
int dir = (deltaY < 0)? -1 : 1;
//change index if within min and max limits
int min = (view.getTag() == Chainring.KEY)? Chainring.MIN_COG : Sprocket.MIN_COG;
int max = (view.getTag() == Chainring.KEY)? Chainring.MAX_COG : Sprocket.MAX_COG;
int value = Integer.valueOf(view.getText().toString());
if ((dir == -1 && value > min) || (dir == 1 && value < max)) {
value = value + dir;
view.setText(String.valueOf(value));
view.playSoundEffect(SoundEffectConstants.CLICK);
}
}
return true;
case MotionEvent.ACTION_UP:
view.setBackgroundResource(R.drawable.cog_unpressed);
return true;
case MotionEvent.ACTION_CANCEL:
view.setBackgroundResource(R.drawable.cog_unpressed);
return false;
default:
return false;
}
}
};
}
I hook up this listener to my TextView in a class that extends linear layout
mValueTextView.setOnTouchListener(CogPickerHelper.getValueTouchListener());
My problem is the method doesn't seem to execute the return command in the ACTION_DOWN, ACTION_UP and ACTION-MOVE cases. For example, with an ACTION_DOWN, the code executes as far as the return command but then steps directly to the default case and returns false - even though Logcat still reports the action as being ACTION-DOWN.
If I try to set a breakpoint on the return commands, IntelliJ says: "No executable code found at line X in class com.amb.GearBuddyV2.views.CogPickerHelper$2"
Does anyone know what I'm doing wrong here?
Your code seems to be working fine, the OnTouch return value is being returned correctly.
And yes, adding a break at a return statement will lead the debugger to complain about missing executable code.
I'm trying to move sprites to stop the frames in center(or move them to certain x position) when right or left pressed on screen. There are 3 sprites created using box.java in the view, placed one after another with padding, stored in arraylist.
The problem: No smooth movement and doesn't stop in the center of each frames after movement has begun, sometimes all boxes are moving on top of each others, padding is totally lost. Please let me know what I'm doing wrong, thanks a lot!
//BEGINING OF BOX.JAVA >> The problem is in this class!
//This goes in Update();
private void boxMove()
{
int get_moved_pos = getMovedPos(); //get moved pos
int sprite_size = view.getSpriteSize(); //get sprite arraylist size
currentDirection = view.getDirection(); //get direction "left" or "right" from view
if(currentDirection == "right" && isMoving == false)
{
setSpriteMovedNext();
}else
if(currentDirection == "left" && isMoving == false)
{
setSpriteMovedPrev();
}
if(currentDirection != lastDirection)
{
lastDirection = currentDirection;
//MOVE RIGHT
if(currentDirection == "right" && get_moved_pos > 0) //move left and make sure that moved pos isn't overlapping / or moving to empty space
{
//Animate left until it reaches the new x position
if(x > get_new_pos_left)
{
x -= pSpeedX;
}
Log.d("RIGHT","POS: " + get_moved_pos);
}else
//MOVE LEFT
if(currentDirection == "left" && get_moved_pos < sprite_size-1) //move left and make sure that moved pos isn't overlapping / or moving to empty space
{
//Animate right until it reaches the new x position
if(x < get_new_pos_right)
{
x += pSpeedX;
}
}
}
}
//Call when screen is touched (in View.java), to set a new position to move to.
public void resetMoving()
{
isMoving = false;
this.lastDirection = "";
Log.d("RESET", "MOVING RESET");
}
public int getMovedPos()
{
return this.smoved_pos;
}
private void setSpriteMovedNext()
{
int get_max_moved = getMovedPos();
int s_size = view.getSpriteSize();
if (isMoving == false) //take a break between movements
{
if(get_max_moved < s_size-1)
{
Log.d("NEXT", "CALLED");
this.get_new_pos_right = x + view.getNextPosX(); //current x and next stop position
this.smoved_pos += 1;
this.isMoving = true; //set to avoid double touch
Log.d("NEXT", "X POS SET: " + get_max_moved);
}
}
}
private void setSpriteMovedPrev()
{
int get_max_moved = getMovedPos();
if (isMoving == false) //take a break between movements
{
if(get_max_moved > 0)
{
Log.d("PREV", "CALLED");
this.get_new_pos_left = x - view.getNextPosX(); //get current x pos and prev stop position
this.smoved_pos -= 1; //to limit the movements
this.isMoving = true; //set to avoid double touch
Log.d("PREV", "X POS SET: " + get_max_moved);
}
}
}
//END OF BOX.JAVA
//VIEW
//Add boxes
public void addBox()
{
int TOTAL_BOXES = 3;
int padding_left = 200;
int padding_tmp = this.getWidth()/2;
box.clear(); //clear old
//Box 1
box.add(new Boxes(box, this, "box1",
padding_tmp,
this.getHeight()/2,
boxSpriteImage, 1, 2, 0, 0));
padding_tmp += boxSpriteImage.getWidth()/TOTAL_BOXES + padding_left;
//Box 2
box.add(new Boxes(box, this, "box2",
padding_tmp,
this.getHeight()/2,
boxSpriteImage, 1, 2, 1, 1));
padding_tmp += boxSpriteImage.getWidth()/TOTAL_BOXES + padding_left;
//Box 3
box.add(new Boxes(box, this, "box3",
padding_tmp,
this.getHeight()/2,
boxSpriteImage, 1, 2, 2, 1));
}
public boolean onTouchEvent(MotionEvent event){
if (System.currentTimeMillis() - lastClick > 100){
lastClick = System.currentTimeMillis();
float x = event.getX();
float y = event.getY();
synchronized (getHolder())
{
if(isBoxWindow() == true)
{
if(x >= this.getWidth()/2)
{
Direction = "right";
}else
{
Direction = "left";
}
}
}
}
//called in box.java to get next x pos to move
public float getNextPosX()
{
int PADDING = 200; //padding between frames
next_pos_x = boxSprite.getWidth()/TOTAL_COLUMNS + PADDING;
return next_pos_x;
}
I think your error is in the if statements, where you compare currentDirection and lastDirection (I'm assuming that lastDirection is a String) with other Strings using the == operator. The == almost operator never works when you want to compare Objects for equality. You should use the equals() method.
For eg.
if(currentDirection != lastDirection)
should be written as:
if(!currentDirection.equals(lastDirection)
Make such changes in your code(They are needed at many places!) and I think your problem should be solved.
A good debugging practice would be logging data about your app, from each of the if blocks, to see if each of them is executed. You could have found out if your if statements are being executed.
EDIT: Why have you put this code?
if (System.currentTimeMillis() - lastClick > 100)
This means onTouchEvents are only interpreted after 100ms. remove it and check, probably that's what is causing the problem.
Alrite, decided to use onFling() method and call via View instead of adding the animations separately into the class itself, works really well when called box.get(i).update() in a loop of all added boxes, all of them animated equally. Thanks udiboy.
I am working on an Android game similar to the Rush Hour/Traffic Jam/Blocked puzzle games. The board is a square containing rectangular pieces. Long pieces may only move horizontally, and tall pieces may only move vertically. The object is to free the red piece and move it out of the board. This game is only my second ever programming project in any language, so any tips or best practices would be appreciated along with your answer.
I have a class for the game pieces called Pieces that describes how they are sized and drawn to the screen, gives them drag-and-drop functionality, and detects and handles collisions.
I then have an activity class called GameView which creates my layout and creates Pieces objects to add to a RelativeLayout called Board. I have considered making Board its own class, but haven't needed to yet.
Here's what my work in progress looks like:
My Question:
Most of this works perfectly fine except for my collision handling. It seems to be detecting collisions well but instead of pushing the pieces outside of each other when there is a collision, it frantically snaps back and forth between (what seems to be) where the piece is being dragged to and where it should be. It looks something like this:
Another oddity: when the dragged piece collides with a piece to its left, the collision handling seems to work perfectly. Only piece above, below, and to the right cause problems.
Here's the collision code:
#Override
public boolean onTouchEvent(MotionEvent event){
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//check if touch is on piece
if (eventX > x && eventX < (x+width) && eventY > y && eventY < (y+height)){
initialX=x;
initialY=y;
break;
}else{
return false;
}
case MotionEvent.ACTION_MOVE:
//determine if piece should move horizontally or vertically
if(width>height){
for (Pieces piece : aPieces) {
//if object equals itself in array, skip to next object
if(piece==this){
continue;
}
//if next to another piece,
//do not allow to move any further towards said piece
if(eventX<x&&(x==piece.right+1)){
return false;
}else if(eventX>x&&(x==piece.x-width-1)){
return false;
}
//move normally if no collision
//if collision, do not allow to move through other piece
if(collides(this,piece)==false){
x = (eventX-(width/2));
}else if(collidesLeft(this,piece)){
x = piece.right+1;
break;
}else if(collidesRight(this,piece)){
x = piece.x-width-1;
break;
}
}
break;
}else if(height>width){
for (Pieces piece : aPieces) {
if(piece==this){
continue;
}else if(collides(this,piece)==false){
y = (eventY-(height/2));
}else if(collidesUp(this,piece)){
y = piece.bottom+1;
break;
}else if(collidesDown(this,piece)){
y = piece.y-height-1;
break;
}
}
}
invalidate();
break;
case MotionEvent.ACTION_UP:
// end move
if(this.moves()){
GameView.counter++;
}
initialX=x;
initialY=y;
break;
}
// parse puzzle
invalidate();
return true;
}
This takes place during onDraw:
width = sizedBitmap.getWidth();
height = sizedBitmap.getHeight();
right = x+width;
bottom = y+height;
My collision-test methods look like this with different math for each:
private boolean collidesDown(Pieces piece1, Pieces piece2){
float x1 = piece1.x;
float y1 = piece1.y;
float r1 = piece1.right;
float b1 = piece1.bottom;
float x2 = piece2.x;
float y2 = piece2.y;
float r2 = piece2.right;
float b2 = piece2.bottom;
if((y1<y2)&&(y1<b2)&&(b1>=y2)&&(b1<b2)&&((x1>=x2&&x1<=r2)||(r1>=x2&&x1<=r2))){
return true;
}else{
return false;
}
}
private boolean collides(Pieces piece1, Pieces piece2){
if(collidesLeft(piece1,piece2)){
return true;
}else if(collidesRight(piece1,piece2)){
return true;
}else if(collidesUp(piece1,piece2)){
return true;
}else if(collidesDown(piece1,piece2)){
return true;
}else{
return false;
}
}
As a second question, should my x,y,right,bottom,width,height variables be ints instead of floats like they are now?
Also, any suggestions on how to implement things better would be greatly appreciated, even if not relevant to the question! Thanks in advance for the help and for sitting through such a long question!
Update:
I have gotten it working almost perfectly with the following code (this doesn't include the code for vertical pieces):
#Override
public boolean onTouchEvent(MotionEvent event){
float eventX = event.getX();
float eventY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//check if touch is on piece
if (eventX > x && eventX < (x+width) && eventY > y && eventY < (y+height)){
initialX=x;
initialY=y;
break;
}else{
return false;
}
case MotionEvent.ACTION_MOVE:
//determine if piece should move horizontally or vertically
if(width>height){
for (Pieces piece : aPieces) {
//if object equals itself in array, skip to next object
if(piece==this){
continue;
}
//check if there the possibility for a horizontal collision
if(this.isAllignedHorizontallyWith(piece)){
//check for and handle collisions while moving left
if(this.isRightOf(piece)){
if(eventX>piece.right+(width/2)){
x = (int)(eventX-(width/2)); //move normally
}else{
x = piece.right+1;
}
}
//check for and handle collisions while moving right
if(this.isLeftOf(piece)){
if(eventX<piece.x-(width/2)){
x = (int)(eventX-(width/2));
}else{
x = piece.x-width-1;
}
}
break;
}else{
x = (int)(eventX-(width/2));
}
The only problem with this code is that it only detects collisions between the moving piece and one other (with preference to one on the left). If there is a piece to collide with on the left and another on the right, it will only detect collisions with the one on the left. I think this is because once it finds a possible collision, it handles it without finishing looping through the array holding all the pieces. How do I get it to check for multiple possible collisions at the same time?
My best guess is that the dragged piece is dragged into the other piece, then moved back so it is no longer colliding, then dragged into the other piece again. At least that is what I would expect if collision response happens after drawing.
On a side note, the following code makes me wonder a bit:
//if next to another piece,
//do not allow to move any further towards said piece
if(eventX<x&&(x==piece.right+1)){
return false;
}else if(eventX>x&&(x==piece.x-width-1)){
return false;
}
Checking for equality (==) on floats is generally a very bad idea, see zillions of "floating point precision" topics. Alternative a) use ints instead of floats. b) use ranged comparisions (>=, etc.). Don't use (a) though, there are many drawbacks with tiny (time) steps and such.
Try using the same approach as with the touch detection instead:
//check if touch is on piece
if (eventX > x && eventX < (x+width) && eventY > y && eventY < (y+height))
You should be able to do this more simply :)
Note that I made some assumptions on how you store the direction of movement, which you may have to adjust to your likings.
Also note that I treat piece1 as the moving piece we are concerned with, and piece2 simply as an object it collides with. So piece1 is repositioned, piece2 is not.
// Horizontal:
if( piece1.isMovingLeft )
piece1.x += Math.max( 0, piece2.right - piece1.x );
else if( piece1.isMovingRight )
piece1.x -= Math.max( 0, piece1.right - piece2.x );
// Vertical:
if( piece1.isMovingUp )
piece1.y -= Math.max( 0, piece2.bottom - piece1.y );
else if( piece1.isMovingDown )
piece1.y += Math.max( 0, piece1.bottom - piece2.y )
What I do here is as follows:
If the piece is moving in a certain direction, we move it back (a little bit) in the opposite direction to compensate for (possible) collision. We need to move it back by exactly the amount of overlapping pixels.
The amount of overlap, when moving left, for instance, is the right side of the second object minus the left side of the first object. (Negative means no overlap.)
The Math.max( 0, overlap ) ensures that said negative values become 0, i.e. no collision leads to no compensation.
The compensation is then applied in the direction opposite of movement, effectively taking piece1 out of piece2. (You can then choose to invert its movement direction or respond in any further way.)
You can apply this simple routine to every possible combination of two pieces, and they will no longer penetrate.
I hope this helps you out!
In reply to your second question:
The only problem with this code is that it only detects collisions between the moving piece and one other (with preference to one on the left). If there is a piece to collide with on the left and another on the right, it will only detect collisions with the one on the left.
There is a break statement within your loop - specifically, in the left collision check. ;)
I made a few changes and, so far, it has been working perfectly. Here is my new code:
case MotionEvent.ACTION_MOVE:
//determine if piece should move horizontally or vertically
if(width>height){
for (Pieces piece : aPieces) {
//if object equals itself in array, skip to next object
if(piece==this){
continue;
}
//check if there the possibility for a horizontal collision
if(this.isAllignedHorizontallyWith(piece)){
//check for and handle collisions while moving left
if(this.isRightOf(piece)){
if(eventX>piece.right+(width/2)){
x = (int)(eventX-(width/2)); //move normally
continue;
}else{
x = piece.right+1;
}
}else if(this.isLeftOf(piece)){ //check for and handle collisions while moving right
if(eventX<piece.x-(width/2)){
x = (int)(eventX-(width/2));
continue;
}else{
x = piece.x-width-1;
}
}else{
continue;
}
break;
}else{
x = (int)(eventX-(width/2));
}
}
}
if(height>width){
for (Pieces piece : aPieces) {
//if object equals itself in array, skip to next object
if(piece==this){
continue;
}
//check if there the possibility for a vertical collision
if(this.isAllignedVerticallyWith(piece)){
//check for and handle collisions while moving up
if(this.isBelow(piece)){
if(eventY>piece.bottom+(height/2)){
y = (int)(eventY-(height/2)); //move normally
continue;
}else{
y = piece.bottom+1;
}
}else if(this.isAbove(piece)){ //check for and handle collisions while moving down
if(eventY<piece.y-(height/2)){
y = (int)(eventY-(height/2));
continue;
}else{
y = piece.y-height-1;
}
}else{
continue;
}
break;
}else{
y = (int)(eventY-(height/2));
}
}
}
invalidate();
break;
And these are the methods I use:
private boolean isLeftOf(Pieces piece) {
int r1 = this.right;
int x2 = piece.initialX;
boolean bool = false;
if (r1<=x2){
for(Pieces piece2: aPieces){
if (piece2==piece || piece2==this){
continue;
}
if (this.isAllignedHorizontallyWith(piece2) && r1<=piece2.initialX){
if (piece.x>piece2.x){
bool = false;
break;
}else{
bool = true;
continue;
}
}else{
bool = true;
}
}
}else{
bool = false;
}
return bool;
}
private boolean isRightOf(Pieces piece) {
//True means that "this" is right of "piece" and "piece" is farther right than other possible pieces
int x1 = this.initialX;
int r2 = piece.right;
boolean bool = false;
if (x1>=r2){
for(Pieces piece2: aPieces){
if (piece2==piece || piece2==this){
continue;
}
if (this.isAllignedHorizontallyWith(piece2) && x1>=piece2.right){
if (piece.x<piece2.x){
bool = false;
break;
}else{
bool = true;
continue;
}
}else{
bool = true;
}
}
}else{
bool = false;
}
return bool;
}
private boolean isBelow(Pieces piece){
int y1 = this.initialY;
int b2 = piece.bottom;
boolean bool = false;
if (y1>=b2){
for(Pieces piece2: aPieces){
if (piece2==piece || piece2==this){
continue;
}
if (this.isAllignedVerticallyWith(piece2) && y1>=piece2.bottom){
if (piece.y<piece2.y){
bool = false;
break;
}else{
bool = true;
continue;
}
}else{
bool = true;
}
}
}else{
bool = false;
}
return bool;
}
private boolean isAbove(Pieces piece){
int y2 = piece.initialY;
int b1 = this.bottom;
boolean bool = false;
if (b1<=y2){
for(Pieces piece2: aPieces){
if (piece2==piece || piece2==this){
continue;
}
if (this.isAllignedVerticallyWith(piece2) && b1<=piece2.y){
if (piece.y>piece2.y){
bool = false;
break;
}else{
bool = true;
continue;
}
}else{
bool = true;
}
}
}else{
bool = false;
}
return bool;
}
private boolean isAllignedHorizontallyWith(Pieces piece) {
int y1 = this.y;
int b1 = this.bottom;
int y2 = piece.y;
int b2 = piece.bottom;
if((y1>=y2&&y1<=b2)||(b1>=y2&&b1<=b2)){
return true;
}else{
return false;
}
}
private boolean isAllignedVerticallyWith(Pieces piece) {
int x1 = this.x;
int r1 = this.right;
int x2 = piece.x;
int r2 = piece.right;
if((x1>=x2&&x1<=r2)||(r1>=x2&&r1<=r2)){
return true;
}else{
return false;
}
}
This could be commented better and probably done much simpler, but I am leaving it here for reference.
Nothing just get the your imageviews Left,right,top,bottom, and intersect with current x and y and find this solution more about this go to this link its also help i hop you got a right answer on this link
/technical/game-programming/collision-detection-algorithm-r754">http://www.gamedev.net/page/resources//technical/game-programming/collision-detection-algorithm-r754