1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
Keeping track of time
=====================
pyglet's :py:mod:`~pyglet.clock` module allows you to schedule functions
to run periodically, or for one-shot future execution. There are also some
helpful utilities provided for calculating and displaying the application
frame rate.
.. _guide_calling-functions-periodically:
Calling functions periodically
------------------------------
As discussed in the :ref:`programming-guide-eventloop` section, pyglet
applications begin execution by entering into an application event loop::
pyglet.app.run()
Once called, this function doesn't return until the application windows have
been closed. This may leave you wondering how to execute code while the
application is running.
Typical applications need to execute code in only three circumstances:
* A user input event (such as a mouse movement or key press) has been
generated. In this case the appropriate code can be attached as an
event handler to the window.
* An animation or other time-dependent system needs to update the position
or parameters of an object. We'll call this a "periodic" event.
* A certain amount of time has passed, perhaps indicating that an
operation has timed out, or that a dialog can be automatically dismissed.
We'll call this a "one-shot" event.
To have a function called periodically, for example, once every 0.1 seconds::
def update(dt):
# ...
pyglet.clock.schedule_interval(update, 0.1)
The `dt`, or `delta time` parameter gives the number of "wall clock" seconds
elapsed since the last call of this function, (or the time the function was
scheduled, if it's the first period). Due to latency, load and timer
inprecision, this might be slightly more or less than the requested interval.
Please note that the `dt` parameter is always passed to scheduled functions,
so be sure to expect it when writing functions even if you don't need to
use it.
Scheduling functions with a set interval is ideal for animation, physics
simulation, and game state updates. pyglet ensures that the application does
not consume more resources than necessary to execute the scheduled functions
on time.
Rather than "limiting the frame rate", as is common in other toolkits, simply
schedule all your update functions for no less than the minimum period your
application or game requires. For example, most games need not run at more
than 60Hz (60 times a second) for imperceptibly smooth animation, so the
interval given to :py:func:`~pyglet.clock.schedule_interval` would be
``1/60.0`` (or more).
If you are writing a benchmarking program or otherwise wish to simply run at
the highest possible frequency, use `schedule`. This will call the function
as frequently as possible (and will likely cause heavy CPU usage)::
def benchmark(dt):
# ...
pyglet.clock.schedule(benchmark)
By default pyglet window buffer swaps are synchronised to the display refresh
rate, so you may also want to disable vsync if you are running a benchmark.
For one-shot events, use :py:func:`~pyglet.clock.schedule_once`::
def dismiss_dialog(dt):
# ...
# Dismiss the dialog after 5 seconds.
pyglet.clock.schedule_once(dismiss_dialog, 5.0)
To stop a scheduled function from being called, including cancelling a
periodic function, use :py:func:`pyglet.clock.unschedule`. This could be
useful if you want to start running a function on schedule when a user provides
a certain input, and then unschedule it when another input is received.
Sprite movement techniques
--------------------------
As mentioned above, every scheduled function receives a `dt` parameter,
giving the actual "wall clock" time that passed since the previous invocation.
This parameter can be used for numerical integration.
For example, a non-accelerating particle with velocity ``v`` will travel
some distance over a change in time ``dt``. This distance is calculated as
``v * dt``. Similarly, a particle under constant acceleration ``a`` will have
a change in velocity of ``a * dt``.
The following example demonstrates a simple way to move a sprite across the
screen at exactly 10 pixels per second::
sprite = pyglet.sprite.Sprite(image)
sprite.dx = 10.0
def update(dt):
sprite.x += sprite.dx * dt
pyglet.clock.schedule_interval(update, 1/60.0) # update at 60Hz
This is a robust technique for simple sprite movement, as the velocity will
remain constant regardless of the speed or load of the computer.
Some examples of other common animation variables are given in the table
below.
.. list-table::
:header-rows: 1
* - Animation parameter
- Distance
- Velocity
* - Rotation
- Degrees
- Degrees per second
* - Position
- Pixels
- Pixels per second
* - Keyframes
- Frame number
- Frames per second
The frame rate
--------------
Game performance is often measured in terms of the number of times the display
is updated every second; that is, the frames-per-second or FPS. You can
determine your application's FPS with a single function call::
pyglet.clock.get_fps()
The value returned is more useful than simply taking the reciprocal of `dt`
from a period function, as it is averaged over a sliding window of several
frames.
Displaying the frame rate
^^^^^^^^^^^^^^^^^^^^^^^^^
A simple way to profile your application performance is to display the frame
rate while it is running. Printing it to the console is not ideal as this
will have a severe impact on performance. pyglet provides the
:py:class:`~pyglet.window.FPSDisplay` class for displaying the frame rate
with very little effort::
fps_display = pyglet.window.FPSDisplay(window=window)
@window.event
def on_draw():
window.clear()
fps_display.draw()
By default the frame rate will be drawn in the bottom-left corner of the
window in a semi-translucent large font.
See the :py:class:`~pyglet.window.FPSDisplay` documentation for details
on how to customise this, or even display another clock value (such as
the current time) altogether.
User-defined clocks
-------------------
The default clock used by pyglet uses the system clock to determine the time
(i.e., ``time.time()``). Separate clocks can be created, however, allowing
you to use another time source. This can be useful for implementing a
separate "game time" to the real-world time, or for synchronising to a network
time source or a sound device.
Each of the ``clock_*`` functions are aliases for the methods on a global
instance of :py:class:`~pyglet.clock.Clock`. You can construct or subclass
your own :py:class:`~pyglet.clock.Clock`, which can then maintain its own
schedule and framerate calculation.
See the class documentation for more details.
|