-->

AS3 Stop character from moving through walls

2019-07-15 06:31发布

问题:

I want to stop the movieclips movement when it hits a wall (another movieclip). The example below works, but after the collision the movieclip 'blocks' all movement to the left... My question to you is, is this a good way and why isn't it working well?

There will be something wrong in this code, but i'm learning. For now the example with the leftArrow key;

variables to check the key, if it's hitting the walls and if it's moving or not:

var leftArrow:Boolean;
var speed:int = 10;
var hitting:Boolean;
var ismoving:Boolean;

event listeners for the keys/movement and detecting collision:

stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP, keyReleased);
stage.addEventListener(Event.ENTER_FRAME, walking);
stage.addEventListener(Event.ENTER_FRAME, detectHit);

detecting collision function:

function detectHit(e:Event) :void
{
   if(char.hitTestObject(bounds))
   {
       hitting = true;
   }
}

function to the left arrow key:

function keyPressed(event:KeyboardEvent):void
{

    if (event.keyCode == Keyboard.LEFT)
    {
       leftArrow = true;
    }

}

function keyReleased(event:KeyboardEvent):void 
{

    if (event.keyCode == Keyboard.LEFT) 
    {
        leftArrow = false;
    }

}

And the reason it's not working is probably here, but I don't understand why not:

function walking(event:Event):void {
    if (rightArrow) {
        char.x += speed;    
    }

    if (leftArrow && ! hitting) {
        char.x -= speed;
    }
    else
    {
        ismoving = false
    }

回答1:

if (leftArrow && ! hitting)

char will move if hitting is false. When char.hitTestObject(bounds) is true you are setting hitting to true. You are not setting hitting again to false anywhere. That's why once left wall is hit it stops left movement permanently. You need to figure out suitable condition to set hitting to false again.

Adding an else branch in detectHit should solve the problem.

function detectHit(e:Event):void {
   if(char.hitTestObject(bounds))
   {
       hitting = true;
   } else { 
       hitting = false;    // add this
   }
}


回答2:

Allthough Taskinoor's method should work, I would suggest another way to do your hittests.

Since you probably are creating a game (character and bounds), you will have more than one bound. In that case, I would strongly suggest bitmap-hittesting. This way, you can create all your bounds in one movieclip and test for a hit.

I will explain this by using the example of a maze. The maze would then be some lines in a movieclip, randomly put together. If you use HitTestObject and you aren't hitting one of the lines, but your character is over the movieclip, hitTestObject will return true, even though you are not hitting a wall. By using bitmapHitTesting, you can overcome this problem (BitmapHitTest takes transparant pixels into account, whereas hitTestObject does not).

Below you can find an example of how to do bitmapHitTesting. Creating the bitmaps in this function is not necesarry if they do not change shape. In that case, I would suggest placing the code for the bitmapdata in a added_to_stage-method.

private var _previousX:int;
private var _previousY:int;
private var _bmpd:BitmapData ;
private var _physicalBitmapData:BitmapData;

private function walkAround(e:Event):void
{
    var _xTo:int = //Calculate x-to-position;
    var _yTo:int = //Calculate y-to-position;

    //If your character doesn't change shape, you don't have to recalculate this bitmapData over and over. 
    //I suggest placing it in a ADDED_TO_STAGE-Event in that case.
    _bmpd = new BitmapData(char.width, char.height, true, 0);
    _bmpd.draw(char);   

    //If your bounds are static, you don't have to recalculate this bitmapData over and over. 
    //I suggest placing it in a ADDED_TO_STAGE-Event in that case.
    _physicalBitmapData = new BitmapData(bounds.width, bounds.height, true, 0);
    _bmpd.draw(bounds);

    //The below line is the actual hittest
    if(_physicalBitmapData.hitTest(new Point(0, 0), 255, _bmpd, new Point(char.x, char.y), 255))
    {
        char.x = _previousX;
        char.y = _previousY;
    }
    else
    {           
        char.x = _xTo;
        char.y = _yTo;
    }

    _previousX = char.x;
    _previousY = char.y;
}


回答3:

Look at my hint,

function loop(Event)
{    
    if(isHit==false)
    {
       if(isRight==true){head_mc.x+=1}
       if(isLeft==true){head_mc.x-=1}
       if(isUp==true){head_mc.y-=1}
       if(isDown==true){head_mc.y+=1}
    }

    if(head_mc.hitTestObject(build_mc))
    {
        isHit=true;

        if(isRight==true){head_mc.x-=1}
        if(isLeft==true){head_mc.x+=1}
        if(isUp==true){head_mc.y+=1}
        if(isDown==true){head_mc.y-=1}
    }
    else
    {
        isHit=false;
    }                                        
 }

I use step back to opposite direction instead.