Monday, December 24, 2012

Box2D in Flash and AS3.. explained for beginners Part 3

For Part one Click here
For Part two Click here

That's what we have done till now


If you have seen the demo in the first part of this series you can see that we can't shoot the player unless it's on the ground, also we added some more walls in the scene and so in this tutorial we will be creating more walls and limit the shooting to only on the ground.

Creating more walls

We would need also to create one more thing and that is to set the returned b2Body for the creation of the wall that represents the ground to a ground object, so let's define that ground first

private var ground:b2Body;

In the init method

public function init(e:Event = null):void
{  
    
    createWorld();
    
    player = createPlayer(40, 50, 50, 10);
    
    addEventListener(Event.ENTER_FRAME, update);
    stage.addEventListener(MouseEvent.CLICK, stageClicked);
    
    var th:uint = 10;
    // Vertical walls
    createWall(0, 0                    , th, stage.stageHeight);
    createWall(stage.stageWidth - th, 0, th, stage.stageHeight);
    // Horizontal walls
    createWall(0, 0                     , stage.stageWidth, th);
    ground = createWall(0, stage.stageHeight - th, stage.stageWidth, th);
    
    createWall(200, 300 , th, 100);
    createWall(350, 300 , th, 100);
    createWall(250, th , th, 200);

}

You can see how easy and fast it's to create walls using our createWall method.

Shooting player when only on the ground

What we would like to do here is when the mouse is clicked we need to check if the player is colliding with the ground or not to allow shooting.
Each b2Body has a list of bodies that it's in contact with, we will loop through them and check if one of them is the ground to execute the shooting algorithm.

for (var contact:b2ContactEdge = player.GetContactList(); contact; contact = contact.next) 
{
    if (contact.other == ground)
    {
        var force_x:Number = (mouseX / PM - player.GetPosition().x);
        var force_y:Number = (mouseY / PM - player.GetPosition().y);
        
        var force:b2Vec2 = new b2Vec2(force_x, force_y);
        force.Multiply( 2 / 3 );
        player.ApplyImpulse(force, player.GetPosition());
    }
}

That loop looks like the one in the second part of this series, GetContactList() method gets the first contactEdge in the list and saves the next contactEdge in the "next" variable.
We have checked if the ground is the same as the b2body object saved in the "other" variable, "other" means the other body that is in contact with the player.

Part 3 All come together

The final code

public class Main extends MovieClip
{
    
    public static var world:b2World;
    public static const PM:uint = 30;
    private var player:b2Body;
    private var ground:b2Body;
    
    public function Main() 
    {
        if (stage) init();
        else addEventListener(Event.ADDED_TO_STAGE, init);
    }
    
    public function init(e:Event = null):void
    {  
        
        createWorld();
        
        player = createPlayer(40, 50, 50, 10);
        
        addEventListener(Event.ENTER_FRAME, update);
        stage.addEventListener(MouseEvent.CLICK, stageClicked);
        
        var th:uint = 10;
        // Vertical walls
        createWall(0, 0                    , th, stage.stageHeight);
        createWall(stage.stageWidth - th, 0, th, stage.stageHeight);
        // Horizontal walls
        createWall(0, 0                     , stage.stageWidth, th);
        ground = createWall(0, stage.stageHeight - th, stage.stageWidth, th);
        
        createWall(200, 300 , th, 100);
        createWall(350, 300 , th, 100);
        createWall(250, th , th, 200);
    }
    
    public function stageClicked(e:MouseEvent):void
    {
        for (var contact:b2ContactEdge = player.GetContactList(); contact; contact = contact.next) 
        {
            if (contact.other == ground)
            {
                var impulse_x:Number = (mouseX / PM - player.GetPosition().x);
                var impulse_y:Number = (mouseY / PM - player.GetPosition().y);
                
                var impulse:b2Vec2 = new b2Vec2(impulse_x, impulse_y);
                impulse.Multiply( 2 / 3 );
                player.ApplyImpulse(impulse, player.GetPosition());
            }
        }
    }
    
    public function update(e:Event):void
    {
        world.Step(1 / 30, 10, 10);
        var temp:Sprite;
        for (var body:b2Body = world.GetBodyList(); body != null; body = body.GetNext())
        {
            if (body.GetUserData())
            {
                temp = body.GetUserData() as Sprite;
                temp.x = body.GetPosition().x * PM;
                temp.y = body.GetPosition().y * PM;
                temp.rotation = body.GetAngle() * (180 / Math.PI); // radians to degrees
            }
        }
    }
    
    public function createWorld():void
    {
        var gravity:b2Vec2 = new b2Vec2(0, 9.8);
        var sleep:Boolean = true;
        
        world = new b2World(gravity, sleep);
    }
    
    public function createWall(_x:Number, _y:Number, _width:Number, _height:Number):b2Body
    {    
        _x = _x + _width / 2;
        _y = _y + _height / 2;
        
        // Create Wall Sprite Using code
        var wallSprite:Sprite = new Sprite();
        wallSprite.graphics.beginFill(0xe7d7c0, 1);
        wallSprite.graphics.drawRect( -_width / 2, -_height / 2 , _width, _height);
        wallSprite.graphics.endFill();
        wallSprite.x = _x;
        wallSprite.y = _y;
        addChild(wallSprite);
        
        // Create body definition
        var bodyDef:b2BodyDef = new b2BodyDef();
        bodyDef.userData = wallSprite;
        bodyDef.type     = b2Body.b2_staticBody;
        bodyDef.position.Set( _x / PM, _y / PM);
        
        // Create body from world using bodyDef
        var body:b2Body = world.CreateBody(bodyDef);
        
        // Create shape
        var shape:b2PolygonShape = new b2PolygonShape();
        shape.SetAsBox((_width / 2) / PM, (_height / 2) / PM);
        
        // Create fixtureDef giving shape
        var fixtureDef:b2FixtureDef = new b2FixtureDef();
        fixtureDef.shape       = shape;
        fixtureDef.restitution = 0.2;
        fixtureDef.friction    = 1;
        fixtureDef.density     = 0.5;
        
        // Pass the fixtureDef to the createFixture method in the body object
        body.CreateFixture(fixtureDef);
        
        return body;
    }
    
    public function createPlayer(_x:Number , _y:Number, _width:Number, _height:Number):b2Body
    {
        // Create car sprite
        var carSprite:Sprite = new Sprite();
        carSprite.graphics.beginFill(0xafafaf, 1);
        carSprite.graphics.drawRect( -_width / 2, -_height / 2 , _width, _height);
        carSprite.graphics.endFill();
        carSprite.x = _x;
        carSprite.y = _y;
        addChild(carSprite);
        
        // Create body definition
        var bodyDef:b2BodyDef = new b2BodyDef();
        bodyDef.userData = carSprite;
        bodyDef.type     = b2Body.b2_dynamicBody;
        bodyDef.position.Set(_x / PM, _y / PM);
        
        // Create body from world using bodyDef
        var body:b2Body = world.CreateBody(bodyDef);
        
        // Create shape
        var shape:b2PolygonShape = new b2PolygonShape();
        shape.SetAsBox(_width / 2 / PM, _height / 2 / PM);
        
        // Create fixtureDef giving shape
        var fixtureDef:b2FixtureDef = new b2FixtureDef();
        fixtureDef.shape       = shape;
        fixtureDef.restitution = 0.2;
        fixtureDef.friction    = 1;
        fixtureDef.density     = 0.5;
        
        // Pass the fixtureDef to the createFixture method in the body object
        body.CreateFixture(fixtureDef);
        
        return body;
    }
    
}