From 30c36b02077d83a0f00f27243cf45e11d7eff7ee Mon Sep 17 00:00:00 2001 From: "T. Joseph Carter" Date: Wed, 11 Nov 2020 06:29:25 -0800 Subject: [PATCH] LukeMS's port of the PyGame time API to sdl2 LukeMS has this added to his personal sdl2 repo on GitHub, but hasn't offered the patch upstream. Enough people seem to want it that I've decided ti offer it as a PR. I didn't take time to write unit tests for it, sorry. Closes marcusva/py-sdl2#168. --- sdl2/ext/__init__.py | 1 + sdl2/ext/time.py | 170 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 sdl2/ext/time.py diff --git a/sdl2/ext/__init__.py b/sdl2/ext/__init__.py index d0a0c07a..5e7d987f 100644 --- a/sdl2/ext/__init__.py +++ b/sdl2/ext/__init__.py @@ -19,4 +19,5 @@ from .pixelaccess import * from .sprite import * from .surface import * +from .time import * from .window import * diff --git a/sdl2/ext/time.py b/sdl2/ext/time.py new file mode 100644 index 00000000..7e09fe8c --- /dev/null +++ b/sdl2/ext/time.py @@ -0,0 +1,170 @@ +"""Track and control game time and framerate. + +.. note:: + + This is a **MODIFIED**, pure Python implementation of time-related functions + from `PyGameSDL2's time`_, in Cython, released under zlib license. + +.. code-block:: none + + PygameSDL2 Notice / zlib License: + + -------------------------------------------------------------------------- + Original work: + Copyright 2014 Tom Rothamel + Copyright 2014 Patrick Dawson + Modified work: + Copyright 2017 Lucas Siqueira + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source + distribution. + -------------------------------------------------------------------------- + +.. _`PyGameSDL2's time`: https://www.github.com/renpy/pygame_sdl2/blob/\ + master/src/pygame_sdl2/pygame_time.pyx +""" + +import math +from ..timer import SDL_Delay, SDL_GetTicks + + +__all__ = ('Clock', 'wait', 'get_time', 'get_delta') + + +def get_delta(t0): + """Get the time elapsed since the passed time.""" + return get_time - t0 + + +def get_time(): + """Get the current time from SDL clock.""" + return SDL_GetTicks() + + +def wait(milliseconds): + """Pause the program for an amount of time. + + Will pause for a given number of milliseconds. This function sleeps the + process to share the processor with other programs. A program that waits + for even a few milliseconds will consume very little processor time. + + Usage: + wait(milliseconds) + + Returns: + int (the actual number of milliseconds used) + """ + start = SDL_GetTicks() + SDL_Delay(int(milliseconds)) + return SDL_GetTicks() - start + + +class Clock: + """Clock is used track and control the framerate of a game. + + The clock can be used to limit the framerate of a game, as well as track + the time used per frame. + + Usage: + clock = time.Clock() + """ + + def __init__(self): + """Initialization.""" + self.last = SDL_GetTicks() + self.last_frames = [] + self.frametime = 0 + self.raw_frametime = 0 + + def tick(self, framerate=0): + """Update the Clock. + + This method should be called once per frame. It will compute how many + milliseconds have passed since the previous call. + + If you pass the optional framerate argument the function will delay + to keep the game running slower than the given ticks per second. This + can be used to help limit the runtime speed of a game. By calling + clock.tick(40) once per frame, the program will never run at more + than 40 frames per second. + + Usage: + tick(framerate=0) + + Returns: + float (milliseconds) + """ + now = SDL_GetTicks() + self.raw_frametime = now - self.last + while len(self.last_frames) > 9: + self.last_frames.pop(0) + if framerate == 0: + self.last = now + self.last_frames.append(self.raw_frametime) + return self.raw_frametime + frame_duration = 1.0 / framerate * 1000 + if self.raw_frametime < frame_duration: + wait(frame_duration - self.raw_frametime) + now = SDL_GetTicks() + self.frametime = now - self.last + self.last = now + self.last_frames.append(self.frametime) + return self.frametime + + def get_time(self): + """Time used in the previous tick. + + Returns the parameter passed to the last call to Clock.tick(). + + Usage: + clock.get_time() + + Returns: + int (milliseconds) + """ + return self.frametime + + def get_rawtime(self): + """Actual time used in the previous tick. + + Similar to Clock.get_time(), but this does not include any time used + while clock.tick() was delaying to limit the framerate. + + Usage: + clock.get_rawtime() + + Returns: + int (milliseconds) + """ + return self.raw_frametime + + def get_fps(self): + """Compute the clock framerate. + + Compute your game's framerate (in frames per second). It is computed + by averaging the last ten calls to Clock.tick(). + + Usage: + get_fps() + + Returns: + float + """ + total_time = sum(self.last_frames) + average_time = total_time / 1000.0 / len(self.last_frames) + average_fps = 1.0 / average_time + return 0 if math.isnan(average_fps) else average_fps