Do you like my tutorials?

Then consider supporting me on Ko-fi

Talking about Sokoban game, 3D, Actionscript 3, Flash, Game development and Users contributions.

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?

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.