Jump to content

Problem with collision


didjs
 Share

Recommended Posts

Hi there,

 

I'm making a 2d platformer on my own, without using a game engine and I'm facing a very annoying problem with my collision system.

 

Each level is made of blocks which are placed side by side and displayed. You can find an example here :

 

http://cauvin.didier.free.fr/html5/squarejs/index.html

 

When the little square jumps in the middle of two blocks, we can notice that it is making a slight move to the right. For the collision manager, the square is making a collision on the left side of a block, but I really don't understand why because it's just on top. I have tested two or three collision detection algorithms with the same results. Here is the method I use :

 

square.createBoxCollisionManager = function() {var that = {};that.boxWithBoxCollision = function(obj1, obj2) {var c1X = obj1.getCenterPositionX();var c2X = obj2.getCenterPositionX();var c1Y = obj1.getCenterPositionY();var c2Y = obj2.getCenterPositionY();var normal = square.createPosition({x : (c1X - c2X), y : (c1Y - c2Y)});var overlapX = (obj1.halfWidth + obj2.halfWidth - Math.abs(normal.x));if (overlapX > 0) {var overlapY = (Math.round(obj1.halfHeight + obj2.halfHeight) - Math.abs(normal.y));if (overlapY > 0) {if (overlapX < overlapY) {if (normal.x < 0) {return 'left';}return 'right';}else {if (normal.y <= 0.1) {return 'top';}return 'bottom';}}}return '';}return that;}

As you can see, I need to know on which side the collision occures, so I return 'top', 'bottom', 'left', 'right' or ''.

 

Here is the code handling the collision detection result :

 

scene.when(player).onCollisionWith(boxes).then(function(info) { if (info.where === 'bottom') {player.velY = -player.velY;player.position.y = info.collisioner.position.y + info.collisioner.height;}if (info.where === 'top') {player.isJumping = false;player.isGrounded = true;player.position.y = (info.collisioner.position.y - player.height);player.position.x += info.collisioner.velX;player.velY = 0;}if (info.where === 'left') {player.isJumping = false;player.position.x = info.collisioner.position.x - player.width;}if (info.where === 'right') {player.isJumping = false;player.position.x = info.collisioner.position.x + info.collisioner.width;}});

I apply a gravity to the square and adding it to its velocity on Y. Maybe it's because of the values. I first thought that the square was falling down to quickly so I added a maximum velocity, changed the gravity value, but that was not a success. I also rounded the overlap on Y, not a success too. :)

 

If someone has a clue, I'm taking it! :)

 

Thanks,

Did

 

Link to comment
Share on other sites

Looks like your code isn't compensating for the direction of the hit. I can tell by hitting the very front of the first block and it moving me to the right when I would have expected it to move me to the left. You should probably find the difference between the 2 x coordinates and move it + or - based on that. If the character is -dx the block move -x if +dx the block move +x. This wont solve your center block issue as you have the code now returning once it finds a hit. This means it will only register the first hit and not the second. To combat this you could allow it to keep checking at which point the character would be asked to move +x and -x  resulting in the character moving 0. Or the better way to handle this is to create rectangles for the block groups and test collision with that. You'll do fewer checks and you'll never hit two blocks at the same time. 

 

What you render on the screen and what you test against don't need to be the same thing.

Link to comment
Share on other sites

Not sure if this will be useful, however I wrote a platformer logic that doesn't rely on collisions just the x and y of protagonist in relation

to platforms. It would get fiddly with a lot of platforms but perhaps you could improve on the code.

base = new createjs.Bitmap(base);    // Platformsbase.x = 0;base.y = 400;base.alpha = 1;//base.scaleX = .1;base2 = new createjs.Bitmap(base2);base2.x = 0;base2.y = 250;base2.alpha = 1;

Each platform has an accessible id

 

As you can tell this would get complex with a lot of platforms. 

Also you would have to say: if ghost .x > platform.x + length of platform. If you wanted platforms on the same level.

if(helpmeghost.y < base4.y){	   path = base4;}elseif(helpmeghost.y > base4.y &&    helpmeghost.y < base.y){	   path = base;}elseif(helpmeghost.y > base.y &&    helpmeghost.y < base2.y){		path = base2;}elseif(helpmeghost.y > base2.y &&    helpmeghost.y < base3.y){		path = base3;}	if (				helpmeghost.x >= path.x && 				helpmeghost.x <= path.width && 				helpmeghost.y >= path.height &&				helpmeghost.y >= path.y)				{				helpmeghost.y=path.y; 		// set pos of ghost to platform/base/path				vy=0; 						// switch gravity off				jumping = false;				inAir = false;							}else{				inAir = true;			}function path(width, height, x, y){	this.width = width;	this.height = height;	this.x = x;	this.y = y;}// you can determine the width and height of platforms despite how they look. Perhaps you have a drop shadow on your image. Wouldn't want to bump into one of those.

Sorry that's a bit of a mess, I have to go to work now, it may or may not be helpful

Link to comment
Share on other sites

Here's the `rectangleCollision` function I always use to prevent two rectangles from overlapping:

 

https://gist.github.com/kittykatattack/89445303755c4f0b6161

 

It returns `undefined` if there's no collision. If there is a collision, it returns "top", "left", "bottom" or "right" so that you know on which side the collision occurred.

For this function to work your sprites need `x`, `y`, `center.x`, `center.y`, `halfWidth` and `halfHeight` properties.

It's based on the SAT (Separating Axis Theorem)  - I've used it in dozens of projects and it's very precise.

 

This function is from this book:

 

http://www.apress.com/9781430247166

 

And used in this micro game engine:

 

https://github.com/kittykatattack/ga

Link to comment
Share on other sites

Hi there,

 

I tried this and it seems to be nearly working :

 

http://jsfiddle.net/didjs/5b7ee69j/7/

 

I "cast" the overlapX and overlapY values as intefers (with | 0) so it's kind of rounded. And then when I test their values, I apply a not strict comparison with 0.

 

What do you think? I now have to see why the player is falling down when jumping in the middle of the two blocks.

 

Edit : In fact, this doesn't work at all if I place the blocks 100 pixels above... Grrrrr....

Link to comment
Share on other sites

You really shouldn't be returning the collision values the way you are. You need to perform all the checks and then return a value. As it stands you can never hit the corner of a block, just left,right,top,bottom.

 

I would recommend changing it from top, bottom, left, right to an hit-x and hit-y value. They both start at 0 and depending on a hit the x and y values change. You can assume that if you hit left, you won't hit right, and if you hit top, you won't hit bottom. However you should make it possible to return 2 values for those pesky corners. 

 

Your player is falling between the 2 blocks because the distance it travels puts it at a point that it registers as hitting left on the first block. If you also returned the y value it would know that it hit the top left, and prevent the fall through.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...