For my latest game prototype, aside from KeyActionBinder, I’m also using my new GameLooper class to control the main game loop. Because of the way the class has been built, it is particularly easy to integrate it with something like Box2DFlash, which is what I’m using in my prototype for physics simulation.
Integration works by first, creating the instances I’ll use:
private var looper:GameLooper; // Game looper private var world:b2World; // Box2D world private function gameStart():void { world = loadWorldFromRUBE(jsonLevelTest); looper = new GameLooper(false, 60); looper.timeScale = 1; }
The code above creates a new b2World instance (loading the level data from an object that came from a R.U.B.E. level, using my work-in-progress loadWorldFromRUBE function), as well a GameLooper
instance that is not paused (hence the false
parameter) and that, most importantly, has a minimum FPS parameter of 60. This is important because I want to achieve a certain level of consistency in my simulation.
Next, I because I am using a forced minimum FPS, I attach two events to the looper – one for each frame (tick) call, and one for each visual frame call.
private var looper:GameLooper; // Game looper private var world:b2World; // Box2D world private function gameStart():void { world = loadWorldFromRUBE(jsonLevelTest); looper = new GameLooper(false, 60); looper.timeScale = 1; looper.onTicked.add(onTick); looper.onTickedOncePerVisualFrame.add(onTickVisual); }
In this code, onTicked
will get called at least the amount of times I’ve asked it to per second – it will be called at least 60 times per second, regardless of the visual framerate. The onTickedOncePerVisualFrame
signal, on the other hand, will only be dispatched once per frame; this may be 60 times per second, if the actual framerate allows.
The idea is that I want to run the physics simulation at least 60 times per second, to achieve a certain level of consistency regardless of a device’s actual performance (I’m trying to make it be a deterministic simulation so I can have demo replaying solely based on input). That way, even if the screen doesn’t get updated 60 times per second, the actual results will be the same (as long as it doesn’t explode). It’s not every situation that calls for this level of consistency, however; if a game can do with less consistent physics across devices or platforms, a minimum amount of simulation frames per second is not needed.
It must be said that Box2D already handles longer loops pretty well, since it takes the amount of time passed into account, but I’ve seen pretty different behavior between mobile devices and the desktop if a minimum number of frames is not enforced. With the minimum frame enforcement, however, the simulation looked the same between platforms, with no noticeable loss of performance (the physics loop was actually taking very little time in comparison to the time spent drawing the elements in my non-optimized, vector-based tests).
The game loop, then, is split between two different functions: one for running the physics simulation steps, and one for doing all visual updates. This is what it looks like right now:
private function onTick(__currentTimeSeconds:Number, __tickDeltaTimeSeconds:Number, __currentTick:int):void { // Ask box2d to update the world world.Step(__tickDeltaTimeSeconds, 3, 8); world.ClearForces(); } private function onTickVisual(__currentTimeSeconds:Number, __tickDeltaTimeSeconds:Number, __currentTick:int):void { // Handle user input ... code here ... // Handle visual updates (using debug data drawn to container, centering in the player's body) world.DrawDebugData(); container.x = -playerBody.GetPosition().x * worldScale + stage.stageWidth * 0.5; container.y = playerBody.GetPosition().y * worldScale + stage.stageHeight * 0.5; }
In this example, I’m simply using Box2D’s built-in DrawDebugData()
method, which draws the entire world to the container
Sprite that later gets centered in the player’s position. Looping through the list of bodies present in world
would be the normal application of that loop.
Update (6 August 2013): I’ve started creating my prototype with an alternative “World” in parallel using the popular AS3 physics engine Nape. While this engine is less popular than Box2D, doesn’t have an editor as good as R.U.B.E., and I’m missing a few features found on Box2D, it has an interface that is much Flash-friendly and is normally assumed to be faster than Box2D. So while I’m still deciding on which engine to use more seriously in my prototype, here’s the equivalent Nape-minded code for the above functionality:
private var looper:GameLooper; // Game looper private var space:Space; // Nape space private function gameStart():void { space = new Space(Vec2.weak(0, 600)); // Setup space contents here... looper = new GameLooper(false, 60); looper.timeScale = 1; looper.onTicked.add(onTick); looper.onTickedOncePerVisualFrame.add(onTickVisual); } private function onTick(__currentTimeSeconds:Number, __tickDeltaTimeSeconds:Number, __currentTick:int):void { // Ask Nape to update the world space.step(__tickDeltaTimeSeconds, 3, 8); } private function onTickVisual(__currentTimeSeconds:Number, __tickDeltaTimeSeconds:Number, __currentTick:int):void { // Handle user input ... code here ... // Handle visual updates (using a ShapeDebug, centering in the player's body) debug.transform.tx = _width * 0.5 - playerBody.position.x; debug.transform.ty = _height * 0.5 - playerBody.position.y; debug.clear(); debug.draw(space); debug.flush(); }
Of course, since many of the abstractions and paradigms used by Nape are similar to Box2D, the code looks very similar.
Comments are closed.