Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411520 Posts in 69380 Topics- by 58436 Members - Latest Member: GlitchyPSI

May 01, 2024, 03:40:25 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Sigh Collision detection
Pages: [1]
Print
Author Topic: Sigh Collision detection  (Read 1638 times)
desdinova
Level 0
***



View Profile
« on: December 04, 2009, 10:21:43 PM »

Ok so I have a Narrow Phase collision Detection rutine I'm have problems with.  Basically I'm using aabb to aabb collision detection.  I have a grid for broad phase, which I am assuming for now isn't causing the problem until I make sure narrow phase isn't wrong. Basically what happens is that I have a top down game.  The cells are either "right side walls"(at cell.x+cell.width) or left hand walls or corners (which have 2 bounding boxes)
The walls on the left of a cell or at the top can't be touched.  The player stops on the outside of a grid cell, as if I was calling the x+width of the Cell rather then the bounding box.

Here is the collision detection I'm using:
Code:
public function npcd(mover:MovieClip, index:int)
{
var hitX:Boolean = false
var hitY:Boolean = false
if (!lvlTiles[index].isEmpty)
{
                               
hitX = false  //stop dx?
hitY = false //stop dy?
                                        //players min/max x/y's
var hLeft:Number = h.x - (h.width / 2)+h.dx
var hRight:Number = h.x + (h.width / 2)+h.dx
var hTop:Number = h.y - (h.height / 2)+h.dy
var hBottom:Number = h.y+(h.height/2)+h.dy
                                       
                                        //The Grid cell to check (taken from Broad phase)
                                        //LvlTiles[] is the level data
var cellToCheck:Entity = lvlTiles[index]

                                        //Bounding boxes of the cells contents
                                        //I've checked my entity class which is what    cellToChek holds
var cellLeft:Number = cellToCheck.lines[0].x1
var cellRight:Number = cellToCheck.lines[0].x2
var cellTop:Number = cellToCheck.lines[0].y1
var cellBottom:Number = cellToCheck.lines[0].y2

                                        //Here are the calculations for seperatis the axis
var sumEventsX:Number = (hRight - hLeft) + (cellRight - cellLeft)
var sumEventsY:Number = (hBottom -hTop) + (cellBottom - cellTop)
//ctoC refers to center to center
var CtoCX:Number = (cellLeft + cellRight)-(hLeft+hRight)
var CtoCY:Number = (cellTop + cellBottom)-(hTop + hBottom)



if ((CtoCX <= sumEventsX) &&(CtoCY <= sumEventsY))
{
hitX = true
hitY = true

}

if (hitX == true)
{
h.dx = -1*h.dx
}
if (hitY == true )
{
h.dy= -1*h.dy
}
}
}
I know the collision reaction is shit but I'll fix that when I get the detection right
So is there an error that you can find here or could it be hidden somewhere else in my code?

Hopefully my code and explanations are not too convoluted for you to understand
Logged
Zaphos
Guest
« Reply #1 on: December 04, 2009, 11:01:05 PM »

CtoCX and CtoCY are distances right?  Shouldn't you take their absolute value?
Logged
Tycho Brahe
Level 10
*****

λx.x


View Profile
« Reply #2 on: December 05, 2009, 10:30:11 AM »

The cells are either "right side walls"(at cell.x+cell.width) or left hand walls or corners (which have 2 bounding boxes)
You say the "right side walls" are the x+width, is your x on the left wall, or in the centre in which case you should probably be doing x+width/2. this might be a reason, apart from that I'm not really sure...
Logged
desdinova
Level 0
***



View Profile
« Reply #3 on: December 05, 2009, 08:23:39 PM »

Thanks for the replies, much appreciated.  I got home after sunrise this morning and probably am not in a great state for working stuff out (too much of  Beer! ) but I'll give it a look tomorrow and see if I can reach
Logged
desdinova
Level 0
***



View Profile
« Reply #4 on: December 10, 2009, 11:01:24 PM »

