Collision problems with drag-n-drop puzzle game - java

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

Related

Collision - recognizing when object has stopped colliding

What I mean is that when I collide with the side of an entity and want to jump, I cannot move right/left as I have a flag which prevents this when I collide with the entity on the right/left side, like so:
This is code code I use to detect collision:
public void onCollision(Wrapper wrapper) {
if (horizontal_velocity == 0 && vertical_velocity == 0) {
return;
}
Entity e1 = wrapper.thisEntity; //the object we are controlling
Entity e2 = wrapper.otherEntity; //the object we are impacting with
Rectangle rect1 = wrapper.thisEntityRectangle;
Rectangle rect2 = wrapper.otherEntityRectangle;
int e1y = (int) rect1.getY(), e1x = (int) rect1.getX(), e1w = (int) rect1.getWidth(), e1h = (int) rect1.getHeight();
int e2y = (int) rect2.getY(), e2x = (int) rect2.getX(), e2w = (int) rect2.getWidth(), e2h = (int) rect2.getHeight();
e1x += e1w / 2;
e1w /= 2;
e1y += e1h / 2;
e1h /= 2;
//horizontal interaction -- x
/* ---+\\\\\\+---
* |------|
* | |
* e1 | e2 | e1
* | |
* |------|
* ---+\\\\\\+---
*/
boolean touchingSide = false;
if(e1y > e2y && e1y < e2y + e2h) {
//touchingSide = true;
if(e1x > e2x - e1w && e1x < e2x) { //going right || hitting left side
collisionsHappening.put(wrapper.otherEntity.getID(), "right");
canMoveRight = false;
horizontal_velocity = 0;
setX(e2x - e1w*2);
}
if(e1x < e2x + e2w + e1w && e1x > e2x) { //going left || hitting right side
collisionsHappening.put(wrapper.otherEntity.getID(), "left");
canMoveLeft = false;
horizontal_velocity = 0;
setX(e2x + e2w);
}
}
//vertical interaction -- y
/*
* \| e1 |\
* \+--+------+--+\
* \\\\| |\\\\
* \\\\| e2 |\\\\
* \\\\| |\\\\
* \+--+------+--+\
* \| e1 |\
*
*/
if(e1x > e2x - e1w && e1x < e2x + e2w && !touchingSide) {
if(e1y + e1h > e2y && e1y < e2y) { //going down || hitting top side
collisionsHappening.put(wrapper.otherEntity.getID(), collisionsHappening.get(wrapper.otherEntity.getID()) + "|top");
setY(e2y - e1h * 2);
vertical_velocity = 0;
//readyNextTurn = true;
isFalling = false;
onGround = true;
}
if(e1y - e1h < e2y + e2h && e1y > e2y) { //going up || hitting bottom side
collisionsHappening.put(wrapper.otherEntity.getID(), collisionsHappening.get(wrapper.otherEntity.getID()) + "|bottom");
setY(e2y + e2h + e1h);
vertical_velocity = 0;
//readyNextTurn = true;
isFalling = false;
onGround = true;
}
}
}
..and where the code is affected by the canMoveRight and canMoveLeft flags:
public void key(int keyCode) {
try {
onEdge();
switch (keyList.get(keyCode)) {
...
case MOVE_RIGHT:
for(Entity e : objectsCollidingWith) { //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
if(collisionsHappening.get(e.getID()) == "right") {
canMoveRightNextFrame = true;
}
}
canMoveLeft = true;
if (!canMoveRight && !canMoveRightNextFrame) break;
if(canMoveRightNextFrame) {
canMoveRightNextFrame = false;
System.out.println(canMoveRightNextFrame);
}
if (horizontal_velocity == 0) {
horizontal_velocity = getXDisplacement();
}
changeX(horizontal_velocity);
direction = "right";
//horizontal_velocity = 0;
break;
case MOVE_LEFT:
for(Entity e : objectsCollidingWith) { //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
if(collisionsHappening.get(e.getID()) == "left") {
canMoveLeftNextFrame = true;
}
}
canMoveRight = true;
if (!canMoveLeft && !canMoveLeftNextFrame) {
break;
}
canMoveLeftNextFrame = canMoveLeftNextFrame ? true : false; //if true, make false
if (horizontal_velocity == 0) {
horizontal_velocity = getXDisplacement();
}
changeX(-horizontal_velocity);
direction = "left";
//horizontal_velocity = 0;
break;
case JUMP:
jump();
}
} catch (NullPointerException e) {
//ignore - a key with no action pressed
}
}
What I have tried
When I look for collisions - not react to them as shown in the above code - I tried to implement a HashMap in which I put a String - either "left" or "right" - to the key Entity.getID() which returns a unique name, like so (after the lines of code that are relevant are // <<<) :
public Wrapper[] collisions(Entity thisEntity) {
ArrayList<Wrapper> wrappers = new ArrayList<Wrapper>();
Player p = null;
boolean isPlayer = thisEntity instanceof Player;
boolean onGround = false;
if (isPlayer) {
p = (Player) thisEntity;
}
try {
for (Entity e : getLevel().getEntities()) {
if (!thisEntity.equals(e)) {
collisionsHappening.put(e.getID(), ""); // <<<
objectsCollidingWith.add(e); // <<<
if (thisEntity.getRect().intersects(e.getRect())) {
wrappers.add(new Wrapper(thisEntity, e, thisEntity.getRect(), e.getRect(),
thisEntity.getRect().intersection(e.getRect())));
onGround = true;
//collisionsHappening.put(e.getID(), true);
//System.out.println("collision between " + e.getID() + " + " + thisEntity.getID());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (isPlayer) {
p.isTouching = onGround;
if (onGround) {
//System.out.println(onGround);
}
}
//Entity[] result = list.toArray(new Entity[1]);
return wrappers.toArray(new Wrapper[0]);
}
This system relies on keeping a flag 1 frame after the collision ceases in-order to remove the flags canMoveRight or canMoveLeft in the lines of code
for(Entity e : objectsCollidingWith) { //check if still colliding, if not, reset variable canMoveLeft inorder to not impair movement
if(collisionsHappening.get(e.getID()) == "left") {
canMoveLeftNextFrame = true;
}
}
canMoveRight = true;
if (!canMoveLeft && !canMoveLeftNextFrame) {
break;
}
if(canMoveLeftNextFrame) {
canMoveLeftNextFrame = false; //if true, make false
}
...
...and same for the canMoveRight flag...
However, this does not work, and I still cannot move mid-jump after I "scrape" the edge of an Entity unless I move in the opposite direction to "reset" the flag for the desired direction (line: case MOVE_RIGHT: ... canMoveLeft = true; and case MOVE_LEFT: ... canMoveRight = true;), as seen in the animation.
So, to conclude:
How can I make this system work?
Is there a more efficient/better way of doing this kind of thing?
Do you recommend I redo my collision?
Thanks.
It is actually impossible to approach this problem in such an event driven way for a game more advanced then Snake. I recommend that you focus on a simple update loop which updates each entity. Each entity then updates their own velocity and then position. Then check for collision and handle response. The key event handlers should not have any logic and be simply changing flags which the entities can poll.
As for the the collision and response part, that is where the meat of a simple platform game engine lies, and there are many different approaches. In the modern world of off the shelf physics engines, e.g. Box2D, you could simply let the physics system handle things. But from the sounds of things, you are not looking for that and what you want is what I call logic based physics.
If your world and entities consist of axis aligned boxes, you can separate horizontal and vertical motion into two discrete steps, for each entity. For each axis, each object tries to move as far as it can for (depending on velocity and collisions) if the object hits something, generate a collision event and set the velocity to zero on that axis. Repeat for the other axis and you will have a simple platform game engine. For this method you will need to handle the state of two objects touching carefully due to floating point numerical error. I recommend doing collision detection with integers (in the scale of the screen coordinates).
If you are looking to have an environment and entities which are not restricted to the axis aligned boxes, then you will have to get more complex and no longer rely on simply handling X then Y movement to give you things like a box sliding on the floor or down a wall. In this case I suggest you keep track of what surface your entity is standing or walking on so that you can handle their movement differently in each state. When they are in the air, normal movement, but when they are on some surface, the movement is constrained to horizontal movement and the vertical position is adjusted to keep them on the surface. You then will need to detect when they change states.
I hope you find this helpful. It turns out that it is not an easy topic to Google. (Update loops, collision detection, physics of motion, numerical integration and the geometry of collision response are all easy to Google). Most programmers seem to just take the approach of keep fiddling with it till it works, and so it never gets documented well.
P.S. Implicit Euler integration method is fine for most games.

How to do collision detection with many walls (maze)?

In my game, the player navigates a maze. I can't figure out how to do proper collision detection with the walls. It is easy to do collision detection for staying in a certain area:
if (x > rightWallX - playerWidth) x = rightWallX - playerWidth;
if (x < leftWallX) x = leftWallX;
//...
But how would I do collision detection for many walls?
I can do plain collision detection without correction (like if (intersecting) return true;), but I can't correct this correctly. If I just store the old x and y and reset them, then
The object never actually touches the wall
If the object can go up but is blocked to the right, it won't go up, it will just not move.
How is collision detection in a maze done?
The easiest way, once you have solved collision detection, to fix the collision is to move the actor to the closest valid position to where the actor would be were it not for the object it collides with. This assumes no inertia, but it is sufficient for maze-like games or top-down map-crawling games.
If you want to simplify your calculations further, you can limit yourself to detecting if changing the actor's x or y coordinate would be better. If your actor has an axis-aligned rectangular hit-box and all obstacles are axis-aligned rectangular as well (the simplest case), this assumption is indeed correct. However, the results might not be satisfactory in some other cases (potential artifact: speed boost from gliding diagonal walls - not the case in most maze games).
Keep in mind multiple collisions could happen concurrently (pushing against two walls). If there are no sharp angles between two walls that an actor could both intersect (say, if all your obstacles are axis aligned and sufficiently spaced), fixing each collision in turn will suffice - just don't stop after the first collision.
You can use Rectangle.intersects() method:
public Rectangle Player(){
return new Rectangle(PlayerX,PlayerY,PlayerWidth,PlayerHeight);
//we do this for getting players x and y values every tick
}
if(Player().intersects(new Rectangle(0,0,100,50)))//if(player touching wall)
new Rectangle(0,0,100,50) is just an example you can change it.
Ok so i'm currently making a 2D top down view game and I'm not sure how you created your maze. However, in my game my Level is created from a Tile[][] tiles = new Tile[levelWidth][levelHeight]; array. The way i handled collision detection was by checking the surrounding tiles to see if they were solid.
This is my getTile method.
public Tile[][] getTile(int x, int y) {
if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) {
return new VoidTile();
} else {
return tiles[x][y];
}
}
In my Tile.java class i have a isSolid() method which returns whether the tile is solid or not. All of my tiles extend my Tile.java so they inherit this method and I override it in their constructor. As i said previously, I am not sure whether or not you use the same style of level implementation as i do. However, It is good practice to do it this way :)
Personally, I am not a big fan of using the .intersects() and .contains() methods for Sprite collision detection. I mainly use them for buttons and alike.
Ok so,
In my player.java class i have a checkBlockedDirection(int x, int y) method and it looks like this.
public void checkBlockedDirection(int x, int y) {
boolean u = map.getTile(x, y - 1).isSolid();
boolean d = map.getTile(x, y + 1).isSolid();
boolean l = map.getTile(x - 1, y).isSolid();
boolean r = map.getTile(x + 1, y).isSolid();
if (u) {
uBlocked = true;
System.out.println("up tile blocked");
} else {
uBlocked = false;
}
if (d) {
dBlocked = true;
System.out.println("down tile blocked");
} else {
dBlocked = false;
}
if (l) {
lBlocked = true;
System.out.println("left tile blocked");
} else {
lBlocked = false;
}
if (r) {
rBlocked = true;
System.out.println("right tile blocked");
} else {
rBlocked = false;
}
}
Then in my player update method i have this
public void tick() {
float dx = 0;
float dy = 0;
if (input.up.isPressed()) {
direction = 0;
} else if (input.down.isPressed()) {
direction = 2;
} else if (input.left.isPressed()) {
direction = 3;
} else if (input.right.isPressed()) {
direction = 1;
} else {
direction = 4; // standing
}
checkBlockedDirection((int)x, (int)y);
if (input.up.isPressed() && y > 0 && !uBlocked) {
dy += -speed;
} else if (input.down.isPressed() && y < map.getHeight() - 1 && !dBlocked) {
dy += speed;
} else if (input.left.isPressed() && x > 0 && !lBlocked) {
dx += -speed;
} else if (input.right.isPressed() && x < map.getWidth() - 1 && !rBlocked) {
dx += speed;
}
x += dx;
y += dy;
}
Basically it just checks whether or not the blocks up, down, left, or right are solid. If they are solid then it wont move and if they arent solid then you can move in the desired direction.
Not sure if this helps or not but it's just my take on this kind of grid collision detection :)
Hope this helps :)
Enjoy

Making game gravity, everything goes way too quickly

So basically i'm currently working on a small project where the player will be able to fight stuff through move but the gravity is giving me kind of a problem. So basically, i got this method, checkGravity() which will check if isGravityApplicable() return true (does the entity's bottom collider(a Line2D) collide with one of the foothold?(another line2D))
If isGravityApplicable() returns true, then the gravity should be applied but the problem that i have is that anything above 1 pixel Y is way too quickly and even, moving the character by one pixel is extremely fast. I'm not sure whether i should fix my game loop or what?
public boolean isGravityApplicable() {
for (Line2D line : frame.getMap().getFootholds()) {
/*Does the bottom collider intersect the foothold?*/
/*v THIS CHECK DOESN'T WORK CORRECTLY FOR SOME REASONS v*/
if (!GraphicHelper.getLineCameraRelative(getBottomCollider(), frame.getCam()).intersectsLine(line)) {
return true;
}
/*Above check returned false, if the velocity.y is above 1, then it might
skip the line since it would be skipping 5 pixels for example at once.
this check should resolve that.*/
if (velocity.y > 0) {
Line2D collider = getPositionToVelocityCollider();
if (!collider.intersectsLine(line)) {
position.y = (int) line.getY1(); //Y1 or Y2, same sh*t.
return true;
}
}
}
return false;
}
public void checkGravity() {
if (isGravityApplicable()) {
if (isJumping) { //Player has jumped
velocity.y += 1;
velocity.y = velocity.y > TERMINAL_ACCELERATION ? (int) TERMINAL_ACCELERATION : velocity.y;
if (velocity.y < 0) {
isFalling = true;
}
} else if (isFalling) { //Player is currently falling but not from jumping. Most likely just spawned
velocity.y = 1;
/*Anything higher than 1 is WAY too quickly...*/
//velocity.y += 1;
//velocity.y = velocity.y > TERMINAL_VELOCITY ? (int) TERMINAL_VELOCITY : velocity.y;
} else if (isJumping && isFalling) { //Player has jumped and has reached its highest point, falling back down
velocity.y = 1;
/*Anything higher than 1 is WAY too quickly...*/
//velocity.y += 1;
//velocity.y = velocity.y > TERMINAL_VELOCITY ? (int) TERMINAL_VELOCITY : velocity.y;
} else { //Player hasn't jumped and has not started falling, he most likely just spawned
isFalling = true;
velocity.y = 1;
}
} else {
isFalling = false;
isJumping = false;
}
}
Thanks in advance. Oh and if there is anything i could of done better like the collisions which consist of simple lines all around the entities, please tell me. :) Thanks.
Nevermind guys, for some reason the update/tick method was called twice in the loop for some reasons. I guess i had a brain fart or something.

ACTION_DOWN or only ACTION_MOVE in onTouch()

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

Android Sprite Animation Move To position X (should be simple but, I'm stuck)

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.

Categories

Resources