views:

256

answers:

2

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

A: 

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

    }
Allan
Of course this makes total sense! Thank you very much.
Morten Twellmann
It's a basic thing to do but will not make movement ultimately smooth as I have experienced in my thread: http://stackoverflow.com/questions/1284886/optimizing-transition-movement-smoothness-for-a-2d-flash-game - what seemed to make things better was to move the background in the inverse direction of the player. I do not know why, though.
Tom
The only problem I ran into was with complicated physics in which large time steps caused some of my simulations to freak out. PS glad to see you solved your problem Tom :)
Allan
+1  A: 

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.

fenomas
Thanks. I totally understand the point of CPU and frame waste when using the Timer event. I also realized that I can simply increase the overall frame rate if I want to increase to the different possible speed values.
Morten Twellmann
You shouldn't increase the framerate to cause a change in speed values. The character speed is a game design concern, that you should choose based on how your game should work, while the framerate is a performance concern. You should make them independent, as in my code sample. Also note that there is no need at all for making the step size a multiple of the frame rate, or anything like that. You should move your actors every frame, and you don't have to move them an integral amount!
fenomas