Before the weekend, I suggest to dig into the Box2D API for Flash with the great WorldConstructionKit.
Box2D is a physical engine wrote in C++ and translated into many languages such as Java, Objective-C, AS3, JavaScript…
For ActionScript3, there are 2 ports : Boris the brave’s port, and the WCK. You can find a performance comparison here made by Allan Bishop.
The WCK is the best thanks to the Alchemy port : it translates the C++ into something which can be understable by Flash. More information here.
The WCK provides a “a toolset / framework for rapidly developing physics based games / websites within the Flash IDE. WCK allows you to layout your 2d worlds / game levels entirely within Flash and have them hook into the physics simulation without writing any code.”
Here are some demos I made with it :
A simple circle made without any code, only with MovieClip on the Stage and propreties added by the WCK components (dynamic object -> the circle vs static objects -> the wall). If you wish the camera followed the circle, just add the name of your MovieClip in the focusOn property of your World; don’t forget to check the scrolling’s property too.
A simple Mario game. In this case, for sure, there is some programming. (Do not forget to click into the windows before pressing any keys)
The code of the AI :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | package { import Box2DAS.Common.V2; import Box2DAS.Dynamics.StepEvent; import shapes.Box; import flash.events.Event; /** * @author Aymeric */ public class Momie extends Box { private var _arriere:Boolean; public function Momie() { this.addEventListener(Event.ADDED_TO_STAGE, _init); } private function _init(evt:Event):void { this.removeEventListener(Event.ADDED_TO_STAGE, _init); } public override function create():void { reportBeginContact = true; reportEndContact = true; super.create(); listenWhileVisible(world, StepEvent.STEP, _parseInput); } private function _parseInput(evt:Event):void { if (_arriere == false) { if (this.x < 800) { b2body.ApplyImpulse(new V2(0.2, 0), b2body.GetWorldCenter()); } else { _arriere = true; this.gotoAndPlay("marche_gauche"); } } else { if (this.x > 30) { b2body.ApplyImpulse(new V2(-0.2, 0), b2body.GetWorldCenter()); } else { _arriere = false; this.gotoAndPlay("marche_droite"); } } } } } |
Nothing too complex, but note that the character moved thanks to some force vector working with impulsion. Not as simple than an old x++ 😉
And now the Hero :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | package { import Box2DAS.Collision.*; import Box2DAS.Common.*; import Box2DAS.Dynamics.*; import misc.*; import shapes.*; import wck.*; import flash.events.*; import flash.utils.Timer; /** * @author Aymeric */ public class Heros extends Box { public var contacts:ContactList; private var _momie:Momie; private var _memoireSens:String; private var _timer:Timer; public function Heros() { this.addEventListener(Event.ADDED_TO_STAGE, _init); } private function _init(evt:Event):void { this.removeEventListener(Event.ADDED_TO_STAGE, _init); } public override function create():void { reportBeginContact = true; reportEndContact = true; // fixedRotation = true; super.create(); listenWhileVisible(world, StepEvent.STEP, parseInput, false, 10); listenWhileVisible(this, ContactEvent.BEGIN_CONTACT, handleContact); contacts = new ContactList(); contacts.listenTo(this); } public function handleContact(ctcEvt:ContactEvent):void { _momie = ctcEvt.other.m_userData as Momie; var piece:Circle = ctcEvt.other.m_userData as Circle; if (_momie) { // Util.addChildAtPosOf(world, new Boum(), p); _momie.gotoAndStop("mort"); _momie.removeAllListeners(); _timer = new Timer(500, 1); _timer.addEventListener(TimerEvent.TIMER_COMPLETE, _supprimerMomie); _timer.start(); } if (piece) { piece.remove(); piece = null; } } private function _supprimerMomie(tEvt:TimerEvent):void { _momie.remove(); _momie = null; _timer.removeEventListener(TimerEvent.TIMER_COMPLETE, _supprimerMomie); _timer = null; } public function parseInput(e:Event):void { var manifold:b2WorldManifold = null; var dot:Number = -1; if (!contacts.isEmpty()) { contacts.forEach(function(keys:Array, c:ContactEvent) { var wm:b2WorldManifold = c.getWorldManifold(); if (wm.normal) { var d:Number = wm.normal.dot(gravity); if (!manifold || d > dot) { manifold = wm; dot = d; } } }); contacts.clean(); } var left:Boolean = Input.kd('A', 'LEFT'); var right:Boolean = Input.kd('D', 'RIGHT'); var jump:Boolean = Input.kp(' ', 'UP'); var v:V2; if (jump && manifold) { v = manifold.normal.clone().multiplyN(-7); b2body.ApplyImpulse(v, b2body.GetWorldCenter()); } else if (left) { if (this.currentLabel != "marche_gauche") { this.gotoAndPlay("marche_gauche"); } b2body.ApplyImpulse(new V2(-0.2, 0), b2body.GetWorldCenter()); _memoireSens = "gauche"; } else if (right) { if (this.currentLabel != "marche_droite") { this.gotoAndPlay("marche_droite"); } b2body.ApplyImpulse(new V2(0.2, 0), b2body.GetWorldCenter()); _memoireSens = "droite"; } else { if (_memoireSens == "gauche") { this.gotoAndPlay("init_gauche"); } else { this.gotoAndPlay("init_droite"); } } } } } |
There are some collisions problem to handle, I haven’t found the best resolution yet…
To conclude I found that the WCK is really a great tool to create a dynamic world ! I’m sure that a breakout game is really easy to make with the WCK, but if you want to make something very complex, you must learn Box2D and have some knowledges of C++.
Recently, I tried the Citrus Engine and find it awesome. It is a game engine based on Box2D (but not the WCK), but I’m sad, it is no more supported… However maybe, I will make a school project with it.
Stay tuned !