In my recent project, since the team has quite a few junior ActionScript devs, what was supposed to be pathfinding got cut down to straight-line collision. And the tragic part is, they still tell me the straight-line collision is broken.
Yesterday a guy was having the character do a per-pixel collision check after every step to decide whether it could walk. Let me repeat myself one more time: that’s both inaccurate and inefficient.
Today I’m posting this example to show the recommended way to do straight-line collision: first draw a line and hit-test it against the walkable/blocked model, and only once you’ve confirmed the destination is reachable do you move the character straight there (speed control can come later, it’s unrelated to this example).
Demo: the black area is unreachable, and the small red square is the object that needs to move.
Document Class :
package
{
import flash.display.SimpleButton;
import com.gskinner.motion.GTween;
import com.gskinner.motion.easing.Sine;
import org.mousebomb.display.RealHitTest;
import flash.display.Sprite;
import flash.events.MouseEvent;
/**
* @author Mousebomb (mousebomb@gmail.com)
* @date 2010-6-8
*/
public class HitTestMain extends Sprite
{
public var pz : Sprite ;
public var line : Sprite = new Sprite();
public var box : Sprite;
public var showBtn : SimpleButton;
private var boxTween : GTween;
private var curX : int = 300;
private var curY : int = 200;
public function HitTestMain()
{
addChild(line);
boxTween = new GTween(box,.3,null,{ease:Sine.easeOut});
curX=box.x;
curY=box.y;
line.graphics.lineStyle(3, 0xff0000);
line.graphics.moveTo(curX, curY);
stage.addEventListener(MouseEvent.CLICK, onClick);
showBtn.addEventListener(MouseEvent.CLICK, onShowClick);
}
private function onShowClick(event : MouseEvent) : void
{
if(pz.parent)
{
removeChild(pz);
removeChild(line);
}else{
addChildAt(pz,0);
addChildAt(line,1);
}
}
private function onClick(event : MouseEvent) : void
{
line.graphics.lineTo(event.stageX, event.stageY);
if(RealHitTest.hitTestObj(line, pz))
{
line.graphics.clear();
line.graphics.lineStyle(3, 0xff0000);
line.graphics.moveTo(curX, curY);
}
else
{
curX = event.stageX;
curY = event.stageY;
boxTween.setValues({x:curX,y:curY});
}
}
}
}
RealHitTest:
package org.mousebomb.display
{
import flash.display.BlendMode;
import flash.display.DisplayObjectContainer;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Rectangle;
/**
* Flash’s built-in HitTestObject is buggy; this class is the real HitTest
* @author Mousebomb (mousebomb@gmail.com)
* @date 2010-3-14
*/
public class RealHitTest
{
private static var bmp : Bitmap = new Bitmap();
/**
* The idea behind this method comes from ryan-liu
* @return Whether they overlap
* @param src The object to compare
* @param target The object to compare it against
* src and target must share the same container, or have their origin coordinates aligned
*/
public static function hitTestObj(src : DisplayObject,target : DisplayObject) : Boolean
{
//Get the region of the object being compared to offset the bitmap
var srcBound : Rectangle = src.getBounds(src.parent);
var bmd : BitmapData = new BitmapData(srcBound.width, srcBound.height, false, 0);
//bmp.bitmapData = bmd;
var mtx1 : Matrix = new Matrix();
var mtx2 : Matrix = new Matrix();
mtx1.rotate(src.rotation * Math.PI / 180);
mtx1.tx = src.x-srcBound.x;
mtx1.ty = src.y-srcBound.y;
mtx2.rotate(target.rotation * Math.PI / 180);
mtx2.tx = target.x - srcBound.x;
mtx2.ty = target.y - srcBound.y;
bmd.fillRect(bmd.rect, 0);
bmd.draw(src, mtx1, new ColorTransform(1, 1, 1, 1, -255, 255, 255, 255));
bmd.draw(target, mtx2, new ColorTransform(1, 1, 1, 1, 255, -255, -255, 255), BlendMode.DIFFERENCE);
var rect : Rectangle = bmd.getColorBoundsRect(0xffffff, 0xffffff);
//Collision detection done
//If all four Rectangle properties are 0, nothing was found
return (rect.x != 0 rect.y != 0 rect.width != 0 rect.height != 0);
}
public static function debug(s : DisplayObjectContainer):void
{
s.addChild(bmp);
bmp.x = 500;
bmp.y = 300;
}
}
}
Source files click here to download