More discussions! Great!
I don't understand why one would want to use the FPS count to time animations... It just doesn't seem like a very accurate method for tracking/managing animations. Am I missing something?
It is entirely possible that my ideas are totally wrong.
You (and others here as well) seem to link animations to the real time, in one way or another.
That is a very good idea, it seems better than my idea of slowing down the entire game if the cycle time becomes too long.
As far I know, there is a variable(the computer processing speed) that you must put in this equation, I just don't remember where.
That sounds like the next problem you have after deciding that you have x milli seconds for each frame. How do you divide those x milli seconds between input processing, updating game state, rendering, and (possibly) idling?
For idling, you need to know much time is taken for the other activities. Perhaps that is what you were aiming at?
This article is gold, ingest its deep wisdom! You don't need to be running physics to benefit from using a fixed timestep, just chuck out the bit about integrating between states and keep everything else that fixes the timestep of updates.
I read it, and it is indeed very clear. I definitely want to use it for my physics (including the interpolation, it sounds like fun to build it :) ).
For animations, a similar system would be useful I think. I just need to wrap my head around it.
I would recommend you abandon the callbacks and use a fixed timestep to keep your animations running smoothly. It should be left up to each individual object to update its state based on information available to it. So supply it with the fixed deltaTime value via an update function.
This is simpler, and you don't distribute state, which is good.
My callback system is mostly about avoiding update of some internal timer of each object, which you very quickly do in more than 50% of the cases. That is a lot of mostly useless memory access if you have a 1000+ game objects wandering around.
Let everything inherit from Updatable with a void update(float deltaTime). In your game loop you check if stuff must be updated yet and call update on everything if it does.
Haha, and then you continue with how bad it is to abuse inheritance :D
1) the programmer is not thinking in OO and has blobs of data called Blah that he is manipulating with BlahManager (instead of letting each object do it's own thing via methods) [see TickCallback *next; ///< Next callback in the chain (managed by #TickManager).]
I am well aware of sneaking in foreign data here, but to me this really means "don't you dare touch it, object!". It exists so I can make a poor man's list out of the objects, without having to create a full-blown list<TickCallback *>
at every point in the array.
(ie it is piggy-backing some inter-object management data onto the object, the object is not touching 'next, and the manager is not touching data of the object).
'Manager' for me is strictly inter-object business, as well as storage/retrieval of collections of objects, and dispatching calls from the environment to the right object. Like you say, it is not
doing the job of the objects.
2) the programmer didn't have a clear enough idea of what the class was trying to achieve and it ended up with the highly generic Manager title. I think this is maybe applicable to what you've created.
To some extent that is certainly true. The whole point of this thread is to figure out a good way to handle many animated objects at the same time.
(Which is working very nicely, I must say. Thanks guys for all the feedback you are providing. It's very useful and much appreciated.)
The class is indeed very generic, which came as surprise to me. It caused me to reconsider the structure of the classes. Not by extending the responsibilities, but by shifting animation more to the objects themselves, so this tick-manager gets room to do just tick-handling (and nothing more).
I have been playing the OO game long enough to know that you should keep responsibilities at the right place, and that you cannot simply 'add' responsibilities.
The code you've outlined is highly general purpose, even though you set out to create something for animation timing.
Yeah, at which point I concluded I created something else than my objective, and I started wondering how others do this. I had a quick peek at the GPWiki, but could not find a discussion quickly, so I ended up posting here :)
Now if and when you start doing more things with your class, you will end up widening the class's responsibility. Making any changes to it becomes harder and harder. Say you want to start running AI logic with this class. Now a GameObject which has inherited TickCallback for animation purposes has to hack the method to add in stuff to check whether the callback is getting called because of an AI reason or an animation reason.
That leads onto another issue: abusing inheritance. Inheritance should only be used in is-a relationships. You're abusing it because you're wanting access to private information of the various objects in order to execute the callback. Only you're hiding the fact that you're breaking encapsulation by forcing the classes to inherit from TickCallback. What's actually happening is that TickManager is manipulating objects' private data that it shouldn't know anything about, albeit in a dynamic and extensible way.
I assume this is about 'next', right?
I explained that above already.
I thought about 'abuse of inheritance' this morning before getting up, and I think the issue is somewhat different.
In Java, you'd write an interface for this (not sure whether you can add data in an interface, but let's assume you can), and implement that interface for all objects (much like you propose with 'Updatable'). In C++ however, inheritance is not only pure inheritance (as it is in newer languages), it may also be used for the notion of 'interface'. Stroustrup already sees abstract base classes as interfaces
So technically, my base class is a proper solution. However, I fully agree with you that it is not one I should be proud of. Multiple inheritance gets very complicated very quickly, so you want to avoid it as much as possible.
Minor gripe: using this-> is making your code harder to read. It's unnecessary given the scope and lack of name conflicts.
True, but I have to come to like explicitness where things are coming from. Blame it on programming Python for too long, as well as other C++ projects :p
Another minor gripe: your TickCallback is an abstract class, which means everything that wants to make use of timing functions is going to end up with a VTABLE lookup hit every time Tick() is called. This is probably going to make no difference to performance, but it feels wrong and unnecessary.
And doing your own administration is faster, is less error prone, and feels better???
For a generic class, it is the right solution, I think.
The question is whether the solution is the right answer to the problem. The answer seems to be mostly 'no'. The only thing stopping it from dropping out of sight completely is the fact that I don't like updating several hundred timer counters each update cycle just because I didn't organize my animated objects in a saner way.
I think your solution is overly complex.
It is certainly more complex. What I find difficult to decide is whether it is worth the gain in CPU time.
Perhaps I should stop worrying, and just what you suggest. At least as initial version it will work, and perhaps even longer :p
You set TickCallback *cb = this->ticks[this->head] but then don't use cb on the next line where you set the head to NULL. Then you say TickCallback *next = cb->next; which would segfault.
I would hope not. Note that 'ticks' is an array of pointers, so I should be clearing a pointer, not the object itself.
C++ is very complicated and some of the stuff I've said might be wrong. I've tried to double check it as much as possible though.
And your efforts are appreciated (just like your entire post btw).
Also, thank both you and weezl for posting the game-loop. They will come in handy when hooking the animations into the main loop.
(It looks like it's time to ditch the fixed rate game loop :D )