I am trying to get the collision in my game to be exactly perfect. What I am testing is if you hit a wall with the player, you come to a stop. I only implemented the collision code for when the player hits the left side of a wall(when the wall is on the right side of the player). Here is the code.
if(entityOnRight){
if(player.getPositionCorner(SquareMapTuples.BOTTOM_RIGHT).x -
ent.getPositionCorner(SquareMapTuples.BOTTOM_LEFT).x > -.9f)
player.setMovementBooleans(false, false, false, false);
else
player.setMovementBooleans(true, false, false, false);
}
Note: If I go very slow, it will stop the player where I desire it to be stopped, but going fast, it won't do the collision the way I want
Essentially, the code states if the wall is on the right side, it will check the bottom right corner of the rectangle of the player, subtract the bottom left corner of the wall, and check if the distance between the two is 0.001. 0.001 is almost an unnoticeable distance, hence why I used that value. Here is the code for player.setMovementBooleans
public void setMovementBooleans(boolean canMoveRight, boolean canMoveLeft, boolean canMoveUp, boolean canMoveDown){
this.canMoveRight = canMoveRight;
if(canMoveRight == false && moveRight)
vel.x = 0;
}
The canMoveRight boolean in the Player class (not in parameters) is what allows you to be able to move, moveRight is when you are trying to move right. Here is some code that will better explain how these booleans interact:
//If you clicked right arrow key and you're not going
//Faster then the max speed
if(moveRight && !(vel.x >= 3)){
vel.x += movementSpeed;
}else if(vel.x >= 0 && !moveRight){
vel.x -= movementSpeed * 1.5f;
System.out.println("stopping");
//Make sure it goes to rest
if(vel.x - movementSpeed * 1.5f < 0)
vel.x = 0;
}
and:
if(Gdx.input.isKeyPressed(Keys.D) && canMoveRight)
moveRight = true;
else
moveRight = false;
So to give a summary, if you click the "D" key, it allows you to start moving. However if the boolean canMoveRight is false, it won't move you. Here is an image showing what happens (The player is yellow, the wall is green)
As you can see, the player goes much further then I want it to. It should stop at this point:
Any help with figuring out how to accomplish this is extremely appreciated!
Maybe the way you tried it is a bit too complicated :-). I suggest a simpler way from scratch: make the map and the player a com.badlogic.gdx.math.Rectangle instance. Now, in the following part of the code you make a check, whether after the move the player would still be inside the map, if yes then allow the move, if not, then don't allow:
if(Gdx.input.isKeyPressed(Keys.D){
float requestedX, requestedY;
//calculate the requested coordinates
Rectangle newPlayerPositionRectangle = new Rectangle(requestedX, requestedY, player.getWidth(), player.getHeight());
if (newPlayerPositionRectangle.overlaps(map) {
//move the player
} else {
//move the player only to the edge of the map and stop there
}
}
The best way to handle those collisions would be to use a physics engine like Box2D which already comes packed with Libgdx. When a collision occurs in Box2D a event gets fired and you can easly handle that event. So you should probably take a look here.
Another alternative to achieve this without physics would be to use logical rectanlges representing the player and walls (can also be polyline) and use Intersector class of libgdx.
which is here.
Related
I am writing a physics engine and when the ball hits a surface it bounces up at half the velocity it came down at(velocity.y = -velocity). However when the ball is almost at rest it switches velocities constantly and starts falling through the platform incredibly slow but still will eventually fall through if I let it run long enough, and this is not acceptable. I tried writing some code to stop it but it checks way too fast here is the update method that attempts to check it.
public void update() {
velocity.y += Globals.GRAVITY.y;
if(canFall)
position.y += velocity.y;
position.x += velocity.x;
oldPosition.y = position.y;
oldPosition.x = position.x;
elapsedTime += 1 * Gdx.graphics.getDeltaTime();
if(elapsedTime >= 5){
if(oldPosition.y - 5 <= position.y && oldPosition.y + 5 >= position.y){
elapsedTime = 0;
canFall = false;
}else{
canFall = true;
elapsedTime = 0;
}
}
}
The collision handler knows when it hits the top of the ball which is the only solution I need for this. How do I check when the ball is at rest.
This code attempts to check if the ball hasn't moved in a couple seconds, if it hasn't it sets canFall to false. I need a generic check that will see if the ball should be at a stop. The velocity of the ball when it is falling through the platform you can see from the picture below. The picture shows the ball "at rest" after ten seconds so as you can see the ball is slowly but surely falling through. Every three seconds with this "rest" velocity the y position goes down 1, the velocity changes from positive to negative so the velocity you see in the picture isn't constantly negative. Ignore the elapsed time variable Any help is greatly appreciated!
Why let time have anything to do with it? I'm not saying it coudln't, but should it? So maybe lose the if(elapsedTime ... ) conditional.
Isn't this what you want? If the magnitiude of the vertical difference between the last two positions is negligible (set tolerance accordingly; maybe it's 5), stop bouncing:
if(Math.abs(position.y - oldPosition.y) < tolerance)
{
canBounce = false;
...
}
else
{
canBounce = true;
...
}
or, if nothing else needs to change at the ... above:
canBounce = Math.abs(position.y - oldPosition.y) > tolerance;
Also, don't you need { and } somewhere in the segment below? Maybe not, if ball is bouncing vertically, but it looks like x can change, too, which might make the code above need adjusting. But the way code below is indented makes me think there's supposed to be more to do than just changing position.y:
if(canFall)
position.y += velocity.y;
position.x += velocity.x;
oldPosition.y = position.y;
oldPosition.x = position.x;
Finally, you're addding velocity to position. That seems questionable. Multiply velocity times time? Or is time = 1 always? (Doubt it.)
The problem may be external to this method. Something needs to keep ball's y from becoming less than the y of the top of the dark blue box. This code can't do that without knowing y of top of dark blue box (unless you apply physics formulas?). Maybe include this value in parameter list as topOfDarkBlueBox?
Then add code such as:
if(position.y < topOfDarkBlueBox.y) position.y = topOfDarkBlueBox.y
I'd rather see physics at work, but you might say, "Who'd notice?"
EDIT
You wrote, "The collision handler knows when it hits the top of the ball," but I assume you meant "when the ball hits the top of the dark blue box". Maybe call the collision handler instead of passing new parameter topOfDarkBlueBox?
I'm creating java game (I'm a beginner with this for now) and I'd like to start with some kind of platform game.
I'd like to know how I can make the player jump (I know how to move him up and down), but I don't know how how to make him go back down after going up.
Here is my code:
public void keyPress() {
if (listener.arrowUp) {
Jump();
}
}
private void Jump() {
if(player.get(1).getPosY() > maxJump) {
player.get(1).moveY(-10);
} else if(player.get(1).getPosY() == maxJump) {
player.get(1).moveY(85);
}
}
So.. the player moves -10px upwards as long as i press 'w' and when he hits maxJump (which is 375 and players position at the start is 465) he "teleports" back to 465 instead of sliding back down like he does when going up.. It's really hard to explain this without a video, but i hope somebody understands and can help me with this.
This question gives a basic answer to yours. Now in your jump function, you have to set the vertical_speed only once and only call fall() every frame and add some moveY.
Here are two alternatives:
Option 1. Simulating some very simple physics. You add forces to your player, so pressing the up arrow will add a force that makes the player move upwards. Gravity is constantly applied and thus it will gradually cancel out the force you applied when the up arrow was pressed. Perhaps you only want to use forces in the vertical direction you do something like this:
// Each frame
if(notOnTheGround){
verticalVelocity -= GRAVITATIONAL_CONSTANT;
}
// When user jumps
vertivalVelocity += JUMP_FORCE;
Option 2. An alternative is to kind of animate the jumps using projectile motion.
// Each frame
if(isJumping){
verticalVelocity = initialVelocity*sin(angle) - g*animationTime;
animationTime += TIME_STEP;
}
// When user jumps
isJumping = true;
animationTime = 0;
initialVelocity = ... // calculate initial velocity
i'm quite stuck. Before I do anything, heres the code:
if(inAir&&!falling&&!jumping){
if(py<600){
if (!(isBlocked(xminusd, py) || isBlocked(xminus, py + 32 - 1))) {
falling=true;
}else{
py-=2;
inAir=false;
}
}
}
for(int g = 0;g<Map.r.size();g++){
if(rect.intersects(Map.r.get(g))||Map.r.get(g).contains(rect)||rect.contains(Map.r.get(g))||Map.r.get(g).intersects(rect)){
System.out.println("Intersecting!");
inAir= false;
hasjumped=false;
onPlat = true;
falling = false;
jumping = false;
py-=4;
break;
}else{
onPlat = false;
if(inAir==false&&!onPlat){
inAir = true;
onPlat = false;
}
}
}
Now the problem is, i'm trying to make collision detection with a certain type of tile, by creating rectangles for each tile, and if the player collides with it it stops all movement.(falling wise at least). But i've run into a problem. I've used an array list, to create all my rectangles, and i'm using a for loop to check each rectangle. Problem is, if it checks a rectangle and i'm not colliding with that rectangle currently, it immidiatly starts falling, then resetting, because it finds the rectangle i'm colliding with. The problem is im using a for loop to cycle through each rectangle... I'm really stuck on how to do collision detection with platform tiles. Anyone have any help to provide? Please be descriptive.
It doesn't look like it matters which rectangle you are colliding with so try abstracting by making a function that returns true or false if it is colliding with any block and put that in your else if statement. that way if you are still colliding it wont start falling while cycling through the rest of your blocks
Something like
...
if(colliding())
{
//stop falling
}
else
{
//start falling
}
....
boolean colliding(){
for(int g = 0;g<Map.r.size();g++){
//check for collision if true return true
}
return false;
}
EDIT:
The reason this works is because as you are going through the for loop it keeps the object falling while it determines that it should stop. By moving the falling logic away from the collision logic we can determine if it is colliding and then take the appropriate action.
I've been stuck on a bug regarding collision in my 2D Mario-like game.
public void collision() {
if (Rect.intersects(Robot.rect2, r)){
robot.setSpeedY(0);
robot.setCenterY(tileY - 32);
robot.setJumped(false);
} else if (Rect.intersects(Robot.rect, r)){
robot.setSpeedY(0);
robot.setCenterY(tileY + 32);
}
updateRect();
if (Rect.intersects(Robot.rect3, r)){
robot.setCenterX(tileX + 32);
robot.setSpeedX(0);
} else if (Rect.intersects(Robot.rect4, r)){
robot.setCenterX(tileX - 32);
robot.setSpeedX(0);
}
}
Collision in my game is checked by comparing rectangles. My robot's rectangles: rect (bottom collision, so rectangle on bottom half of my robot sprite), rect2 (top collision), rect3 (left collision), and rect4 (right collision).
Every tile in my game is bounded by an instanced Rectangle r. When my robot's rectangle and the tile's rectangle intersect, I call collision(), which sets the position of my robots direction.
Here is my problem: If my robot is at a corner, where it is in bottom collision and right collision, and I move to the right, my robot moves INTO the right tile, and that triggers the bottom collision, and screws everything up.
Switching the postiions of the Bottom/Top & Right/Left collision doesn't help since then the same problem occurs, where the robot falls into the ground, triggering the Right/Left collision.
Essentially, I need a way to run all four at the same time.
Thanks
Yku are trying to build it one check at a time. Why not something like
if(!Rect.intersects(robot.rect1) && !Rect.intersects(robot.rect2) && !Rect.intersects(robot.rect3) && !Rect.intersects(robot.rect4)
{
//act normally
} else {
//collieion
}
What youre doing now is checking one side and base your result on that.
yes I also had a trouble with that, what you are missing is to call updateRect(); again after you made the collision detection on the X axis.
And I recommendto check the X axis first.
here is a nice Article about tile based Collision:
http://higherorderfun.com/blog/2012/05/20/the-guide-to-implementing-2d-platformers/
I am working on a program which simulates objects moving in a field. The field has a boundary of 1024x1024. The object cannot go below 0 in terms of x,y coordinate and it cannot go above 1024. I have a method for each object called "move()" which moves the object in its current direction at its current speed. If the object approaches the boundary, it then turns around with a new direction and same speed.
The problem I am having is that when one of my objects gets close to both the x and y bound (corner of the field), it gets stuck in the corner. It is almost as if it is trying to move away from the corner, but then it turns back. It must love that corner. I looked over my code and to me, my logic seems correct. I check to make sure the new direction is not negative or over 359. I check to make sure the new x,y coordinate with the new direction is within the bounds too. I even have a method to set a new direction.
I have tried re-implementing this method with different logic, but no luck. If anyone could possibly find a flaw in my programming or point out what may be causing it, then that would be much appreciated.
I have tried to debug and step through my program and I see that when it gets to the corner, it changes direction to turn around, moves about 3 spaces, then goes back to the corner. Must be a wonderful corner.
Code for move method is below:
public void move(){
localX = super.getX();
localY = super.getY();
float newX=0, newY=0;
float testX, testY;
boolean acceptX = false, acceptY = false;
testX = (float) (Math.cos(direction)*10) + localX;
testY = (float) (Math.sin(direction)*10) + localY;
int testDirection;
while(!acceptX){
if(testX >= 0 && testX <= bound){
newX = testX;
acceptX = true;
}//end if statement
else{
if(direction+180 > 359){
setDirection(direction-180);
testX = (float) (Math.cos(Math.toRadians(direction))*speed) + localX;
}
else{
setDirection(direction+180);
testX = (float) (Math.cos(Math.toRadians(direction))*speed) + localX;
}
}//end else
}//end while that checks for X value
while(!acceptY){
if(testY >= 0 && testY <= bound){
newY = testY;
acceptY = true;
}//end if statement
else{
if(direction+180 > 359){
setDirection(direction-180);
testY = (float) (Math.sin(Math.toRadians(direction))*speed) + localY;
}
else{
setDirection(direction+180);
testY = (float) (Math.sin(Math.toRadians(direction))*speed) + localY;
}
}//end else
}//end while that checks for Y value
super.setX(newX);
super.setY(newY);
}
and here is the code for setDirection
public void setDirection(int d) {
direction = d;
}
Say you have an object in the upper left corner, going up. Your first test turns it around so it goes down. Then comes your second check, which turns it around again to go up... again.
Your code could also use some more readability. The very first thing I noticed is that you're using the >359 checks to normalize the new direction to go in. Yet all cases include the movement code as well. I would do something like:
setDirection(direction + 180); //turn around
if (direction >= 360) direction -= 360; //normalize
testY = ...; //move
to move the movement code out of the direction checking if/else blocks. 360 is also a better magic number to use; 359 degrees means nothing. As has been suggested, you should ultimately use a vector library and thus throw away most of the math.
I'd really recommend storing your direction as a vector (x, y) instead of calculating that vector from a scalar; I think that would help you immensely with your code.
Issue: When your object hits an edge, you turn it 180 degrees. If it hits both edges, it'll spin in place, and the test coordinates will always be in the wrong spot.
When one of your objects hits an edge, it needs to bounce, not About Face! Angle of incidence == angle of refraction, or some such. In other words, if you're checking the x coordinate and it bounces, negate the x velocity, not both x & y.