Nah I must have fucked something up somewhere in my code but its all such a mess.  I've been meaning to get back into this sooner but I've been so incredibly burnt out.

I reckon I'll scrap the entire collision detection section and begin again with a new plan. I'll try to keep it neat this time.  In the mean time if anyone has any good links on Collision detection/response, it might come in handy (especially because right now my method of dealing with response is incredibly lazy... But then again, that's just my style Gentleman )
Logged
zamp
Level 1
*



View Profile
« Reply #5 on: December 13, 2009, 05:26:05 AM »

Here's my wall method.
Every wall in the map is a line segment, load up some walls and do some crazy math and boom. You've got a collision detection thats quite fast.
The walls arent sorted so if theres alot of walls the collision checks might get sluggish.
One way to speed up the collisions would be to skip far away walls alltogether.

Best part of this method is that the pathfinder's speed doesn't change with the distance of the start and end positions, unlike with A* and other tile based pathfinders

ps.
Sucks to not have a good save function in flash.
Trace + copy and paste ftw! Tongue

Here's a picture of the pathfinder
Green line is a test path
Blue line is an optimized path


Line.as
Code:
package  
{
import flash.geom.Point;
/**
* ...
* @author zamp
*/
public class Line
{
public var start:Point, end:Point;
public function Line(start:Point, end:Point)
{
this.start = start;
this.end = end;
}

public function hitTestPoint(point:Point, radius:Number):Boolean
{
var dist:Number = getClosestPointDistance(point);
if (dist < radius)
return true;
return false;
}

public function getClosestPointDistance(point:Point):Number
{
var closestPoint:Point = getClosestPoint(point);
return zGeom.length(closestPoint,point);
}

// returns the closest point at line segment (not infinite) from point
public function getClosestPoint(point:Point):Point
{
var A:Number = point.x - start.x;
var B:Number = point.y - start.y;
var C:Number = end.x - start.x;
var D:Number = end.y - start.y;
 
var dot:Number = A * C + B * D;
var len_sq:Number = C * C + D * D;
var param:Number = dot / len_sq;

var xx:Number,yy:Number;

if(param < 0)
{
xx = start.x;
yy = start.y;
}
else if(param > 1)
{
xx = end.x;
yy = end.y;
}
else
{
xx = start.x + param * C;
yy = start.y + param * D;
}

return new Point(xx, yy);
}

// intersects with line and returns a point if any intersection is found, null if not
public function getLineIntersectPoint(line:Line):Point
{
var nx:Number, ny:Number, dn:Number;
var x4_x3:Number = line.end.x - line.start.x;
var y4_y3:Number = line.end.y - line.start.y;
var x2_x1:Number = end.x - start.x;
var y2_y1:Number = end.y - start.y;
var x1_x3:Number = start.x - line.start.x;
var y1_y3:Number = start.y - line.start.y;

nx = x4_x3 * y1_y3 - y4_y3 * x1_x3;
ny = x2_x1 * y1_y3 - y2_y1 * x1_x3;
dn = y4_y3 * x2_x1 - x4_x3 * y2_y1;

nx /= dn;
ny /= dn;

// has intersection
if(nx>= 0 && nx <= 1 && ny>= 0 && ny <= 1){
ny = start.y + nx * y2_y1;
nx = start.x + nx * x2_x1;
return new Point(nx, ny);
}else{
// no intersection
return null;
}
}

}
}

Walls.as
Code:
package  
{
import flash.display.LineScaleMode;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.xml.XMLDocument;
import flash.xml.XMLNode;
import flash.net.URLLoader;
import flash.net.URLRequest;
/**
* ...
* @author zamp
*/
public class Walls extends Sprite
{
public var walls:Array = new Array();
private var fileName:String;

public function Walls()
{

}

public function addWall(start:Point, end:Point):void
{
var line:Shape = new Shape();
line.graphics.lineStyle(1, 0xFF0000);
line.graphics.moveTo(start.x, start.y);
line.graphics.lineTo(end.x, end.y);
addChild(line);

walls.push(new Line(start, end));
}

public function undoLastWall():void
{
removeChild(getChildAt(numChildren-1));
walls.pop();
}

public function getClosestIntersectPointAndLine(line:Line):Array
{
var shortestLength:Number = Number.MAX_VALUE;
var shortestLine:Line = null;
var shortestPoint:Point = null;

for (var i:String in walls)
{
var point:Point = walls[i].getLineIntersectPoint(line);
if (point != null) // intersects
{
// distance from line start location
var length:Number = zGeom.length(new Point(line.start.x, line.start.y), point);
if (length < shortestLength)
{
shortestLine = walls[i];
shortestPoint = point;
shortestLength = length;
}
}
}

if (shortestPoint != null)
return new Array(shortestPoint, shortestLine);
else
return null;
}

public function load(file:String):void
{
fileName = file;
var req:URLRequest = new URLRequest(Config.mapDir + file + ".mwx");
var loader:URLLoader = new URLLoader(req);
loader.addEventListener(Event.COMPLETE, loadComplete);
}

private function loadComplete(e:Event):void
{
var xml:XML = new XML(e.target.data);
xml.ignoreWhite = true;

for (var i:int = 0; i < xml.*.length(); i++)
{
if (xml.wall[i])
{
//trace("Loading layer:", xml.wall[i].@name);
var start:Point = new Point(xml.wall[i].@startx, xml.wall[i].@starty);
var end:Point = new Point(xml.wall[i].@endx, xml.wall[i].@endy);

addWall(start, end);
}
}
trace("Wall load complete");
}

public function save():void
{
var output:String = "<walls>\n";
for (var i:int = 0; i < walls.length; i++)
{
output += "\t<wall startx=\"" + walls[i].start.x + "\" starty=\"" + walls[i].start.y + "\"";
output += " endx=\"" + walls[i].end.x + "\" endy=\"" + walls[i].end.y + "\" />\n";
}
output += "</walls>";
trace(output);
}

}
}

zGeom.as (tool for geometry calculation)
Code:
package  
{
import flash.geom.Point;
/**
* ...
* @author zamp
* This class needs a better name ASAP :P
* A basic calculator for geometry (add functions when needed)
*/
public class zGeom
{

public function zGeom()
{

}

public static function length(p:Point, p2:Point):Number
{
return Math.sqrt(((p.x - p2.x) * (p.x - p2.x)) + ((p.y - p2.y) * (p.y - p2.y)));
}

}

}

Pathfinder.as (unfinished pathfinder, it sometimes finds the path)
Code:
package  
{
import flash.display.Shape;
import flash.display.Sprite;
import flash.geom.Point;
import flash.utils.getTimer;
/**
* ...
* @author zamp
*/
public class Pathfinder extends Sprite
{

public function Pathfinder()
{

}

private function drawPath(color:uint, start:Point, end:Point):void
{
/*var d:Shape = new Shape();
d.graphics.lineStyle(1, color);
d.graphics.moveTo(start.x, start.y);
d.graphics.lineTo(end.x, end.y);
addChild(d);
*/
}

public function findPath(start:Point, target:Point):Array
{
//trace("finding path");

var startTick:uint = getTimer();

var testline:Line = new Line(start, target);
var path:Array = new Array(); // add start location to the path
path.push(new Point(start.x, start.y));

var size:Number = 5;

var findingPath:Boolean = true;
var giveUp:uint = 0;
while (findingPath == true && giveUp < 10)
{
giveUp++;
// hit test with walls
var ht:Array = Main.getInstance().walls.getClosestIntersectPointAndLine(testline);

if (ht != null)
{
// hits
// get point X distance from wall
var hp:Point = ht[0]; // hit point in space
// make a vector from start to hitpoint
var vp:Point = new Point(testline.start.x - hp.x, testline.start.y - hp.y);
// normalise vp to X size
vp.normalize(size);
// subtract vp from hitpoint and add this point to path
hp = hp.add(vp);

addPathPoint(path, hp);

// go around the wall
// choose end point that is closer to target
var rwSp:Point = new Point(ht[1].start.x + vp.x, ht[1].start.y + vp.y); // relative wall start point
var rwEp:Point = new Point(ht[1].end.x + vp.x, ht[1].end.y + vp.y); // relative wall end point

var wStartLength:Number = zGeom.length(rwSp, target);
var wEndLength:Number = zGeom.length(rwEp, target);

wStartLength += zGeom.length(rwSp, hp);
wEndLength += zGeom.length(rwEp, hp);

var wcp:Point; // wall collide point
if (wStartLength < wEndLength)
{
wcp = ht[1].start;
// will the new line collide, if does go to the other end of the wall
if (Main.getInstance().walls.getClosestIntersectPointAndLine(new Line(hp, rwSp)) != null)
{
drawPath(0xAA0000, hp, rwSp);
wcp = ht[1].end;
}
} else {
wcp = ht[1].end;
// will the new line collide, if does go to the other end of the wall
if (Main.getInstance().walls.getClosestIntersectPointAndLine(new Line(hp, rwEp)) != null)
{
drawPath(0xAA0000, hp, rwEp);
wcp = ht[1].start;
}
}

// make new path point to wall end point - vp
var newEp:Point = new Point(wcp.x + vp.x, wcp.y + vp.y);
//var newTestline:Line = new Line(hp, newEp);
// add new end point to path
// if the new line collides, return null (there is no path)
if (Main.getInstance().walls.getClosestIntersectPointAndLine(new Line(hp, newEp)) != null)
{
//trace ("no path");
drawPath(0xAA0000, hp, wcp);
return null;
}

addPathPoint(path, newEp);

// make new path point to wall end + X size (go beyond the wall)
vp = new Point(wcp.x - hp.x, wcp.y - hp.y);
// normalize point to X size
vp.normalize(size);
// make new point to wall end + vp
var wEp:Point = new Point(wcp.x + vp.x, wcp.y + vp.y);

// test new line, if it collides return the path currently made
if (Main.getInstance().walls.getClosestIntersectPointAndLine(new Line(newEp, wEp)) != null)
{
// it collides, return path
optimizePath(path);
return path;
} else {
// add point to path list
addPathPoint(path, wEp);

// assign testline start point to wall end point
testline.start.x = wEp.x;
testline.start.y = wEp.y;
}
// repeat
} else {
// wont hit
// add last path point to list
addPathPoint(path, target);
//trace("time taken: ", getTimer() - startTick, "ms");

findingPath = false;

optimizePath(path);

return path;
}
}
// cant find path, thus return null
return null;
}

private function addPathPoint(path:Array, p:Point):void
{
// draw path from last path point to new point
var sp:Point = path[path.length - 1];
drawPath(0x00FF00, sp, p);
// push new path point to list
path.push(new Point(p.x, p.y));
}

private function optimizePath(path:Array):void
{
// TODO: change this to run until fully optimized
for (var i:int = 0; i < path.length; i++)
{
if (i < path.length - 2)
{
var atob:Number = zGeom.length(path[i], path[i + 1]);
var atoc:Number = zGeom.length(path[i], path[i + 2]);
var btoc:Number = zGeom.length(path[i + 1], path[i + 2]);

if (atoc < (atob + btoc)) // a to c is shorter
{
if (Main.getInstance().walls.getClosestIntersectPointAndLine(new Line(path[i], path[i + 2])) == null)
{
// wont collide
// can delete i+1 path point
path.splice(i + 1, 1);
i -= 1;
}
}
}
}

for (i = 0; i < path.length-1; i++)
drawPath(0x0000FF,path[i], path[i + 1]);
}

}

}
« Last Edit: December 13, 2009, 05:35:06 AM by zamp » Logged
desdinova
Level 0
***



View Profile
« Reply #6 on: December 14, 2009, 05:01:03 AM »

Thanks for posting all that Zamp, I've actually got a project which would be really suited to testing something like that out
Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic