Talking about Sokoban game, 3D, Actionscript 3, Flash and Game development.
This post should end the Sokoban game prototype made with Flare3D started here and continued here.
The changes in this version are:
* Added some textures
* Fixed multiple keys pressed bug
* Changed player to a rolling sphere
* Added background environment
This is the source code:
package {
// required flash classes
import flash.geom.Vector3D;
import flash.display.Sprite;
import flash.events.Event;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.filters.*;
// required flare3d classes
import flare.basic.*;
import flare.materials.*;
import flare.primitives.*;
import flare.system.*;
import flare.utils.*;
import flare.core.*;
public class Main extends Sprite {
private const CUBESIZE:Number=10;
// 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;
// flare3d variables
private var scene:Scene3D;// scene3D is the canvas of the flare3d environment
private var player:Sphere;// sphere primitive representing the player
private var cameraTarget:Sphere;// sphere to be used as camera target
private var movingCrate:Cube;// cube primitive representing the moving crate
// bitmap datas
private var crateBitmap:BitmapData=new BitmapData(256,256);
private var crateTopBitmap:BitmapData=new BitmapData(256,256);
private var crateTopGoalBitmap:BitmapData=new BitmapData(256,256);
private var floorBitmap:BitmapData=new BitmapData(256,256);
private var wallBitmap:BitmapData=new BitmapData(256,256);
private var goalBitmap:BitmapData=new BitmapData(256,256);
private var playerBitmap:BitmapData=new BitmapData(512,512);
private var backBitmap:BitmapData=new BitmapData(512,512);
private var backBottomBitmap:BitmapData=new BitmapData(512,512);
// textures
private var crateTexture:Texture3D;
private var crateTopTexture:Texture3D;
private var crateTopGoalTexture:Texture3D;
private var floorTexture:Texture3D=new Texture3D("",floorBitmap);
private var backTexture:Texture3D=new Texture3D("",backBitmap);
private var wallTexture:Texture3D;
private var playerTexture:Texture3D;
private var goalTexture:Texture3D=new Texture3D("",goalBitmap);
// materials
private var floorMaterial:TextureMaterial=new TextureMaterial("",floorTexture);
private var goalMaterial:TextureMaterial=new TextureMaterial("",goalTexture);
// multi materials
private var wallMaterial:MultiMaterial = new MultiMaterial();
private var crateMaterial:MultiMaterial = new MultiMaterial();
private var crateTopMaterial:MultiMaterial = new MultiMaterial();
private var crateTopGoalMaterial:MultiMaterial = new MultiMaterial();
private var playerMaterial:MultiMaterial = new MultiMaterial();
public function Main() {
floorBitmap.draw(new floorPic(256,256));
goalBitmap.draw(new goalPic(256,256));
backBitmap.draw(new backPic(512,512));
backBottomBitmap.draw(new backBottomPic(512,512));
//
wallBitmap.draw(new wallPic(256,256));
wallTexture=new Texture3D("",wallBitmap);
wallMaterial.setTextureChannel(0,wallTexture);
wallMaterial.setShadedColorChannel(1);
wallMaterial.setBlendMode(BlendMode.MULTIPLY);
//
crateBitmap.draw(new cratePic(256,256));
crateTexture=new Texture3D("",crateBitmap);
crateMaterial.setTextureChannel(0,crateTexture);
crateMaterial.setShadedColorChannel(1);
crateMaterial.setBlendMode(BlendMode.MULTIPLY);
//
crateTopBitmap.draw(new crateTopPic(256,256));
crateTopTexture=new Texture3D("",crateTopBitmap);
crateTopMaterial.setTextureChannel(0,crateTopTexture);
crateTopMaterial.setShadedColorChannel(1);
crateTopMaterial.setBlendMode(BlendMode.MULTIPLY);
//
crateTopGoalBitmap.draw(new crateTopGoalPic(256,256));
crateTopGoalTexture=new Texture3D("",crateTopGoalBitmap);
crateTopGoalMaterial.setTextureChannel(0,crateTopGoalTexture);
crateTopGoalMaterial.setShadedColorChannel(1);
crateTopGoalMaterial.setBlendMode(BlendMode.MULTIPLY);
//
playerBitmap.draw(new playerPic(512,512));
playerTexture=new Texture3D("",playerBitmap);
playerMaterial.setTextureChannel(0,playerTexture);
playerMaterial.setShadedColorChannel(1);
playerMaterial.setBlendMode(BlendMode.MULTIPLY);
// scene setup
scene=new Viewer3D(this,"",0,0);
// background evironment
var front:Texture3D=new Texture3D("",backBitmap);
front.filename="front.png";
var back:Texture3D=new Texture3D("",backBitmap);
back.filename="back.png";
var left:Texture3D=new Texture3D("",backBitmap);
left.filename="left.png";
var right:Texture3D=new Texture3D("",backBitmap);
right.filename="right.png";
var top:Texture3D=new Texture3D("",backBitmap);
top.filename="top.png";
var bottom:Texture3D=new Texture3D("",backBottomBitmap);
bottom.filename="bottom.png";
Resources3D.addTexture(front);
Resources3D.addTexture(back);
Resources3D.addTexture(left);
Resources3D.addTexture(right);
Resources3D.addTexture(top);
Resources3D.addTexture(bottom);
var sky:SkyBox=new SkyBox("","","png",50);
scene.addChild(sky);
// level construction
var cube:Cube;
for (var i:uint=0; i<6; i++) {
for (var j:uint=0; j<8; j++) {
switch (levels[i][j]) {
case 0 :
cube=new Cube("",CUBESIZE,CUBESIZE/2,CUBESIZE,1,floorMaterial);
cube.setPosition(CUBESIZE*j,0,CUBESIZE*i);
scene.addChild(cube);
break;
case 1 :
cube=new Cube("",CUBESIZE,CUBESIZE/2,CUBESIZE,1,floorMaterial);
cube.setPosition(CUBESIZE*j,0,CUBESIZE*i);
scene.addChild(cube);
cube=new Cube("",CUBESIZE,CUBESIZE,CUBESIZE,1,wallMaterial);
cube.setPosition(CUBESIZE*j,CUBESIZE*3/4,CUBESIZE*i);
scene.addChild(cube);
break;
case 2 :
cube=new Cube("",CUBESIZE,CUBESIZE/2,CUBESIZE,1,goalMaterial);
cube.setPosition(CUBESIZE*j,0,CUBESIZE*i);
scene.addChild(cube);
break;
case 3 :
cube=new Cube("",CUBESIZE,CUBESIZE/2,CUBESIZE,1,floorMaterial);
cube.setPosition(CUBESIZE*j,0,CUBESIZE*i);
scene.addChild(cube);
cube=new Cube("crate_"+i+"_"+j,CUBESIZE,CUBESIZE,CUBESIZE,1,crateMaterial);
cube.setPosition(CUBESIZE*j,CUBESIZE*3/4,CUBESIZE*i);
scene.addChild(cube);
// top of the crate
var polyVec:Vector.=cube.polys;
polyVec[4].material=crateTopMaterial;
polyVec[5].material=crateTopMaterial;
break;
case 4 :
cube=new Cube("",CUBESIZE,CUBESIZE/2,CUBESIZE,1,floorMaterial);
cube.setPosition(CUBESIZE*j,0,CUBESIZE*i);
scene.addChild(cube);
player=new Sphere("player",CUBESIZE/2,24,playerMaterial);
player.setPosition(CUBESIZE*j,CUBESIZE*3/4,CUBESIZE*i);
scene.addChild(player);
cameraTarget=new Sphere("cameraTarget",1,12,crateTopMaterial);
cameraTarget.setPosition(CUBESIZE*j,CUBESIZE*3/4,CUBESIZE*i);
scene.addChild(cameraTarget);
playerCol=j;
playerRow=i;
break;
}
}
}
// listener to handle the 3D engine
scene.addEventListener(Scene3D.UPDATE_EVENT,updateEvent);
}
private function updateEvent(e:Event):void {
var currentRotation:Number=0;
// 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;
dCol=0;
break;
case 180 :
dRow=0;
dCol=1;
break;
case 270 :
dRow=-1;
dCol=0;
break;
}
if (playerRotation==0&&playerMovement==0) {
// look how does flare3D listens for key pressed
if (Input3D.keyDown(Input3D.RIGHT)) {
playerRotation=9;
playerAngle+=90;
} else {
if (Input3D.keyDown(Input3D.LEFT)) {
playerRotation=-9;
playerAngle-=90;
} else {
if (Input3D.keyDown(Input3D.UP)) {
movingCrate=null;
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=scene.getChildByName("crate_"+(playerRow+dRow)+"_"+(playerCol+dCol))as Cube;
playerMovement=- CUBESIZE/10;
}
}
}
}
}
}
if (playerAngle<0) {
playerAngle+=360;
}
if (playerAngle==360) {
playerAngle=0;
}
} else {
if (playerRotation) {
cameraTarget.rotateY(playerRotation);
if (Math.abs(Math.round(cameraTarget.getRotation().y))%90==0) {
playerRotation=0;
}
}
if (playerMovement) {
var vectorAngle:Vector3D=player.getRotation();
cameraTarget.translateX(playerMovement);
switch (playerAngle) {
case 0 :
player.x+=playerMovement;
if (Math.abs(Math.round(vectorAngle.x))==180) {
player.rotateZ(-18);
} else {
player.rotateZ(18);
}
if (movingCrate) {
movingCrate.translateX(playerMovement);
}
break;
case 90 :
player.z-=playerMovement;
if (Math.abs(Math.round(vectorAngle.z))==180) {
player.rotateX(-18);
} else {
player.rotateX(18);
}
if (movingCrate) {
movingCrate.translateZ(-playerMovement);
}
break;
case 180 :
player.x-=playerMovement;
if (Math.abs(Math.round(vectorAngle.x))==180) {
player.rotateZ(18);
} else {
player.rotateZ(-18);
}
if (movingCrate) {
movingCrate.translateX(-playerMovement);
}
break;
case 270 :
player.z+=playerMovement;
if (Math.abs(Math.round(vectorAngle.z))==180) {
player.rotateX(18);
} else {
player.rotateX(-18);
}
if (movingCrate) {
movingCrate.translateZ(playerMovement);
}
break;
}
// we need this to know if the player stopped on the destination tile
if ((playerAngle%180==0&&(Math.round(player.getPosition().x*10)/10)%CUBESIZE==0)||(playerAngle%180!=0&&(Math.round(player.getPosition().z*10)/10)%CUBESIZE==0)) {
playerMovement=0;
levels[playerRow+dRow][playerCol+dCol]+=4;
levels[playerRow][playerCol]-=4;
if (movingCrate) {
var polyVec:Vector.=movingCrate.polys;
levels[playerRow+2*dRow][playerCol+2*dCol]+=3;
if (levels[playerRow+2*dRow][playerCol+2*dCol]==5) {
polyVec[4].material=crateTopGoalMaterial;
polyVec[5].material=crateTopGoalMaterial;
} else {
polyVec[4].material=crateTopMaterial;
polyVec[5].material=crateTopMaterial;
}
levels[playerRow+dRow][playerCol+dCol]-=3;
movingCrate.name="crate_"+(playerRow+2*dRow)+"_"+(playerCol+2*dCol);
}
playerCol+=dCol;
playerRow+=dRow;
}
}
}
// camera management
Pivot3DUtils.setPositionWithReference(scene.camera,CUBESIZE*3,CUBESIZE*6,0,cameraTarget,0.1);
Pivot3DUtils.lookAtWithReference(scene.camera,-CUBESIZE*2,0,0,cameraTarget);
}
}
}
And this is the result:
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.