I've been trying to find/create a collision detection for rectangles in Libgdx but I can't seem to get anywhere. I have a rectangle called bucket with width and height 64 and a rectangle called wall with width and height 64. I'm trying to make it so that the player does not pass through the rectangle and can continue on moving while sticking to the wall without phasing through or random teleporting. My method works when there is 1 block but when there is more then one it just breaks and doesn't work.
I know this method is ugly but it is just experimentation
private void checkCollisions(Rectangle bucket, Wall wall){
if(bucket.overlaps(wall.getRectangle())){
if(bucket.x > wall.getRectangle().x - 64 && bucket.x < wall.getRectangle().x - 55){
//collision with right side
bucket.x = wall.getRectangle().x - 64;
}
if(bucket.x < wall.getRectangle().x + 64 && bucket.x > wall.getRectangle().x + 55){
//collision with left side
bucket.x = wall.getRectangle().y + 64;
}
if(bucket.y < wall.getRectangle().y + 64 && bucket.y > wall.getRectangle().y + 55){
//collision with top side
bucket.y = wall.getRectangle().y + 64;
}
if(bucket.y > wall.getRectangle().y - 64 && bucket.y < wall.getRectangle().y - 55){
//collision with bottom side
bucket.y = wall.getRectangle().y - 64;
}
}
}
I would be very appreciative if someone could point me in the correct direction or share some code that would help me.
In LibGDXwhen looking at collision detection you must first note two important aspects of the default LibGDX Coordinate system.
When you draw a image, Its (x, y) coordinate is the bottom left of the image
(0,0) is the bottom left side of the screen
Using this information we can fix your logic for collisions to the following:
private void checkCollisions(Rectangle bucket, Wall wall){
if(bucket.overlaps(wall.getRectangle())){
if(bucket.x + 64 > wall.getRectangle().x && bucket.x < wall.getRectangle().x){
//collision with right side of bucket
}
if(bucket.x < wall.getRectangle().x + 64 && bucket.x > wall.getRectangle().x){
//collision with left side of bucket
}
if(bucket.y + 64 > wall.getRectangle().y && bucket.y < wall.getRectangle().y){
//collision with top side of bucket
}
if(bucket.y < wall.getRectangle().y + 64 && bucket.y > wall.getRectangle().y){
//collision with bottom side of bucket
}
}
}
This method will determine which side of the bucket detected a collision with the default LibGDX coordinate system.
Related
In my platformcollision function, when the player is checking for collision on his left side everything is fine. My player doesnt go through, he can move the opposite way. However, when I put the warMage on the left and its checking if theres collision with the platform on his right side, the player immediately teleports next to the platform. I dont understand why as all I did was flip the arithmetic signs in that if statement from the other for the player to check its right side.
import flash.events.Event;
import flash.display.MovieClip;
var Key:KeyObject = new KeyObject(stage);//Help the stage checks for keypressed objects //Initialized variable integers
var hsp:Number = 0;// horizontal speed
var vsp:Number = 0;// vertical speed
var grav:Number = 2;//Gravity
var fric:Number = .5;//Friction
var floor:int = 800;//Bottom of the stage
//All Booleans
var lDown:Boolean = false;
var rDown:Boolean = false;
var jumped:Boolean = false;
var attacking:Boolean = false;
warMage.gotoAndStop("idleWarmage");//Initially starts at idle state
stage.addEventListener(Event.ENTER_FRAME, keyPressed);//Listens for buttons pressed
stage.addEventListener(Event.ENTER_FRAME, gameloop);// The physics applied to character
stage.addEventListener(Event.ENTER_FRAME, platformCollision);
function keyPressed(e:Event):void
{
if(Key.isDown(Key.LEFT))//If we pressed the left arrow button
{
lDown = true;//Condition to check if player is in running state
if(lDown = true)//If we are running left
{
hsp -= 15;//Move left
warMage.gotoAndStop("RunWarmage");//Play the running animation
warMage.scaleX = -1;//Flip the image scale
}
}else if(Key.isDown(Key.RIGHT))//If we pressed the right arrow button
{
rDown = true;//Condition to check if player is in running state
if(rDown = true)//If we are moving right
{
hsp += 15;//Move the position right
warMage.gotoAndStop("RunWarmage");//Play the animation
warMage.scaleX = 1//Face right
}
}else if(Key.isDown(Key.SPACE))//If we press the spacebar
{
warMage.gotoAndStop("AttackWarmage");//Play teh attack animation
warMage.x += 5; //Lunge right
if(warMage.scaleX == -1)//If we are initially facing left
{
warMage.x -= 10;//Lunge left
}
}else if(Key.isDown(Key.DOWN))
{
warMage.gotoAndStop("CrouchWarmage");
}else if(Key.isDown(Key.UP) || jumped == true)//If we press the up arrow or we've jumped
{
warMage.y -= 60;//vertical speed goes up to 20
jumped = true;//We know that player has jumped
warMage.gotoAndStop("JumpWarmage");//Play teh jump animation
}else if(jumped == false)//If we're not jumping
{
warMage.gotoAndStop("idleWarmage");//Return to idle position
}
}
function gameloop(e:Event):void
{
warMage.y += grav;//Apply gravity to the player
hsp *= fric;//Friction is applied to hsp to prevent infinite acceleration
warMage.x += hsp;//The plater moves horizontal position
if(warMage.x - warMage.width/2 < 0)//If the player goes past the left side
{
warMage.x = warMage.width/2;
}
if(warMage.x + warMage.width/2 > 1400)//If the player goes past right
{
warMage.x = 1400 - warMage.width/2;//Player cant go past
}
if(warMage.y < floor)//If we are above the floor
{
// warMage.y += grav;//Apply gravity to the player
grav++;//Accelerate gravity in the process
warMage.gotoAndStop("JumpWarmage");//Play the jump animation
} else //if(warMage.y - warMage.height/2 > floor)
{
jumped = false;//If we are on the floor then we're not jumping
grav = 0;//Gravity can no longer be applied
warMage.y = floor;//Player sits on top of the floor
}
}
function platformCollision(e:Event):void
{
//If the player.x is less then the left side and the player can go into the box and if the warMage.y is equal to the height
if(warMage.x - warMage.width/2 < platform.x + platform.width/2 + 2 && warMage.y - platform.y == platform.height/2)
{
warMage.x = platform.x + platform.width/2 + warMage.width/2;
//Player.x is equal to the left side of the platform
}
if(warMage.x + warMage.width/2 > platform.x - platform.width/2 - 2 && warMage.y - platform.y == platform.height/2)
{
rDown = false;
warMage.x = platform.x - platform.width/2 - warMage.width/2;
//Player.x is equal to the left side of the platform
}
}
Don't you need if/ else if in here instead of 2 ifs?
If this bit is true:
if(warMage.x - warMage.width/2 < platform.x + platform.width/2 + 2 && warMage.y - platform.y == platform.height/2)
and you set
warMage.x = platform.x + platform.width/2 + warMage.width/2;
Then the 2nd if would be true right away.
Now warMage.x is platform.x + platform.width/2 + warMage.width/2;
So checking 2nd if:
if(warMage.x + warMage.width/2 > platform.x - platform.width/2 - 2 && warMage.y - platform.y == platform.height/2)
So warMage.x (which is now platform.x + platform.width/2 + warMage.width/2) + warMage.width/2 is definitely greater than platform.x - platform.width/2 - 2 and that's why you set
warMage.x = platform.x - platform.width/2 - warMage.width/2;
right after.
Looks like platformCollision needs rethinking.
I have a gun and a player. I want to constrain the angle of the gun so the player doesn't raise his gun up too much or too low. The player and gun turns to the right side when the mouse is facing the right, and if the mouse faces the left, the player and gun turns to the left. I want to constrain the gun between 160 and -160 when it faces right. And constrain the gun between 20 and -20 when it faces left. So it doesn't rotate over the constraining restriction.
I have a code that makes it rotate 360, but I'm not sure on how to stop it from rotating once it reaches a certain point.
if (parent != null)
{
var dx = MovieClip(parent).crosshair.x - x;
var dy = MovieClip(parent).crosshair.y - y;
var angle = Math.atan2(dy, dx)/Math.PI * 180;
rotation = angle;
}
Ok, here's what's happening:
Flash rotation at 0° faces strictly up. So the right side will be from 0° to 180° and left side will be from 0° to -180°. This is easy to separate because all right side > 0 and all left side < 0.
But, Math.atan2(dy, dx) calculates a different angle which cannot be directly assigned to object rotation. Instead of left and right side, it is < 0 on upper side and > 0 on lower side. If you calculate the rotation that way, it will be a mess.
So, the atan calculation must be shifted by 90° clockwise in order to match the Flash rotation. This is done by transforming the parameters and now it looks like Math.atan2(dx, -dy). After that the calculated angle and the rotation angle will match.
var angle:Number = Math.atan2(dx, -dy) / Math.PI * 180;
if (angle < 0) { // facing left
if (angle > -30) angle = -30;
if (angle < -150) angle = -150;
} else { // facing right
if (angle < 30) angle = 30;
if (angle > 150) angle = 150;
}
This is the solution without using -dy and instead using dy. (Code added by OP and I didn't check it :)
var angle = Math.atan2(dy, dx) / Math.PI * 180;
if (rotation > -180 && rotation < -90 || rotation > 90 && rotation < 180 )
{ // facing left
if (rotation > -150 && rotation < 0)
{
rotation = -150;
}
if (rotation < 120 && rotation > 0)
{
rotation = 120;
}
}
else
{
{ // facing right
if (rotation < -30)
{
rotation = -30;
}
if (rotation > 60)
{
rotation = 60;
}
}
Basically, I've made it so that my "player" in my game doesn't exceed the stage width (only moves along the x axis) and so when it gets to the edge it just stops. However, I want to make it so that if the player exceeds the width on the left side it will flow in from the right and vice versa. This is the code I have at the minute which is what stops it from leaving the stage area:
function movePlayer(e:Event):void {
player.x = stage.mouseX;
// Doesn't go off the right or left side.
if (player.x < 0) {
player.x = 0;
} else if (player.x > (stage.stageWidth - player.width)) {
player.x = stage.stageWidth - player.width;
}
}
Is there a way I could edit this?
You can use the % (modulus) operator to calculate a new position value. Something like:
player.x = player.x % this.stage.stageWidth;
I am working on a platform game and I am having issues with some collision detection. The game is set up with many blocks that are next to each other or spaced out so the player jumps from one block to the next. Right now the player is able to jump on top of the blocks, run into the side of the blocks and get stopped if jumping into the bottom of the blocks. The issues that I am having right now is that if I get in a rare scenario where the player falls on top of a certain part of the block, the game recognizes it as the side of the block for the block next to it and the player falls through the block. So if I am jumping onto the very edge of blocks[3] and blocks[4] is right next to it, the game recognizes that the player has jumped into the side of blocks[4]. Any idea how I can go about fixing this? Below is the code for my collision detection.
function checkBlockCollisions() {
// If a player collides with a block, their motion should be stopped.
for(var i = 0; i < blocks.length; i ++) {
var angle = blocks[i].angleTo(player);
var thisTop = blocks[i].y - blocks[i].height / 2;
var thisBottom = blocks[i].y + blocks[i].height / 2;
var playerTop = player.y - player.height / 2;
var playerBottom = player.y + player.height / 2;
if(blocks[i].collidesWith(player)) {
if((angle >= 55 && angle <= 360) && player.falling) {
// If the player is above the block, then they should stop falling.
player.falling = false;
player.onBlock = true;
player.setDY(0);
// Make sure the player is not inside the block.
player.setPosition(player.x, thisTop - player.height / 2);
}
//top of block is lower than the middle of the player to the top and bottom of block is higher than the middle of the player to the bottom
if(thisTop > (player.y - player.height / 2) && thisBottom < (player.y + (player.height / 2 ))) {
// If the player runs into the side of a block, only stop the sideways motion.
player.setPosition(player.x - player.dx, player.y);
player.setDX(0);
} // end if
else if(player.falling) {
// If the player is below the block, then they should keep falling but stop accelerating upwards and sideways.
player.setDY(0);
player.setDX(0);
player.setPosition(player.x, thisBottom + player.height / 2 + 1);
} // end if
} // end if
} // end for
} // end checkBlockCollisions
Check out my engine, it's an engine written entirely for collisions and is very simple to use: https://github.com/Murplyx/AAE---Axis-Aligned-Engine
I am having trouble with testing if a rectangle has collided with another rectangle and where the collision is in relation to each object (Left, right, Top, Bottom).
My code works well in theory but there are logical issues, there are false positives when an object enters the left side of another object. The parameters I have set mean that both the top collision and left collision become true when in truth only the left should be true.
image of problem
How can I stop a double positive happening in my code, I only need basic rectangle collision and nothing more. Thank you.
//Col on top?
if (Obj1.getRect(this).bottom - vSpeed < Obj2.getRect(this).bottom &&
Obj1.getRect(this).bottom - vSpeed > Obj2.getRect(this).top)
{
if (Obj1.getRect(this).right + hSpeed > Obj2.getRect(this).left &&
Obj1.getRect(this).left + hSpeed < Obj2.getRect(this).right)
{
Obj1.y = Obj2.y - Obj1.height;
vSpeed = 0;
colTop = true;
}
}
//Col on Bottom?
else if (Obj1.getRect(this).top - vSpeed > Obj2.getRect(this).top &&
Obj1.getRect(this).top - vSpeed < Obj2.getRect(this).bottom)
{
if (Obj1.getRect(this).right + hSpeed > Obj2.getRect(this).left &&
Obj1.getRect(this).left + hSpeed < Obj2.getRect(this).right)
{
Obj1.y = Obj2.y + Obj2.height;
vSpeed = 0;
colBot = true;
}
}
//Col on left side?
if (Obj1.getRect(this).right + hSpeed > Obj2.getRect(this).left &&
Obj1.getRect(this).right + hSpeed < Obj2.getRect(this).right)
{
if (Obj1.getRect(this).bottom - vSpeed > Obj2.getRect(this).top &&
Obj1.getRect(this).top - vSpeed < Obj2.getRect(this).bottom)
{
Obj1.x = Obj2.x - (Obj2.width * 0.5);
hSpeed = 0;
colLeft = true;
}
}
//Col on right side?
else if (Obj1.getRect(this).left + hSpeed > Obj2.getRect(this).left &&
Obj1.getRect(this).left + hSpeed < Obj2.getRect(this).right)
{
if (Obj1.getRect(this).bottom - vSpeed > Obj2.getRect(this).top &&
Obj1.getRect(this).top - vSpeed < Obj2.getRect(this).bottom)
{
Obj1.x = (Obj2.x + Obj2.width) + (Obj1.width * 0.5);
hSpeed = 0;
colRight = true;
}
}
You need to separate top collision from left collision by parsing the objects' relative speed. You cannot parse top/left collision using coordinates only because they are really both true if one rectangle's top left point goes inside another rectangle. So, move your Obj1 and velocity altering code out of your array of if statements, and leave the rest of the code as is. Then you check for velocities like this:
if (colTop)
if (Obj1.getRect(this).bottom > Obj2.getRect(this).top)
colTop=false;
Etc., for other collision vars. This code means: "If the bottom of Obj1 already was under the top of Obj2, then in this frame we are not hitting the top side". So, only the left side collision flag should remain. And only then you check for resultant collision vars and adjust coordinates and velocities
The starling library provides great collision detection, including simple rectangle collision. Are your methods being called on every frame?
All you would need for Starling collision detection
is:
var bounds1:Rectangle = image1.bounds;
var bounds2:Rectangle = image2.bounds;
if (bounds1.intersects(bounds2))
trace("Collision!");
collision detection