Flash 3D Sokoban Prototype with Alternativa3D – final version
Talking about Sokoban game, 3D, Actionscript 3, Flash, Game development and Users contributions.
Do you like my tutorials?
Then consider supporting me on Ko-fi.
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:
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.