Emanuele Feronato added textures and some environment to Sokoban game prototype made with Flare3D.
I reproduced this prototype on Alternativa3D engine. And here you can find it:
Generally this port is bit different from Emanuele’s version. I can’t find information about feature like SkyBox in Alternativa3D, so this feature coded manually. Another difference is camera behavior. Emanuele binds camera to object and during rotations player object stand still and only camera fly around by its orbit. In Alternativa3d version object rotates too, this allow us to face player object it to it’s current direction. Camera is not binded to the object directly it is binded to object container and we can manipulate with player object in container, add animations, rotations etc. But both variants are good I think.
Here is 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(); } } }
Here you can download sources.