<Emanuele Feronato/>
home

Flash 3D Sokoban Prototype with Alternativa3D – final version

Flare3D and Away3D versions of the Sokoban prototype have been published. What’s now?

Directly from InspiritGames we have an Alternativa3D port of the prototype.

It’s a great porting, although it looks a bit “flat” because Alternativa3D does not support light.

In the same blog you can find the Alternativa3D first prototype and even a version with a walking character.

Here it is the source code:

package
{
    import alternativa.Alternativa3D;
    import alternativa.engine3d.containers.*;
    import alternativa.engine3d.controllers.*;
    import alternativa.engine3d.core.Camera3D;
    import alternativa.engine3d.core.Clipping;
    import alternativa.engine3d.core.Debug;
    import alternativa.engine3d.core.MipMapping;
    import alternativa.engine3d.core.MouseEvent3D;
    import alternativa.engine3d.core.Object3D;
    import alternativa.engine3d.core.Object3DContainer;
    import alternativa.engine3d.core.Sorting;
    import alternativa.engine3d.core.View;
    import alternativa.engine3d.materials.FillMaterial;
    import alternativa.engine3d.materials.TextureMaterial;
    import alternativa.engine3d.objects.Sprite3D;
    import alternativa.engine3d.primitives.Box;
    import alternativa.engine3d.primitives.Plane;
    import alternativa.engine3d.primitives.Sphere;
     
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.filters.GlowFilter;
    import flash.geom.ColorTransform;
    import flash.geom.Vector3D;
    import flash.sampler.NewObjectSample;
    import flash.system.Capabilities;
    import flash.ui.Keyboard;
         
    [SWF(backgroundColor="#000000", frameRate="100", width="640", height="480")]
    public class alternativa3dSokoban extends Sprite
    {
        private const CUBESIZE:Number=10;
        //embeding textures images
        [Embed(source="resource/crateTextureImg.jpg")] static private const crateTextureImg:Class;
        [Embed(source="resource/floorTextureImg.png")] static private const floorTextureImg:Class;
        [Embed(source="resource/crateTopTextureImg.jpg")] static private const crateTopTextureImg:Class;
        [Embed(source="resource/crateTopGoalTextureImg.jpg")] static private const crateTopGoalTextureImg:Class;
        [Embed(source="resource/wallTextureImg.png")] static private const wallTextureImg:Class;
        [Embed(source="resource/goalTextureImg.jpg")] static private const goalTextureImg:Class;
        [Embed(source="resource/playerTextureImg.jpg")] static private const playerTextureImg:Class;
        [Embed(source="resource/backBitmapImg.jpg")] static private const backTextureImg:Class;
        [Embed(source="resource/backBottomBitmapImg.jpg")] static private const backBottomTextureImg:Class;
 
        // sokobal demo level and player position
        private var levels:Array=[[1,1,1,1,0,0,0,0],[1,0,0,1,1,1,1,1],[1,0,2,0,0,3,0,1],[1,0,3,0,0,2,4,1],[1,1,1,0,0,1,1,1],[0,0,1,1,1,1,0,0]];
        private var playerCol:uint;
        private var playerRow:uint;
        private var playerRotation:Number=0;
        private var playerAngle:Number=0;
        private var playerMovement:Number=0;
        private var dRow:int;
        private var dCol:int;
         
        // alternativa3d  engine variables
        private var camera:Camera3D;
        private var controller:SimpleObjectController;
        private var container:ConflictContainer;           
        private var frame:Sprite = new Sprite();
        public var player:Sphere;// Sphere primitive representing the player
        public var cplayer:SimpleObjectController; //controller for player object
        public var conplayer:Object3DContainer; //container for player
        private var movingCrate:Box;// cube primitive representing the moving crate    
             
        // textures    
        private var crateTexture:TextureMaterial = new TextureMaterial(new crateTextureImg().bitmapData);
        private var floorTexture:TextureMaterial = new TextureMaterial(new floorTextureImg().bitmapData);
        private var crateTopTexture:TextureMaterial = new TextureMaterial(new crateTopTextureImg().bitmapData);
        private var crateTopGoalTexture:TextureMaterial = new TextureMaterial(new crateTopGoalTextureImg().bitmapData);
        private var wallTexture:TextureMaterial = new TextureMaterial(new wallTextureImg().bitmapData);
        private var goalTexture:TextureMaterial = new TextureMaterial(new goalTextureImg().bitmapData);
        private var playerTexture:TextureMaterial = new TextureMaterial(new playerTextureImg().bitmapData);
        // SkyBox textures
        private var backTexture:TextureMaterial = new TextureMaterial(new backTextureImg().bitmapData);
        private var backBottomTexture:TextureMaterial = new TextureMaterial(new backBottomTextureImg().bitmapData);
                         
        public function alternativa3dSokoban()
        {          
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.quality = StageQuality.BEST;
             
            // Camera
            camera = new Camera3D();
            camera.view = new View(640, 480);
            addChild(camera.view);
                         
            // Camera controller
            controller = new SimpleObjectController(stage, camera, 200, 3);
             
            // Root object
            container = new ConflictContainer();
            container.resolveByAABB = true;
            container.resolveByOOBB = true;
             
            //Player controller
            conplayer = new Object3DContainer();
            cplayer = new SimpleObjectController(stage, player, 3);
             
//i am not shure about SkyBox in Alternativa and will prepare it manually
            var backBottom:Plane = new Plane(200*CUBESIZE/2,200*CUBESIZE/2);
            backBottom.setMaterialToAllFaces(backBottomTexture);
            backBottom.x = 0;
            backBottom.y = -100*CUBESIZE/2;
            backBottom.z = 0;
            backBottom.rotationX = 90*Math.PI/180;
            container.addChild(backBottom);
             
            var backLeft:Plane = new Plane(200*CUBESIZE/2,200*CUBESIZE/2);
            backLeft.setMaterialToAllFaces(backTexture);
            backLeft.x = 0;
            backLeft.y = 0;
            backLeft.z = 100*CUBESIZE/2;
            container.addChild(backLeft);
 
            var backRight:Plane = new Plane(200*CUBESIZE/2,200*CUBESIZE/2);
            backRight.setMaterialToAllFaces(backTexture);
            backRight.x = 0;
            backRight.y = 0;
            backRight.z = -100*CUBESIZE/2;
            container.addChild(backRight);
 
            var backFront:Plane = new Plane(200*CUBESIZE/2,200*CUBESIZE/2);
            backFront.setMaterialToAllFaces(backTexture);
            backFront.x = -100*CUBESIZE/2;
            backFront.y = 0;
            backFront.z = 0;
            backFront.rotationY = 90*Math.PI/180;
            container.addChild(backFront);
 
            var backBack:Plane = new Plane(200*CUBESIZE/2,200*CUBESIZE/2);
            backBack.setMaterialToAllFaces(backTexture);
            backBack.x = 100*CUBESIZE/2;
            backBack.y = 0;
            backBack.z = 0;
            backBack.rotationY = 90*Math.PI/180;
            container.addChild(backBack);
// end SkyBox
             
            var box:Box;
            /*
            [[1,1,1,1,0,0,0,0],
             [1,0,0,1,1,1,1,1],
             [1,0,2,0,0,3,0,1],
             [1,0,3,0,0,2,4,1],
             [1,1,1,0,0,1,1,1],
             [0,0,1,1,1,1,0,0]];
            */
            // level construction
            for (var i:uint=0; i<6; i++)
            {
                for (var j:uint=0; j<8; j++)
                {
                    switch (levels[i][j])
                    {
                        case 0 :
                            box = new Box(CUBESIZE,CUBESIZE/2,CUBESIZE,1,1);
                            box.setMaterialToAllFaces(floorTexture);
                            box.x = CUBESIZE*j;
                            box.y = 0;
                            box.z = CUBESIZE*i;
                            container.addChild(box);
                            break;
                        case 1 :
                            box = new Box(CUBESIZE,CUBESIZE/2,CUBESIZE,1);
                            box.setMaterialToAllFaces(floorTexture);
                            box.x = CUBESIZE*j;
                            box.y = 0;
                            box.z = CUBESIZE*i;
                            container.addChild(box);
                             
                            box = new Box(CUBESIZE,CUBESIZE,CUBESIZE,1);
                            box.setMaterialToAllFaces(wallTexture);
                            box.x = CUBESIZE*j;
                            box.y = CUBESIZE*3/4;
                            box.z = CUBESIZE*i;
                            container.addChild(box);
                            break;
                        case 2 :
                            box = new Box(CUBESIZE,CUBESIZE/2,CUBESIZE,1);
                            box.setMaterialToAllFaces(goalTexture);
                            box.x = CUBESIZE*j;
                            box.y = 0;
                            box.z = CUBESIZE*i;
                            container.addChild(box);
                            break;
                        case 3 :
                            box = new Box(CUBESIZE,CUBESIZE/2,CUBESIZE,1);
                            box.setMaterialToAllFaces(floorTexture);
                            box.x = CUBESIZE*j;
                            box.y = 0;
                            box.z = CUBESIZE*i;
                            container.addChild(box);
                            box = new Box(CUBESIZE,CUBESIZE,CUBESIZE,1);
                            box.name = "crate_"+i+"_"+j;
                            box.setMaterialToAllFaces(crateTexture);
                            box.x = CUBESIZE*j;
                            box.y = CUBESIZE*3/4;
                            box.z = CUBESIZE*i;
                            box.rotationX -= 90*Math.PI/180;
                            // top of the crate
                            box.faces[4].material=crateTopTexture;
                            box.faces[5].material=crateTopTexture;
 
                            container.addChild(box);
                            break;
                        case 4 :
                            box = new Box(CUBESIZE,CUBESIZE/2,CUBESIZE,1);
                            box.setMaterialToAllFaces(floorTexture);
                            box.x = CUBESIZE*j;
                            box.y = 0;
                            box.z = CUBESIZE*i;
                            container.addChild(box);
                                                         
                            player = new Sphere(CUBESIZE/2,16,16,false,playerTexture);
 
                            conplayer.addChild(player);
                            conplayer.visible = true;
                            conplayer.x = CUBESIZE*j;
                            conplayer.y = CUBESIZE*3/4;
                            conplayer.z = CUBESIZE*i;
                            conplayer.rotationX -= 90*Math.PI/180;
                            container.addChild(conplayer);
                            playerCol=j;
                            playerRow=i;
                            break;
                    }
                }
            }
 
            // Adding camera
            container.addChild(camera);
             
            // View frame
            addChild(frame);                                               
            onResize();    
            stage.addEventListener(Event.ENTER_FRAME, updateEvent);        
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDwn);      
            stage.addEventListener(Event.RESIZE, onResize);
        }
 
        private function onKeyDwn(e:KeyboardEvent):void
        {
            if (playerRotation==0&&playerMovement==0)
            {
                switch (e.keyCode)
                {
                    case Keyboard.LEFT :
                        playerRotation=+9;
                        playerAngle+=90;
                        break;
                    case Keyboard.RIGHT :
                        playerRotation=-9;
                        playerAngle-=90;
                        break;
                    case Keyboard.UP :
                        movingCrate=null;
                        playerAngle=Math.round(conplayer.rotationY*180/Math.PI)%360;
                        if (playerAngle<0)
                        {
                            playerAngle+=360;
                        }
                        // we have to determine the difference between current row and column
                        // and the new row and column according to player heading
                        switch (playerAngle)
                        {
                            case 0 :
                                dRow=0;
                                dCol=-1;
                                break;
                            case 90 :
                                //dRow=-1;
                                dRow=1;
                                dCol=0;
                                break;
                            case 180 :
                                dRow=0;
                                dCol=1;
                                break;
                            case 270 :
                                //dRow=1;
                                dRow=-1;
                                dCol=0;
                                break;
                        }
                        if (levels[playerRow+dRow][playerCol+dCol]==0||levels[playerRow+dRow][playerCol+dCol]==2)
                        {
                            // the player can move
                            playerMovement=-CUBESIZE/10;
                        }
                        else
                        {
                            if (levels[playerRow+dRow][playerCol+dCol]==3||levels[playerRow+dRow][playerCol+dCol]==5) {
                                if (levels[playerRow+2*dRow][playerCol+2*dCol]==0||levels[playerRow+2*dRow][playerCol+2*dCol]==2) {
                                    // the player can move and can push a crate
                                    movingCrate=container.getChildByName("crate_"+(playerRow+dRow)+"_"+(playerCol+dCol))as Box;
                                    playerMovement=-CUBESIZE/10;
                                }
                            }
                        }
                        break;
 
                }
            }
        }
 
 
        public function updateEvent(e:Event):void
        {      
                if (playerRotation)
                {
                    conplayer.rotationY+=playerRotation*Math.PI/180;
                     
                    if (Math.abs(Math.round(conplayer.rotationY*180/Math.PI))%90==0)
                    {
                        playerRotation=0;
                    }
                }
                 
                if (playerMovement)
                {                  
                    switch (playerAngle)
                    {
                    case 0 :
                        conplayer.x += playerMovement;
                        player.rotationY -= 18*Math.PI/180;
                    break;
                    case 90 :
                        conplayer.z += -playerMovement;
                        player.rotationY -= 18*Math.PI/180;
                    break;
                    case 180 :
                        conplayer.x += -playerMovement;
                        player.rotationY -= 18*Math.PI/180;
                        break;
                    case 270 :
                        conplayer.z += playerMovement;
                        player.rotationY -= 18*Math.PI/180;
                        break;
                    }
                     
                    if (movingCrate)
                    {
                        switch (playerAngle)
                        {
                            case 0 :
                                movingCrate.x += playerMovement;
                                break;
                            case 90 :
                                movingCrate.z += -playerMovement;
                                break;
                            case 180 :
                                movingCrate.x += -playerMovement;
                                break;
                            case 270 :
                                movingCrate.z += playerMovement;
                                break;
                        }
                    }
 
                    // we need this to know if the player stopped on the destination tile
                    if ((playerAngle%180==0&&(Math.round(conplayer.x*10)/10)%CUBESIZE==0)||(playerAngle%180!=0&&(Math.round(conplayer.z*10)/10)%CUBESIZE==0))
                    {
                        playerMovement=0;
                        levels[playerRow+dRow][playerCol+dCol]+=4;
                        levels[playerRow][playerCol]-=4;
                        if (movingCrate) {
                            levels[playerRow+2*dRow][playerCol+2*dCol]+=3;
                            if (levels[playerRow+2*dRow][playerCol+2*dCol]==5) {
                                // changing materials on the fly
                                movingCrate.setMaterialToAllFaces(crateTexture);
                                // top of the crate on goal
                                movingCrate.faces[4].material=crateTopGoalTexture;
                                movingCrate.faces[5].material=crateTopGoalTexture;                             
 
                            }
                            else
                            {
                                //movingCrate.setMaterialToAllFaces(crateMaterial);
                                movingCrate.setMaterialToAllFaces(crateTexture);
                                // top of the crate
                                movingCrate.faces[4].material=crateTopTexture;
                                movingCrate.faces[5].material=crateTopTexture;                             
                            }
                            levels[playerRow+dRow][playerCol+dCol]-=3;
                            movingCrate.name="crate_"+(playerRow+2*dRow)+"_"+(playerCol+2*dCol);
                        }
                        playerCol+=dCol;
                        playerRow+=dRow;
                    }
                }
 
                onEnterFrame();
        }          
 
        public function correct_camera_angles():void
        {
            //set camera position
            var r:Number = 10*CUBESIZE/3;          
            var a:Number = -conplayer.rotationY;
            var cx:Number = conplayer.x+Math.cos(a)*r;
            var cz:Number = conplayer.z+Math.sin(a)*r;
            var cy:Number = conplayer.y+r;         
            controller.setObjectPosXYZ(cx,cy,cz);
             
            //look at player box
            controller.lookAtXYZ(conplayer.x,conplayer.y,conplayer.z);
             
            //correct camera angles        
                var cprotY:Number;
                 
                cprotY=Math.round(conplayer.rotationY*180/Math.PI)%360;        
                if (cprotY<0)
                {
                    cprotY+=360;
                }
                if (cprotY>180)
                {
                    camera.rotationX = camera.rotationX + (90*Math.PI/180)*Math.sin((cprotY%180)*Math.PI/180);
                }                                      
                camera.rotationY = camera.rotationY+90*Math.PI/180-conplayer.rotationY;
        }
         
        public function onEnterFrame(e:Event = null):void
        {
            controller.update();
            correct_camera_angles();
            cplayer.update();
            camera.render();       
        }
         
 
        public function onResize(e:Event = null):void
        {
            //here you can add border size for view
            var pd:Number = 0;
            camera.view.width = stage.stageWidth - pd*2;
            camera.view.height = stage.stageHeight - pd*2;
            camera.view.x = pd;
            camera.view.y = pd;
             
            frame.graphics.clear();
            frame.graphics.beginFill(0x000000, 0);
            frame.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            //frame.graphics.lineStyle(0, 0x7F7F7F);
            frame.graphics.drawRect(pd, pd, camera.view.width, camera.view.height);
            frame.graphics.endFill();
        }
    }
}

and this is the result:

Download the source code.

Which engine would you use?

230 games covered