On 07/07/2012, at 6:02 AM, Charlie Dickman wrote:

> In my view's drawRect method I use a global state variable that defines the 
> next function to perform. When drawRect gets called I do what is necessary 
> for the current state, set up for the next state, set the value for the next 
> state and then return from drawRect. So the primary driving force is the 
> periodic view update requirement (which is guaranteed by actions invoking 
> [self setNeedsDisplay: YES]).


Hmmm, don't do that.

-drawRect: is going to be called for all sorts of reasons, not just because 
your timer refreshed the view. It's quickly going to get out of step if your 
state machine (which is what it is) is advanced on every -drawRect:

Abstract the state machine so its state can be changed by some explicit message 
rather than -drawRect:, then you can control when it changes state according to 
the logic of your game. Again this sounds upside-down, in that you're using the 
drawing to control the state instead of using the state to control the drawing.

A naïve way to implement animation is to try to run a timer at, say 30 fps, 
which refreshes a view. When the view draws, it updates the animation state, 
for example by moving a graphic object some fixed distance. If things run 
smoothly, it "works", in that the speed and position of the object are 
apparently correct. Then sometimes they aren't, because for whatever reason the 
30fps can't be maintained, the motion slows down and the position is less than 
what was predicted. The right way to do this is to model the physics in a 
simple way, remembering that speed is distance / time. So when your timer 
fires, you measure the elapsed time and use that to calculate the correct 
position of the object. You set its position there and you refresh the view. 
Then when it is drawn, it is in the right place. It does not matter how slow or 
fast the view refresh manages to be, it will animate correctly - the object 
speed is now independent of the view update rate. This is a subtle difference 
but crucial to success.

You have to design your game and all its animation as if it were totally 
invisible. In other words, it has a data model that models the game in all its 
states, including transitional animations that has nothing to do with screens 
or views or pixels. Then you get your view to render that data model as a sort 
of 'snapshot' of what it's doing at any particular moment. It should not matter 
how quickly or slowly you take that snapshot - what you see is what the game is 
doing at that moment. If you take snapshots fast enough (30fps) you will see 
the game animated smoothly.

If on the other hand you use the taking of the snapshot to drive the game, it 
will be terrible. Effectively your game is in a static state until someone 
looks at it, at which point you're trying to hurriedly rearrange it into the 
expected state for display. If that were real life, you can see the absurdity 
of it.

You can use a timer to keep the state of the game updated, but at each moment, 
calculate the full state of the game and schedule a view update. Don't schedule 
a view update and use that to update the game state. If the logic is factored 
in a reasonable way, you can see that this amounts to swapping two lines of 
code. It makes all the difference.

--Graham
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to