javascript - AABB collision resolution slipping sides -
so, reinventing wheel (and learning lot) trying hand @ making simple physics engine game engine. have been searching internet, trying (and failing) fix current problem. there lot of resources out there on subject, none of have found seem apply case.
the problem in short: collision resolution not work intended on of corners when 2 rectangles colliding. how fails varies based on dimensions of rectangles. looking "shortest overlap" kind of resolution collision or simple solution (i open suggestions!). (scroll down better explaination , illustrations).
warning: following code not efficient...
first of all, here physics loop. loops through of game entities , checks if collide other game entities. not efficient (n^2 , of that), works now.
updatephysics: function(step) { // loop through entities , update positions based on velocities (var entityid in vroom.entitylist) { var entity = vroom.entitylist[entityid]; if (entity.physicsenabled) { switch (entity.entitytype) { case vroomentity.kinematic: entity.pos.x += entity.vel.x * step; entity.pos.y += entity.vel.y * step; break; case vroomentity.dynamic: // dynamic stuff break; } } } // loop through entities , detect collisions. resolve collisions detected. (var entityid in vroom.entitylist) { var entity = vroom.entitylist[entityid]; if (entity.physicsenabled && entity.entitytype !== vroomentity.static) { (var targetid in vroom.entitylist) { if (targetid !== entityid) { var target = vroom.entitylist[targetid]; if (target.physicsenabled) { // check if current entity , target colliding if (vroom.collideentity(entity, target)) { switch (entity.collisiontype) { case vroomentity.displace: vroom.resolvetesttest(entity, target); break; } } } } } } } },
here code actual collision detection. seems work alright.
collideentity: function(entity, target) { if (entity.getbottom() < target.gettop() || entity.gettop() > target.getbottom() || entity.getright() < target.getleft() || entity.getleft() > target.getright()) { return false; } return true; },
here problems start pop up. want entity "pushed" out of target entity , have velocity set 0. works fine long both entity , target perfect squares. if let's entity (the player figure in gif) rectangle, collision "slipp" when colliding longest sides (the x axis) target (the square). if swap player dimensions short , wide, same problem appears y axis instead.
resolvetesttest: function(entity, target) { var normalizedx = (target.getmidx() - entity.getmidx()); var normalizedy = (target.getmidy() - entity.getmidy()); var absolutenormalizedx = math.abs(normalizedx); var absolutenormalizedy = math.abs(normalizedy); console.log(absolutenormalizedx, absolutenormalizedy); // collision comming left or right if (absolutenormalizedx > absolutenormalizedy) { if (normalizedx < 0) { entity.pos.x = target.getright(); } else { entity.pos.x = target.getleft() - entity.dim.width; } // set velocity 0 entity.vel.x = 0; // collision comming top or bottom } else { if (normalizedy < 0) { entity.pos.y = target.getbottom(); } else { entity.pos.y = target.gettop() - entity.dim.height; } // set velocity 0 entity.vel.y = 0; } },
collision on y axis works these shapes
collision on x axis slips these shapes
what can fix slipping problem? have been bashing head against last 5 days, immensely grateful if 1 push me in right direction!
thank :)
-- edit: --
the slipping happens if moving in 1 direction along left or right side.
-- edit 2 working code: -- see answer below example of working code!
the important logical error have made line:
if (absolutenormalizedx > absolutenormalizedy) {
this works if both entities square.
consider near-extremal case x-slipping example: if touch @ corner:
although diagram little exaggerated, can see absolutenormalizedx < absolutenormalizedy
in case - implementation move on resolve vertical collision instead of expected horizontal one.
another error set corresponding velocity component 0 regardless of side collision on: must 0 component if in opposite direction collision normal, or won't able move away surface.
a way overcome record collided face(s) when collision detection:
collideentity: function(entity, target) { // adjust parameter liking var eps = 1e-3; // no collision var coll_x = entity.getright() > target.getleft() && entity.getleft() < target.getright(); var coll_y = entity.getbottom() > target.gettop() && entity.gettop() < target.getbottom(); if (!(coll_x && coll_y)) return 0; // calculate bias flag in each direction var bias_x = entity.targetx() < target.getmidx(); var bias_y = entity.targety() < target.getmidy(); // calculate penetration depths in each direction var pen_x = bias_x ? (entity.getright() - target.getleft()) : (target.getright() - entity.getleft()); var pen_y = bias_y ? (entity.getbottom() - target.getup()) : (target.getbottom() - entity.getup()); var diff = pen_x - pen_y; // x penetration greater if (diff > eps) return (1 << (bias_y ? 0 : 1)); // y pentration greater else if (diff < -eps) return (1 << (bias_x ? 2 : 3)); // both penetrations approximately equal -> treat corner collision else return (1 << (bias_y ? 0 : 1)) | (1 << (bias_x ? 2 : 3)); }, updatephysics: function(step) { // ... // pass collision flag resolver function var result = vroom.collideentity(entity, target); if (result > 0) { switch (entity.collisiontype) { case vroomentity.displace: vroom.resolvetesttest(entity, target, result); break; } } // ... }
using bit flag instead of boolean array efficiency. resolver function can re-written as:
resolvetesttest: function(entity, target, flags) { if (!!(flags & (1 << 0))) { // collision upper surface entity.pos.y = target.gettop() - entity.dim.height; if (entity.vel.y > 0) // travelling downwards entity.vel.y = 0; } else if (!!(flags & (1 << 1))) { // collision lower surface entity.pos.y = target.getbottom(); if (entity.vel.y < 0) // travelling upwards entity.vel.y = 0; } if (!!(flags & (1 << 2))) { // collision left surface entity.pos.x = target.getleft() - entity.dim.width; if (entity.vel.x > 0) // travelling rightwards entity.vel.x = 0; } else if (!!(flags & (1 << 3))) { // collision right surface entity.pos.x = target.getright(); if (entity.vel.x < 0) // travelling leftwards entity.vel.x = 0; } },
note unlike original code, above allows corners collide - i.e. velocities , positions resolved along both axes.
Comments
Post a Comment