The last days I’ve experimented with jiglibflash, a 3D Physics Engine for Papervision3D. I’ve been inspired by a great example demo and made a little game out of it.
The result is Phykick.
You have to kick the boxes off the platform as fast as possible. That’s all.
The best thing about Phkick is the highscore list.
The game consists of a sinlge class only, here is the code (without highscore implementation):
package {
import __AS3__.vec.Vector;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.ui.Keyboard;
import flash.utils.getTimer;
import jiglib.geometry.*;
import jiglib.math.*;
import jiglib.physics.*;
import jiglib.physics.constraint.*;
import org.papervision3d.cameras.*;
import org.papervision3d.core.geom.*;
import org.papervision3d.core.math.*;
import org.papervision3d.core.proto.*;
import org.papervision3d.events.*;
import org.papervision3d.lights.PointLight3D;
import org.papervision3d.materials.*;
import org.papervision3d.materials.shadematerials.*;
import org.papervision3d.materials.utils.*;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.parsers.*;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.render.*;
import org.papervision3d.scenes.*;
import org.papervision3d.view.Viewport3D;
[SWF(width=800, height=600, backgroundColor=0x062037, frameRate=60)]
public class Phykick extends Sprite {
private var viewport:Viewport3D;
private var scene:Scene3D;
private var camera:Camera3D;
private var renderer:QuadrantRenderEngine;
private var light:PointLight3D;
private var playerBody:JSphere;
private var boxBodies:Vector.<JBox>;
private var keyRight:Boolean = false;
private var keyLeft:Boolean = false;
private var keyForward:Boolean = false;
private var keyReverse:Boolean = false;
private static var textFormat:TextFormat = new TextFormat("Arial", 12, 0xffffff);
private var timeField:TextField;
private var scoreField:TextField;
private var fallsField:TextField;
private var fpsField:TextField;
private var infoField:TextField;
private var endScoreField:TextField
private var aboutField:TextField;
private var score:int;
private var numFalls:int;
private var startTime:int;
private var lastStepTime:int;
private var inGame:Boolean;
private var fpsCount:int;
private var fpsValue:int;
public function Phykick() {
viewport = new Viewport3D(800, 600);
scene = new Scene3D();
this.addChild(viewport);
light = new PointLight3D(true, true);
light.x = 0;
light.y = 600;
light.z = -300;
// ground
var materia1s:MaterialsList = new MaterialsList();
materia1s.addMaterial(new FlatShadeMaterial(light, 0xff9900), "all");
var groundSkin:Cube = new Cube(materia1s);
scene.addChild(groundSkin);
var ground:JBox = new JBox(groundSkin, false);
PhysicsSystem.getInstance().AddBody(ground);
ground.Material.Restitution = 0.6;
ground.Material.StaticFriction = 0.9;
// player
var sphereSkin:Sphere = new Sphere(new FlatShadeMaterial(light, 0x990000), 20);
scene.addChild(sphereSkin);
playerBody = new JSphere(sphereSkin, true, 20);
playerBody.setMass(3);
playerBody.MoveTo(new JNumber3D(0, 500, 0), JMatrix3D.IDENTITY);
PhysicsSystem.getInstance().AddBody(playerBody);
//playerBody.Material.Restitution = 0.9;
// camera
var cameraTarget:DisplayObject3D = new DisplayObject3D();
cameraTarget.y = 250;
camera = new Camera3D();
camera.x = 0;
camera.y = 500;
camera.z = -400;
camera.lookAt(cameraTarget);
renderer = new QuadrantRenderEngine(1);
// boxes
boxBodies = new Vector.<JBox>();
for (var i:int = 0; i < 3; i++) boxBodies.push(createBox());
timeField = new TextField();
timeField.textColor = 0xffffff;
timeField.selectable = false;
timeField.defaultTextFormat = textFormat;
timeField.x = 3;
timeField.y = 3;
addChild(timeField);
scoreField = new TextField();
scoreField.textColor = 0xffffff;
scoreField.selectable = false;
scoreField.defaultTextFormat = textFormat;
scoreField.x = 3;
scoreField.y = 23;
addChild(scoreField);
fallsField = new TextField();
fallsField.textColor = 0xffffff;
fallsField.selectable = false;
fallsField.defaultTextFormat = textFormat;
fallsField.x = 3;
fallsField.y = 43;
addChild(fallsField);
fpsField = new TextField();
fpsField.textColor = 0xffffff;
fpsField.selectable = false;
fpsField.defaultTextFormat = textFormat;
fpsField.x = 3;
fpsField.y = 63;
addChild(fpsField);
aboutField = new TextField();
aboutField.selectable = false;
aboutField.textColor = 0xffffff;
aboutField.autoSize = TextFieldAutoSize.RIGHT;
aboutField.defaultTextFormat = textFormat
aboutField.htmlText = "Phykick v1.0.0\n© Mathis Lichtenberger";
aboutField.y = stage.stageHeight - aboutField.height;
aboutField.x = 3;
addChild(aboutField);
var infoText:String = "// kick the boxes off the platform";
infoText += "\n// as fast as possible";
infoText += "\n// you've got 100 seconds";
infoText += "\n";
infoText += "\n// arrow keys: navigate";
infoText += "\n// space bar: start game";
infoText += "\n// ctrl + F: fullscreen";
infoField = new TextField();
infoField.textColor = 0xffffff;
infoField.y = 60;
infoField.width = stage.stageWidth;
infoField.multiline = true;
infoField.selectable = false;
infoField.autoSize = TextFieldAutoSize.CENTER;
infoField.defaultTextFormat = textFormat;
infoField.text = infoText;
addChild(infoField);
endScoreField = new TextField();
endScoreField.defaultTextFormat = new TextFormat("Arial", 20, 0xffffff, true, false, false);
endScoreField.y = 600 - 90;
endScoreField.width = stage.stageWidth;
endScoreField.selectable = false;
endScoreField.autoSize = TextFieldAutoSize.CENTER;
endScoreField.visible = false;
addChild(endScoreField);
timeField.text = "time: 100";
fallsField.text = "falls: 0";
scoreField.text = "score: 0";
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
addEventListener(Event.ENTER_FRAME, step);
}
private function startGame():void {
inGame = true;
infoField.visible = false;
endScoreField.visible = false;
startTime = getTimer();
lastStepTime = getTimer();
score = 0;
numFalls = 0;
timeField.text = "time: 0";
fallsField.text = "falls: 0";
scoreField.text = "score: 0";
}
private function stopGame():void {
inGame = false;
infoField.visible = true;
endScoreField.text = score.toString();
endScoreField.visible = true;
timeField.text = "time: 100";
}
private function step(event:Event):void {
if (inGame) {
// update player forces
if (keyLeft) playerBody.AddWorldForce(new JNumber3D(-100,0,0),playerBody.CurrentState.Position);
if (keyRight) playerBody.AddWorldForce(new JNumber3D(100,0,0),playerBody.CurrentState.Position);
if (keyForward) playerBody.AddWorldForce(new JNumber3D(0,0,100),playerBody.CurrentState.Position);
if (keyReverse) playerBody.AddWorldForce(new JNumber3D(0,0,-100),playerBody.CurrentState.Position);
}
// check for kicked boxes
for (var i:int = boxBodies.length - 1; i >= 0; i--) {
var boxBody:JBox = boxBodies[i];
if (boxBody.CurrentState.Position.y < 0) {
boxBody.MoveTo(new JNumber3D(Math.random()*400-200, 400, Math.random()*400-200), JMatrix3D.IDENTITY);
if (inGame) score++;
scoreField.text = "score: " + score;
}
}
// check if player is falling
if (playerBody.CurrentState.Position.y < 0) {
playerBody.MoveTo(new JNumber3D(0, 300, 0), JMatrix3D.IDENTITY);
if (inGame) numFalls++;
fallsField.text = "falls: " + numFalls;
}
if (inGame) {
timeField.text = "time: " + (getTimer() - startTime) / 1000;
if (getTimer() - startTime > 100*1000) stopGame();
}
var duration:int = getTimer() - lastStepTime;
var integrateValue:Number = duration / 167;
lastStepTime = getTimer();
fpsCount++;
fpsValue += duration;
if (fpsCount > 100) {
fpsField.text = "fps: " + Math.round(1000 / (fpsValue / fpsCount));
fpsCount = 0;
fpsValue = 0;
}
PhysicsSystem.getInstance().Integrate(integrateValue);
renderer.renderScene(scene, camera, viewport);
}
private function createBox():JBox {
var materials:MaterialsList = new MaterialsList();
materials.addMaterial(new FlatShadeMaterial(light,0xeeee00),"all");
var boxSkin:Cube = new Cube(materials, 40, 40, 40);
scene.addChild(boxSkin);
var boxBody:JBox = new JBox(boxSkin, true, 40, 40, 40);
boxBody.MoveTo(new JNumber3D(Math.random()*400-200, 400, Math.random()*400-200), JMatrix3D.IDENTITY);
PhysicsSystem.getInstance().AddBody(boxBody);
return boxBody;
}
private function keyDownHandler(event:KeyboardEvent):void {
switch(event.keyCode) {
case Keyboard.UP: keyForward = true; break;
case Keyboard.DOWN: keyReverse = true; break;
case Keyboard.LEFT: keyLeft = true; break;
case Keyboard.RIGHT: keyRight = true; break;
case Keyboard.SPACE: if (!inGame) startGame(); break;
}
}
private function keyUpHandler(event:KeyboardEvent):void {
switch(event.keyCode) {
case Keyboard.UP: keyForward = false; break;
case Keyboard.DOWN: keyReverse = false; break;
case Keyboard.LEFT: keyLeft = false; break;
case Keyboard.RIGHT: keyRight = false; break;
}
}
}
}

