Best practices: ENTER_FRAME vs. Timer - actionscript-3

I'm creating a Flash game which is based on the old Pacman and I'm not sure which is the best way to control the animation.
As I understand it these type of games were originally dependent on a game loop which ran faster or slower depending on the CPU, which is why I imagine that the most similar to use would be the ENTER_FRAME event.
This however presents the problem of having to have a specific frame rate and changing it later is out of the question, not to mention being limited to very few different "speeds" (see below). An example could be that the sprite has to move 12 pixels before the next move is determined. If the speed is then 4 pixels per frame, the math is quite simple:
[...]
public var stepCount:uint = 0;
[...]
function enterFrameHandler(e:Event):void
{
if(stepCount==0) {
//Some code to evaluate next move. Let's say it evaluates to MOVE RIGHT
}
if(MOVE_RIGHT)
{
x += 4;
}
stepCount++;
if(stepCount > 2)
{
stepCount = 0; //Now ready to evaluate direction again.
}
}
This all works fine, but let's say that I want the sprite to move 5 pixels per frame. Then the number of frames before making the next evaluation would not compute. The stepSize would have to be a multiple of 12, which limits the different possible speeds (1,2,3,4 and 6 pixels per frame).
This is why I attempted to base the movement on a Timer instead, which I also managed to get to work, but the movement was somewhat erratic and it seemed like the Timer was using far more memory than the ENTER_FRAME event. Instead of an even movement the Timer made the sprite slow down and speed up and slow down again.
Another possible solution could be the Tween class, but it seems extravagant.
Does anyone have experience with what works best in other games?
Morten Twellmann

You have several separate issues here. Your first question is, should you execute your game loop in a frame event or a timer event? The answer is easy - you should do it in a frame event. The reason is that regardless of how you move your characters, the screen is updated precisely once per frame. So any time you're calling your game loop more than once per frame you're wasting CPU, and any time you call it less than once per frame, you're sacrificing visual quality. So this one is easy, don't bother with timer events at all.
The next question is whether your game's movement should be tied to frames or miliseconds, and the answer is that it depends on the game. Ask yourself this: suppose that some user is playing your game, and their spaceship (or whatever) is flying along at a given speed. Suddenly, the user's anti-virus package does something heavy, and the CPU spike causes Flash to stop updating for one second. Once the spike is over, do you want the spaceship to continue moving from where it was when the spike started? Or do you want it to jump forwards to where it would be if it had continued moving during the spike? If you want the former, you should tie your movement to frames; if you want the latter, you should tie it to miliseconds. But which one is best depends on how you want your game to work.
The final question is, how exactly should you move the characters in your game? Based on what you wrote, I'd do it as follows. For frame-based movement (i.e. the first approach described earlier):
// the ship moves 25 pixels per second
var shipSpeed:Number = 25;
// the number of seconds per frame, based on the published framerate
var frameTime:Number = 1 / stage.frameRate;
// game loop called each frame:
function gameLoop() {
// ...
playerShip.x += shipSpeed * frameTime;
// ....
}
This way, the ship's movement on screen is constant, regardless of what framerate you publish your SWF at. Using a higher framerate simply makes the movement smoother. Likewise, to tie your movement to time instead of frames, simply change "frameTime" in the code above to refer to the time elapsed since the previous frame, as described in Allan's answer.

Yes frame rates will vary depending on CPU amongst other things. Therefore you need to take this into account with your game loop. What I like to do is get the time difference between the current frame and the old frame and use that value in my calculations. So if it happens that there is a delay the larger difference value will then make up for the fact less frames ran.
var _previousTime:Number;
//gameLoop is the function called on ENTER_FRAME
public function gameLoop(e:Event):void
{
var currentTime:Number = getTimer();
var difference:Number = currentTime - _previousTime;
_previousTime = currentTime;
//use difference variable with calculations involving movement
}

Related

How to lock FPS with requestAnimationFrame?

