4
\$\begingroup\$

No matter what frameworks we use -- Core Graphics, Cocos2D, OpenGL ES -- to write games, should a timer be used (for games that has animation even when a user doesn't do any input, such as after firing a missile and waiting to see if the UFO is hit)?

I read that NSTimer might not get fired until after scheduled time (interval), and CADisplayLink can delay and get fired at a later time as well, only that it tells you how late it is so you can move the object more, so it can make the object look like it skipped frame. Must we use a Timer? And if so, what is the best one to use?

\$\endgroup\$
3
  • \$\begingroup\$ This is common to the implementation of timers in many languages. .NET, for example, has a similar guarantee that "the timer may not run right away but at some point after the elapsed interval." \$\endgroup\$
    – ashes999
    Apr 20, 2012 at 2:28
  • \$\begingroup\$ Timer resolution for many engines if framerate dependent, meaning that timers are evaluated during a frame. If you create a timer that should fire every 30ms but are running with frames that are taking 60ms, your timer resolution may be limited to 60ms. \$\endgroup\$
    – Nic Foster
    Apr 24, 2012 at 19:14
  • \$\begingroup\$ Using a timer is fine in itself, just don't rely on the idea that the amount of time that you told it to wait between each frame is indeed the amount of time that has passed between frames. instead it is up to you to record what time it is during each frame, and find the time difference between this frame and the last frame, and all of your movements should be based on the amount of time that has past (velocity * deltaTime). This ensures your animations/etc will be the same speed no mater how slow or fast your timer is firing. \$\endgroup\$ Dec 28, 2012 at 0:08

5 Answers 5

1
\$\begingroup\$

For my game, I'm using one global timer for everything. This runs the game loop. I'm always in sync with the frame rate because I'm computing for delta (time difference between the current frame and previous frame). That way no matter how slow/fast your hardware is, it will only either look slow/fast but everything is still in sync.

I rarely use timer to wait for something. It's very unreliable for me.

For me I'm using cocos2d's schedule: method which can run as fast as it can. I read that cocos2d timer performs better than an NSTimer (not sure).

\$\endgroup\$
0
\$\begingroup\$

You don't necessarily have to use a timer but I would definitely recommend using time based animation. Find a high resolution timer (I don't program on iOS so you'll have to google for that) and just set a start time where things occur at intervals based off that start time.

\$\endgroup\$
0
\$\begingroup\$

For the specific case you're asking about, it would be far better to let the animation complete callback determine when to run your next bit of code. This way, it'll always exactly match when that missile hits that UFO.

\$\endgroup\$
0
\$\begingroup\$

Timers in any language can hang up. That's why I always have some Updateable class that uses a given library's Timer. In the constructor for Updateable, you can tell Updateable the interval in which it should call a virtual (overridable) function.

In order to account for slow downs, Updateable needs to store the last time in which the Timer fired. Then, when the Timer fires again, Updateable simply subtracts the current time from the previous time. That is your deltaTime. You then store the previous time, and the process continues.

Here is some code for my solution in Qt and C++

    // This class handles is an abstraction between Timer objects and their logic.
    class UpdateComponent : private QObject {

        Q_OBJECT

        public:
            UpdateComponent();
            boost::signals2::signal<void (float)> onUpdate;

        private Q_SLOTS:
            void onTimerUpdate();

        private:
            QTimer* m_timer;
            QDateTime m_previousDateTime;
            static const int UPDATE_INTERVAL = 17; // This can be changed through the constructor, for example.  This means let the timer fire every 17 milliseconds, or roughly, 60 times per second.
    };

    // Constructor
    UpdateComponent::UpdateComponent() {
        m_timer = new QTimer(); // Create a new QTimer

        connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimerUpdate())); // Tell m_timer to call this class's function, onTimerUpdate().

        m_timer->start(UPDATE_INTERVAL); // Start the timer.  It will now begin to fire every ~17 milliseconds.
        m_previousDateTime = QDateTime::currentDateTime(); // Store the previous time.
    }

    // Fired by the timer every UPDATE_INTERVAL (17 milliseconds)
    void UpdateComponent::onTimerUpdate() {
        QDateTime currentDateTime = QDateTime::currentDateTime(); // Store the current time.
        qint64 deltaTimeMilliseconds = m_previousDateTime.msecsTo(currentDateTime); // Find the delta time in milliseconds.
        m_previousDateTime = currentDateTime; // Make the previous time the current time.

        float deltaTime = deltaTimeMilliseconds/1000.0f; // Convert the delta time into a float with the unit being seconds, instead of milliseconds.
        onUpdate(deltaTime); // This is boost::signals2::signal's way of saying, call everyone that is listening to onUpdate, and pass the parameter deltaTime.
    }

Here is a class that will listen to UpdateComponent's onUpdate(float) function.

    class Updateable {

        public:
            Updateable();
            Components::UpdateComponent* getUpdateComponent();

        protected :
            virtual void onUpdate(float deltaTime) = 0;
            Components::UpdateComponent* m_updateComponent;
    };


    Updateable::Updateable() {
        m_updateComponent = new UpdateComponent();
        m_updateComponent->onUpdate.connect(boost::bind(&Updateable::onUpdate, this, _1));
    }

    UpdateComponent* Updateable::getUpdateComponent() {
        return m_updateComponent;
    }

Any class that is Updateable can implement the virtual function onUpdate(float deltaTime). onUpdate(float deltaTime) is called automagically in the derived class, such that its sole parameter tells you the time, in seconds, since the last time the timer was fired. Note that an Updateable object tries to fire as close to the specified interval as possible, but we make up for any slow-downs or speed-ups. 90% of the timer, you should be getting the interval you specified; however, that 10% of the time where there is a slow-down, or if a Timer prematurely fires, we account for it.

\$\endgroup\$
0
\$\begingroup\$

CADisplayLink callbacks are called on the UI thread, so if your game logic is taking a long time, you'll run into problems.

A solution is to have your game run on a second thread, and use the CADisplayLink callback to signal that thread to do a frame.

This way, your UI thread is free to always handle touches and CADisplayLink callbacks.

It does mean you'll need to synchronize access to input data via a mutex though.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .