Hey guys i'm making a 2D java game and i'm trying to figure out how to make a good collision code. I am currently using the following code:
public void checkCollision() {
Rectangle player_rectangle = new Rectangle(player.getX(),player.getY(),32,32);
for(Wall wall : walls) {
Rectangle wall_rectangle = new Rectangle(wall.getX(), wall.getY(), 32,32);
if (player_rectangle.intersects(wall_rectangle)) {
Rectangle intersection = (Rectangle) player_rectangle.createIntersection(wall_rectangle);
if (player.xspeed > 0) {
player.x -= intersection.getWidth();
}
if (player.yspeed > 0) {
player.y -= intersection.getHeight();
}
if (player.xspeed < 0) {
player.x += intersection.getWidth();
}
if (player.yspeed < 0) {
player.y += intersection.getHeight();
}
Print(Integer.toString(intersection.width) + ", " + Integer.toString(intersection.height));
}
}
}
With this code it works fine if you are press one button but if press down and left for example the player will fly off in some random direction.
Here is a picture of the types of maps I have:
Your main problem is in assuming that the player is running directly into the wall. Consider the case where there is a wall rect (100,100,32,32) and the player is at (80,68,32,32). The player is moving down and to the left, so player.xspeed < 0 and player.yspeed > 0; say the next position for the player is (79,69,32,32). The intersection is then (100,100,11,1).
Note that although the player is moving left (as well as down) the wall is actually to the right of the player. This line:
if (player.xspeed < 0) {
player.x += intersection.getWidth();
}
... causes player.x to be set to 90 in a sudden jump.
One thing you could do is check that the player's left-hand side was contained in the intersection, i.e.
if (player.xspeed < 0 && player.x >= intersection.x) {
player.x += intersection.getWidth();
}
Obviously a similar thing needs to be done for the other directions too.
Related
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.
I want to implement a smooth transition when my sprite is exiting the screen. This means that if my sprite exits the screen from the left edge, it should appear on the right edge. The main problem that I am having is that I don't want to simply set the new coordinates of the sprite from one edge to another. For example, if the sprite is half through the left edge, the other half should appear on the right edge (smooth transition). Can anyone give any advice for this problem?
Pseudocode!
if(sprite.pos.x > screen.width)
{
sprite.pos.x -= screen.width;
}
else if(sprite.pos.x < 0 - sprite.width)
{
sprite.pos.x += screen.width;
}
if(sprite.pos.x + sprite.width > screen.width && sprite.pos.x < screen.width)
{
batch.draw(sprite.texture, sprite.pos.x - screen.width, sprite.pos.y);
}
else if(sprite.pos.x < 0 && sprite.pos.x + sprite.width > 0)
{
batch.draw(sprite.texture, sprite.pos.x + screen.width, sprite.pos.y);
}
batch.draw(sprite.texture, sprite.pos.x, sprite.pos.y);
My problem is when I move my sprite either directly upwards or directly sideways the sprite vibrates. I have tried some things but cant get it fixed. Here is my current code. The framework is libGDX. I have tried moving the sprite exactly to the mouse if its within 1 pixel range and other things but nothing works for me. Why does the sprite vibrate on a straight line and how can I stop it? Thanks!
float speed = 4; // the speed of the sprite
Vector3 mousePosition = new Vector3();
if (Gdx.input.isTouched())
{
camera.unproject(mousePosition.set(Gdx.input.getX(), Gdx.input.getY(), 0));
if(mousePosition.x > 0 && mousePosition.x < Screen.WIDTH )
{
if(mousePosition.y > 0 && mousePosition.y < Screen.HEIGHT)
{
if(mousePosition.x > player.x + (player.width /3))
{
player.x+=speed;
}
else if(mousePosition.x < player.x + (player.width /3))
{
player.x -=speed;
}
if(mousePosition.y > player.y + (player.height /2))
{
player.y+=speed;
}
else if(mousePosition.y < player.y + (player.height /2))
{
player.y-=speed;
}
}
}
}
batch.draw(player.sprite, player.x, player.y);
Consider the case where you are moving the player vertically up. In this case mousePosition.x remains constant and mousePosition.y increases. mousePosition.y increases in such a way that player.y never exceeds it, and so the sprite follows the mousePosition in the Y direction. But remember that mousePosition.x remains constant. So according to your code player.x will increase till it satisfies the condition mousePosition.x > player.x + (player.width /3), and once it exceeds that value, player.x will start decreasing till it satisfies mousePosition.x < player.x + (player.width /3). That means player.x is increasing and decreasing, causing it to vibrate in X direction. Same case occurs when you move horizontally. But in this case, the sprite will vibrate it Y direction
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.
This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
Java 2D Collision?
Hey guys I have another post regarding this which has just gone dead so I thought i'd try get some fresh answers regarding this.
I'm trying to get my collision detection to work. My collision detection works fine when i come side on to my map blocks, and fine when I only hold up or down and hit a block from the top or bottom. But when i hold up or down aswell as right or left and hit a block from the top or bottom it will go off in funny directions.
Small Video of me demonstrating (Sorry if it's not clear): http://www.youtube.com/watch?v=6ILccRtw8ME Hopefully this helps alot.
If you didn't watch the video here is a picture of the map's I will be using:
Here is my current collision code:
public void checkCollision() {
Rectangle player_rectangle = new Rectangle(player.getX(),player.getY(),32,32);
for(Wall wall : walls) {
Rectangle wall_rectangle = new Rectangle(wall.getX(), wall.getY(), 32,32);
if (player_rectangle.intersects(wall_rectangle)) {
Rectangle intersection = (Rectangle) player_rectangle.createIntersection(wall_rectangle);
if (player.xspeed < 0 && player.x >= intersection.x) {
player.x += intersection.getWidth();
} else {
if (player.xspeed > 0 && player.x <= intersection.x) {
player.x -= intersection.getWidth();
} else {
if (player.yspeed < 0 && player.y >= intersection.y) {
player.y += intersection.getHeight();
}else {
if (player.yspeed > 0 && player.y <= intersection.y) {
player.y -= intersection.getHeight();
}
}
}
}
Print(Integer.toString(intersection.width) + ", " + Integer.toString(intersection.height));
}
}
}
All help will be appreciate, thanks.
This is my first attempt at answering a question on StackOverflow so please be patient with me. I will try and structure this answer by first diagnosing the problem, finding the symptoms that may cause your problem and lastly, present a solution and see if it works with you.
First, let me format your code with the correct level of indentation so that we have a more clear picture of what is going on.
public void checkCollision() {
Rectangle player_rectangle = new Rectangle(player.getX(),
player.getY(), 32, 32);
for (Wall wall : walls) {
Rectangle wall_rectangle = new Rectangle(wall.getX(), wall.getY(),
32, 32);
if (player_rectangle.intersects(wall_rectangle)) {
Rectangle intersection = (Rectangle) player_rectangle
.createIntersection(wall_rectangle);
if (player.xspeed < 0 && player.x >= intersection.x) {
player.x += intersection.getWidth();
} else {
if (player.xspeed > 0 && player.x <= intersection.x) {
player.x -= intersection.getWidth();
} else {
if (player.yspeed < 0 && player.y >= intersection.y) {
player.y += intersection.getHeight();
} else {
if (player.yspeed > 0 && player.y <= intersection.y) {
player.y -= intersection.getHeight();
}
}
}
}
Print(Integer.toString(intersection.width) + ", "
+ Integer.toString(intersection.height));
}
}
}
Now that we have done this, it becomes more clear what you are doing. The first thing I noticed is that you are nesting your conditional statement. To clarify, you are doing this:
if (condition){
// do something
else{
if(anotherCondition){
// do something
else{
//etc
}
}
}
}
What you should be doing is using multiple conditional statements in a row like this:
if (condition){
//do something
}
if (anotherCondition){
//do something else
}
//etc
This is because if you have two case, such as you being in the bottom left corner, you will need to execute the conditions for the intersection to the left as well as intersection to the bottom. However, this is irrelevant for your example since you go through each wall individually.
So why is this important?
Well, it seems to me that if the first condition is not satisfied, then all the other conditions will not execute. If player.xspeed < 0 && player.x >= intersection.x returns true, then even if the other 3 statements nested in it returns true, they will not be executed because it relies on that first statement being false. Therefore when you go to an edge, where two or more of the cases would be true, you only get one of them executed.
Therefore my proposed solution, based on all the above is this:
public void checkCollision() {
Rectangle player_rectangle = new Rectangle(player.getX(),
player.getY(), 32, 32);
for (Wall wall : walls) {
Rectangle wall_rectangle = new Rectangle(wall.getX(), wall.getY(),
32, 32);
if (player_rectangle.intersects(wall_rectangle)) {
Rectangle intersection = (Rectangle) player_rectangle
.createIntersection(wall_rectangle);
if (player.xspeed < 0 && player.x >= intersection.x) {
player.x += intersection.getWidth();
}
if (player.xspeed > 0 && player.x <= intersection.x) {
player.x -= intersection.getWidth();
}
if (player.yspeed < 0 && player.y >= intersection.y) {
player.y += intersection.getHeight();
}
if (player.yspeed > 0 && player.y <= intersection.y) {
player.y -= intersection.getHeight();
}
Print(Integer.toString(intersection.width) + ", "
+ Integer.toString(intersection.height));
}
}
}
Try this out and see if it works. If it does not, then we can take it from there and see how to improve and fix your problem.
I suggest writing a bunch of unit tests for all the different states you can imagine that checkCollision needs to deal with.
This way, you'll:
Easily find out which specific inputs are causing the block to jump (because unit tests are short and well-contained)
Ensure that this functionality won't be broken by future changes (because unit tests are run with each build)
A sample unit test for this would look something like this:
public class CollisionTests {
#Test
public void hitFromLeftHeadOn() throws Exception {
List<Wall> walls = Lists.newArrayList(
new Wall( ... ), // south wall
new Wall( ... ), // east wall
// etc.
);
Player player = new Player(...);
// Set player to collide with the east wall head on
player.setX(...);
player.setY(...);
player.setXSpeed(...);
player.setYSpeed(...);
checkCollision();
// Now check if the checkCollision method worked properly
assertEquals("Player's x coordinate should be set to ...", ..., player.getX());
assertEquals("Player's y coordinate should be set to ...", ..., player.getY());
}
Once you've written one test, it's easy to add more. You can create various helper methods to create the walls, player, etc. so that each test case shrinks down to a few lines.
Here's a tutorial on unit testing.
As an added bonus, if you write a unit test you'll also have written an SSCCE :)