I have been stuck on making some collision detection in my game (its kind of like Terraria) for a while but i made this code and... well, it kind of works. It works if the collision is above or on the left of the player, but if the collision is on the right, or below, instead of bouncing back, the player accelerates through the blocks until there is empty space. Here is the code that i made:
private void checkCollision() {
for(int x = (int) (xpos-1); x <= xpos+1; x++){
if(x < 0 || x > main.mw-1) continue;
for(int y = (int) (ypos-2); y <= ypos+1; y++){
if(y < 0 || y > main.mh-1) continue;
if(main.map[x][y] == null) continue;
if(!main.map[x][y].solid) continue;
if(main.map[x][y].blocktype == -1) continue;
double distance = Math.sqrt((xpos-x)*(xpos-x) + (ypos-y)*(ypos-y));
if(distance > 1.0){
continue;
}else{
double x_overlap = Math.max(0, Math.min(xpos + 16, x + 16) - Math.max(xpos, x));
double y_overlap = Math.max(0, Math.min(ypos + 32, y + 16) - Math.max(ypos, y));
double overlapArea = x_overlap * y_overlap;
if(overlapArea > 0){
if(x_overlap > y_overlap){
yblock += y_overlap/2;
}
if(x_overlap < y_overlap){
xblock += x_overlap/2;
}
//guessing i need to do something here to make player
go other way if block is on other side
}
}
}
}
}
So how would i make the player bounce back if the block that he is colliding with is on the right or below. Also is there any way i can make this smoother - right now the player be bouncing all over the place. Thanks! :)
What you want to do is keep track of the player's location, and if the location after moving is out of bounds you can reset the player's position to be right on the edge of the limit.
That's how I dealt with collision detection, I answered another question similar to this one though some folk decided to downvote the answer, go figure.
Related
I'm currently working on a Top-Down-Shooter and having some issues with collision.
My world is made of tiles (64x64). The tiles and the entities are rectangles. The player moves with a speed of e.g 2.74 (and not in pixels for smoother movement). But when it comes to the collision between the player (an entity) and a wall i have some issues. To check if there is a collision i take the current position of my player and his movement speed to calculate where his next position would be and if there is any collision. But i check every pixel on the way, so i cant skip an obstacle even if the movement speed is very high. Let's just say the players current position is X:200 Y:200 and he moves 2.74 Pixels a tick in the x direction. My game now checks if there is any collision at X:201 Y:200, X:202 Y:200 or X:202.74 Y:200 and if not moves the player to that position. If I now try to move the player further in the x direction and there is a wall 0.26 Pixels away the player wont move and leave a tiny gap. I tried to calculate the distance between player and wall and add this amount to the players position but for that I need to know which side of the wall the player hits. Also I want the player to be able to move up and down when the wall he hits is in front of him and the other way around.
Here is my collision method (in Java):
public static boolean collision(float ex, float ey, int width, int height) { // ex, ey would be the next position of the player
if (ex < 0 || ex + width > worldWidth || ey < 0 || ey + height > worldHeight) return true; // checks if this position is in the world
int firstTileX = (int) (ex / Tile.TILE_SIZE); // calculates tiles he could possible collide width
int firstTileY = (int) (ey / Tile.TILE_SIZE);
int lastTileX = (int) ((ex + width - 1) / Tile.TILE_SIZE);
int lastTileY = (int) ((ey + height - 1) / Tile.TILE_SIZE);
for (int y = firstTileY; y <= lastTileY; y++) {
if (y < 0) continue; // checks for out of bounds
if (y >= worldTileHeight) break;
for (int x = firstTileX; x <= lastTileX; x++) {
if (x < 0) continue;
if (x >= worldTileWidth) break;
if (tiles[y][x].solid) return true; // if the tile is solid -> collision found
}
}
return false; // no collision found
}
And my movement method:
public void move(float xa, float ya) {
float nx, ny;
while (xa != 0 || ya != 0) {
nx = x;
ny = y;
if (xa != 0) {
if (Math.abs(xa) > 1) { // if the x-speed is greater than 1
nx = x + MathUtil.abs(xa); // returns -1 for negative numbers and 1 for positiv
xa -= MathUtil.abs(xa);
} else { // less than 1
nx = x + xa;
xa = 0;
}
}
if (ya != 0) { // same here
if (Math.abs(ya) > 1) {
ny = y + MathUtil.abs(ya);
ya -= MathUtil.abs(ya);
} else {
ny = y + ya;
ya = 0;
}
}
if (!Level.collision(nx, ny, width, height)) setPosition(nx, ny); // checks if there is an collision and sets the new position if not
else if (!Level.collision(nx, y, width, height)) x = nx; // if there was a collision check if the player can walk in x direction
else if (!Level.collision(x, ny, width, height)) y = ny; // or in y direction
}
}
My problem is the pretty much the same as CoderMusgrove's problem in his post (Pixel-perfect collision and doubles):
Summary & Question
I have a problem where if the speed of an entity isgreater thanthe distance from the tile it is going into, it will leave at least a pixel in between itself and the tile, and I really don't like this. What kind of algorithm could I use that will find the tiniest difference between the entity and the tile?
If you need any additional information, I will be glad to add it.
Thanks for your help!
Easily resolvable by changing your interpretation.
You are retaining a fractional position for the purpose of fine grained speed. Ignore the fraction for the purpose of collision detection and display (if you were to do sub-pixel rendering, do the collision on the subpixel rendering accurarcy level).
int screenX = (int) Math.round(objX);
int screenY = (int) Math.round(objY);
// rendering and collision detection based on rounded position
I am simply trying to make a generic way to check for collision between rectangles. If you hit the top of a rectangle stop moving. However my rectangles seem to stop regardless of the x coordinate. I have three rectangles falling and one static rectangle that doesn't move, they are supposed to all fall on top of this static rectangle however when I run the code this happens.
Here is the collision handler code
float distanceY, furthestLeft;
public void update() {
for (int i = 0; i < stageObjects.size(); i++) {
iPos = stageObjects.get(i).getPosition();
iDim = stageObjects.get(i).getDimensions();
for(int k = 0; k < stageObjects.size(); k++){
kPos = stageObjects.get(k).getPosition();
kDim = stageObjects.get(k).getDimensions();
if(k == i){
continue;
}
distanceY = assignDy();
furthestLeft = assignDx();
//DistanceY is the subtraction of one objects y coordinate and
// if it equals 0 then they are colliding and the furthest left
// is the bottom right x coord of the furthest left objects x
//coord so it should check if this x is contained within an
// objects left x coord to right x coord
if(distanceY <= 1 && distanceY >= 0 && furthestLeft >= iPos.x && furthestLeft <= iPos.x + iDim.x){
stageObjects.get(i).react(Tuples.HIT_BOTTOM);
stageObjects.get(k).react(Tuples.HIT_FROM_TOP);
System.out.println("Collision: " + stageObjects.get(i).toString() + " with " +
stageObjects.get(k).toString());
}
}
}
}
}
public float assignDy(){
if(kPos.y > iPos.y){
return Math.abs(kPos.y - (iPos.y + iDim.y));
}else
return Math.abs(iPos.y - (kPos.y + kDim.y));
}
public float assignDx(){
if(kPos.x > iPos.x){
return kPos.x + kDim.x;
}else
return iPos.x + iDim.x;
}
The error lies here and here is the react method
public void react(int occurence){
if(occurence == Tuples.HIT_BOTTOM){
velocity.y = 0;
}
}
However, if they are further apart the code works perfectly look.
I have also noticed that the rectangle can fall through other rectangles if it is to the left of another rectangle, but if it further to the right at all it will get hung as if it landed on the rectangle. The only reason the above image worked is because the furthest right fell first, anything to the left to another rectangle will get hung as well if it falls after the rectangle to the left
I just don't see what exactly I am doing wrong any help is greatly appreciated!
Change iPos in the condition to something more generic, assign a variable like assignDx and dy to ccheck if Ipos or kPos is what you need to check for like so
public void assignOx(Vector2 ox){
if(kPos.x > iPos.x){
ox.x = iPos.x;
ox.y = iPos.x + iDim.x;
}else{
ox.x = kPos.x;
ox.y = kPos.x + kDim.x;
}
}
I have been working on a 2D game, kind of like Terraria, and i have got to the stage where i am ready to do collision detection. I tried it myself, but it... well, its very strange. I was wondering if anybody could help me with this. I store all of my blocks in a 2D array:
Block[][] map = new Block[mw][mh];
where mw is map width and mh is map height (in terms of number of blocks). Each Block is an image that is displayed as 16x16 pixels. Here is what i attempted, i know that it is wrong but i dont really know what i need to do.
private void checkCollision() {
for(int x = -1; x <= 1; x++){
for(int y = -1; y <= 2; y++){
Rectangle obj = new Rectangle((int)Block.getXOnScreen(xblock+x), (int)Block.getYOnScreen(yblock+y), 16, 16);
try{
if(main.map[(int) (xblock+x)][(int) (yblock+y)].solid && obj.intersects(bounds()){
if(y <= -1 && velY > 0){
velY = 0;
System.out.println("Collision below");
onground = true;
}else if(y >= 2 && velY < 0){
velY = 0;
System.out.println("Collision above");
}
if(x <= -1 && velX < 0){
velX = 0;
System.out.println("Collision left");
}else if(x >= 1 && velX > 0){
velX = 0;
System.out.println("Collision right");
}
}
}catch(Exception e){}
}
}
}
I call this method every tick like so, but it doesn't collide with anything so the player just falls.
public void tick(){
xblock += velX;
yblock += velY;
velY += gravity;
checkCollision();
}
If anyone knows how to do collision detection efficiently please can you share this with me. Thanks! :)
Based on the few JavaFX/Swing applications that I once wrote, the way you want to detect collision is by always keeping track of where the object is, and when it moves check to see that it is new location (old + movement count) is not exceeding the limits of the map. If it does, you want to reset its position to be right on limit so that it can go in all other directions that are valid. If you keep trying to move outside of the limits then it should keep resetting your position.
So say the limit of the page is 500 pixels wide, you are in position 495, and each move is 10, next time you move you would in position 505, so that's clearly out of range. Thus you run a check if(currentPosition > limit) {reset position to limit} thus your currentPosition will be set to 500 which is right on the border, and within the limits.
I am struggling with what must be a basic concept, but can you have a look at my issue?
I have the code where: ai moves the player bat, HEIGHT = total height of Display, and batHeight is the size of the pong paddle/bat:
public void ai(int bally, int HEIGHT, int batHeight) {
if (bally < this.y + ySize / 2) {
if (this.y <= 0) {
System.out.println("Upper Bound");
y = 0;
} else {
y -= 2;
}
}
if (bally > this.y + ySize / 2) {
if (this.y >= HEIGHT - batHeight) {
System.out.println("Lower Bounds");
y = HEIGHT - batHeight;
} else {
y += 2;
}
}
}
The above does exactly what I want it to do. Pong Bat moves up, and when it hits the top of the screen, it prints the console line, and stops the Bat. Exactly the same happens at the bottom of the screen. It prints the console, and stops the bat. It does this every time with no issues.
Now, if I modify the code slightly:
public void ai(int bally, int HEIGHT, int batHeight) {
if (bally < this.y + ySize / 2) {
if (this.y <= 0) {
System.out.println("Upper Bound");
y = 0;
} else {
if(rand.nextInt(2)+1 == 1){
y -= 2;
}else{
y -=3;
}
}
}
if (bally > this.y + ySize / 2) {
if (this.y >= HEIGHT - batHeight) {
System.out.println("Lower Bounds");
y = HEIGHT - batHeight;
} else {
y += 2;
}
}
}
It iterates once, stopping at the top bound, but then it loses itself, and forgets the bounds and the bat moves off the screen. I have Console printing the Bat y position, and it tracks with no issue, accurately displaying its y co-ord, but after the first iteration, it goes to negative y and greater that screen height.
I did have the theory that you cannot nest a IF inside an ELSE statement, so i tried moving it around so that it read:
if(this.y != 0){
if(rand.nextInt(2) + 1 == 1){
//move the paddle at speed 1
} else {
//move paddle at speed 2
}
}else{
//do not move the paddle
}
But that made no difference.
The idea behind the code was to add some chance for the AI bat. Sometimes its fast, and other times it is slower.
Thanks in advance,
Your code from far away looks like this:
for a given time:
if the ball is below the paddle {
if the paddle is below the screen, put it back
else move it down 2 or 3 units
}
if the ball is above the paddle {
if the paddle is above the screen, put it back
else move it up 2 units
}
Imagine the case where the ball is at y = 1 and the paddle is at y = 2. The first if statement will be triggered (1 < 2), the paddle is not outside (2 > 0), so it moves down 2 or 3 units. Let's say 3, for argument's sake. Now, paddle is at y = -1, the ball is still at y = 1. Now, the condition for the second big if is true! So we enter it: the paddle's not above, and we move it up two units. Now, the paddle is at y = 1...
It is clear that it should not have entered the second loop. So, stick an else in front of it, because it should only ever enter one :)
I'm making a android game for a school project. I'm familiar with java, but not experienced with making games. In my game a ball is controlled by the player. This ball needs to bounce of of walls.
I've tried this in two ways, but both unsuccessful. First try: I'm able to detect overlap, but not able to detect the side the ball hits.
c = the ball, r = the wall
float closestX = c.center.x;
float closestY = c.center.y;
if(c.center.x < r.topLeft.x) {
closestX = r.topLeft.x;
}
else if(c.center.x > r.topLeft.x + r.width) {
closestX = r.topLeft.x + r.width;
}
if(c.center.y < r.topLeft.y) {
closestY = r.topLeft.y;
}
else if(c.center.y > r.topLeft.y + r.height) {
closestY = r.topLeft.y + r.height;
}
return c.center.distSquared(closestX, closestY) < c.radius * c.radius;
So I tried a new approach. But this approach is unstable and treats the ball like a square.
cNew = the ball with the next position, cOld = the ball with the current position, r = wall
if (cNew.center.x + cNew.radius >= r.topLeft.x && cNew.center.x - cNew.radius <= r.topLeft.x + r.width)
{
if (cOld.center.y + cOld.radius < r.topLeft.y && cNew.center.y + cNew.radius >= r.topLeft.y)
{
return Side.TOP;
}
else if (cOld.center.y - cOld.radius > r.topLeft.y + r.height && cNew.center.y - cNew.radius <= r.topLeft.y + r.height)
{
return Side.BOTTOM;
}
}
if (cNew.center.y + cNew.radius >= r.topLeft.y && cNew.center.y - cNew.radius <= r.topLeft.y + r.height)
{
if (cOld.center.x + cOld.radius < r.topLeft.x && cNew.center.x + cNew.radius >= r.topLeft.x)
{
return Side.LEFT;
}
else if (cOld.center.x - cOld.radius > r.topLeft.x + r.width && cNew.center.x - cNew.radius <= r.topLeft.x + r.width)
{
return Side.RIGHT;
}
}
return null;
I need to combine these two is some way, but I haven't been able to find out how.
Help is much appreciated.
Didn't go very carefully with the code (and considering it is your school project, probably I shouldn't be doing your homework), but I believe treating the ball as a square would not have any negative effects if it is just going to bounce off walls. Is there something else you want to do with it?
Your first code is missing out on the fact that the surface of the ball will collide with the wall before its center. You might want to take that into account. And in what sense is your second code unstable?
Some more details would be useful here.
I am going to tell you here how I did :
for the player(and every enemy as well) you need :
x
y
w
h
and :
x velocity
y velocity
and the following point array(lists) :
outline :
upper side
lower side
left side
right side
all together
-all points that are in your ball, every pixel that hasnt alpha=0, for checking collision with walls
Walls :
walls are given as point array(list). For example, analyze the level image, every black pixel is added
Now, do the following :
in your class, have a method for moving
there you need the following logic :
int miny=Integer.MAX_VALUE;
for (Point p:walls) { //For every point in the walls
if (p.x >= (int)x && p.x <= (int)x+w && (int)p.x-(int)x < lower_side.length) {
try {
Point p2=lower_side[(int)p.x-(int)x]; //Get the point that is on the same height as the walls point
if (p.y >= (int)(y+p2.y) && (int)(y+p2.y+yvel) >= p.y && p.y-p2.y-1 < miny) { //Check if you are going to hit the wall, and if it is earlier as the earliest point determined.
miny=p.y-p2.y-1d; //Where is the earliest point where this can happen
}
} catch (Exception bug) {
System.out.println(bug);
}
}
}
apply this to all directions and dimensions.
if (miny != Integer.MAX_VALUE) {
y=miny; //Set position over the wall
yvel=-(yvel*0.75); //Bounce off
}
If you have any questions, feel free to comment.