I used script from Paul Irish
https://gist.github.com/paulirish/1579671
to create animation loop inside html site.
It works although it's faster in fullscreen mode than in browser window.
Also, I observed different speeds depending on canvas size and depending on browser I use.
Question: How can I ensure stable frame rate using the script?
Code is available here (Beginning WebGL, chapter 1 by Brian Danchilla):
https://github.com/bdanchilla/beginningwebgl/blob/master/01/2D_movement.html
Something like this should work. If the time delta between two frames is shorter than your FPS limit, the update function returns and waits for the next frame. But this will only limit the updates from happening too quickly; like emackey said, there's always the possibility the update loop will run more slowly.
var updateId,
previousDelta = 0,
fpsLimit = 30;
function update(currentDelta) {
updateId = requestAnimationFrame(update);
var delta = currentDelta - previousDelta;
if (fpsLimit && delta < 1000 / fpsLimit) {
return;
}
/* your code here */
previousDelta = currentDelta;
}
To embellish what #emackey said,
The short answer is you can't. You could ask the computer to do an infinite amount of work each frame. I can't promise to do that work in a finite amount of time.
On top of that each computer has a different amount of power. A cheap integrated GPU has much less power than a high end graphics card. An intel i3 is much slower than an i7.
You also mentioned changing the canvas size. Drawing a 300x150 canvas is only 45000 pixels worth of work. Drawing a 1920x1080 canvas would be 2,073,600 pixels of work or 46x more work
The best you can do is do the least amount of work possible, and or remove features on slow hardware either automatically or by user choice. Most games do this. They graphics setting options where the user can choose resolution, texture res, anti-alising levels and all kinds of other things.
That said, you can try to do your computations so things in your app move at a consistent speed relative to time. The framerate might slower on a slow machine or with a larger canvas but the distance something moves per second will remain the same.
You can do this by using the time value passed into requestAnimationFrame
function render(time) {
// time is time in milliseconds since the page was loaded
...do work...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
For example here is NON framerate independent animation
function render(time) {
xPosition = xPosition + velocity;
...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
and here is frame rate independent animation
var then = 0;
function render(time) {
var timeInSeconds = time * 0.001;
var deltaTimeInSeconds = timeInSeconds - then;
then = timeInSeconds;
xPosition = xPosition + velocityInUnitsPerSecond * deltaTimeInSeconds;
...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
Note: The time passed into requestAnimationFrame is higher resolution than Date.now()
Here's an article on it with animations
You can't enforce a stable frame rate directly. Your page is not the only app running on the user's platform, and platform capabilities vary widely. requestAnimationFrame runs as fast as it can, not exceeding the display update interval on the target device, but potentially much slower depending on available CPU, GPU, memory, and other limitations.
The standard practice here is to measure the amount of time that has elapsed since the previous animation frame, typically with Date.now(), and each frame advance the animation by that amount of time. To the human eye, this makes the resulting animation run at a consistent speed, even if the frame rate is highly variable.
For example, sites such as Shadertoy and GLSL Sandbox run full-screen GLSL shaders and pass in a uniform called time (or iGlobalTime), which is a float representing the number of seconds elapsed since the shader started. This time value increases at irregular intervals depending on how long each animation frame took to render, but the result is that the float appears to count upwards at a stable 1.0 per second. In this way, shader playback based on this time value can appear consistent.

How to replace Mutiple Timers with TweenLite delayedCall or ENTER_FRAME event?

Hey everyone so I have a lot of timers in my game to be specific around 8 timers. They all control different Movie clip objects that appear on the stage at different times. I also change the timers in my difficulty update function. Now I have read a lot to understand that Timers do cause lag and decrease the performance. I am creating this game using AS3 Adobe AIR for Android devices. My game seems to freeze for half a second every second which I believe is do to the timers as well as the garbage collector. Either way I was wondering if I remove all these timers and instead replace them with TweenLite TweenLite.delayedCallfuncion if it would dramatically increase performance. I Have tried this on one of my old timers that i removed and replaced with the tweenlite function and it seems to be working just fine but not sure if this is the correct way of doing it here is how i have it set up in my constructor:
TweenLite.delayedCall(6.0, addWatch);
and the addWatch function:
private function addWatch():void
{
TweenLite.delayedCall(6.0, addWatchTimer);
var newWatch:mcWatchTimer = new mcWatchTimer();
stage.addChild(newWatch);
aWatchTimerArray.push(newWatch);
//Start screen sound
watchSoundChannel;
watchSound = new watch();
watchSoundChannel = watchSound.play(0, 9999);
}
this seems to loop it without me having to attach an ENTER_FRAME Eveent listener to it. But not sure if this would be wise since I want to be able to change the delayedCall in my difficulty update to a faster time interval.
Any feedback on the situation would be appreciated. Hope I made enough sense.

Speed in game with libgdx

I'm making a game using libgdx. For now, every character has a speed, corresponding actually to the number of render the game wait before update the character. For example, if the character has a speed of 15, it will be updated every 15 renders. I'm conscious that this is not how it has to be done.
What is the proper way to do this? I really want to make a speed in %, for example a character will have a speed of 85%.
Use delta.
Gdx.graphics.getDeltaTime() method return secods since last render frame. Usually this value is very small, and it equal 1 / FPS.
#Override
public void render()
{
// limit it with 1/60 sec
float dt = Math.min(Gdx.graphics.getDeltaTime(), 1 / 60f);
// then move your characted according to dt
player.pos = player.pos + player.speed * dt;
// or, you could mute the speed like this:
player.pos = player.pos + player.speed * dt * 0.85;
}
When you calculate you object's next position just multiply it's speed with Gdx.graphics.getDeltaTime(), so the more time has pass since last render the more object will move. Movement speed will be constant, no matter of FPS.
However, simple solutions like this one always come with a catch! If you are i.e. moving a bullet it may happen that too much time has passed since last rendering (specially on mobile device). I.e. half of second, and for that time your bullet moved i.e. 100px and moved trough some target, but since it was never in range of detection (skipped it) target will be missed even it's not suppose to do so - player aimed well.
So, if just moving some object is not what you want, but you need that movement to be in some regular steps better way is not to multiply speed with delta time, but to repeat movement and all calculations (detections and stuff) depending on delta time. I.e.:
You have method move left(), which moves object one step and with that amount of movement everything works well.
Your method should be called 20 times per second (step takes 50mS).
You measured time since last render and it's 100mS, means that your objects need to be moved 2 steps
Don't just multiply it's speed and do one calculation - instead of that repeat whole calculation process 2 times! Call your left() method twice!
If time since last drawing is less then 50mS - then skip calculations and just draw graphics as you did in last frame.
This way you will have separated calculation rate from drawing rate. Calculation rate will be same on all devices, but drawing will depend on devices performance..

AS3 framerate change issue

I have a KeyboardEvent that switches Boolean, if true an FR var drops -2/frame to a bottom of 10, if false it rises +2/frame to a top of 60. It works fine going up and down but whenever I add "stage.frameRate = FR;" to an ENTER_FRAME function and play it, it freezes the control over the window. Animation still plays but I can't turn it off unless via Ctrl-Alt-Del.
1.Why is this happening?
2.Is it possible to change frameRate every frame?
3.If yes, how?
Um, I am not sure playing with the frame rate on the fly is a good thing to be doing. What are you trying to do? Perhaps there is a better way...
This is a total guess, but perhaps when you set the frame rate to the stage it initialises 'something' in the background. Then since you have it happening every frame, it'll try to initialises on every frame, causing it to be locked up.
Updating answer to include an example of using a Timer object:
import flash.utils.Timer;
import flash.events.TimerEvent;
// the timer takes in milliseconds, so for 30 frames/second, you would have a frame tick at every 1000 / 30, or 33.333ms
var tickSpeed:int = 1000 / 30;
var frameTick:Timer = new Timer(tickSpeed, 0);
function enterFrameListener(inputEvent:Timer):void {
// this method will run on every timer tick
}
frameTick.addEventListener(TimerEvent.TIMER, enterFrameListener);
// can change the tick speed of the timer like so (setting it to 1000 means 1 frame tick every second)
frameTick.delay = 1000;
// according to the api doc however, a tickspeed of less then 20ms is not recommended
// 20ms would be equal to 60 fps
By offloading it to a timer, you shouldn't need to mess around with the overall framerate with another advantage of other objects can be affected by different timers at the same time. Timer class also has a few helpful methods in itself as well: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/Timer.html
I think your app is freezing because you are trying to set the frame rate on every frame, i would suggest you should check your frame rate in your ENTER_FRAME section. if your frame rate goes up/down from you desired frame rate, then you should set your frame rate to FR.
Try this can help you.

Time Based Animation in Flash

I am new to working in ActionScript and I'm experimenting with using the Timeline to trigger certain events in my game. In particular, I'm using the timeline to trigger the fire event for a gun so that when the fire animation gets to a certain frame, a bullet is spawned. In the gun class (Enter_Frame) I check that the mouse is being held down and the time between the last shot and the current time is longer than the cool down, if everything checks out I play the animation.
if (time - lastShot > cooldown)
{
canShoot = true;
lastShot = time;
}
if (mouseHold && canShoot)
{
play();
}
This creates a problem when the frame rate is brought below the point where the full animation cannot be played before the cool down is up again. In this instance the bullets fire slower with lower frame rates.
In addition, higher frame rates make the gun more responsive to mouse input because the fire frame comes sooner than in lower frame rates.
The goal is obviously to make the game completely frame rate independent. I would like to have the ability to slow down or speed up the frame rate of the animation depending on the cool down of the gun.
What is the best way to make this animation play in a specific time period without skipping over the actions that I have put into the timeline?
It sounds like you're on the right track, but approaching this the wrong way. Check out the Timer class, it's framerate independent, and will greatly aid you.
var my_timer = new Timer(1000,0); //in milliseconds
my_timer.addEventListener(TimerEvent.TIMER, catchTimer);
my_timer.start();
function catchTimer(e:TimerEvent)
{
trace("event fired");
}
You should look into time based animation instead of frame based if you want your animations to be precise and not depend on the framerate, this way your animations will always be the same and will compensate the slower frame rates of slower computers. Here is an article that explains it very well: http://www.flashgamesclassroom.com/classroom/actionscript/frame-based-vs-time-based-animation/