robolib.utils.clock

clock.py - clock used for physics with fixed DT in main loops Usage: clock = Clock(dt=1 / FPS, max_ticks=MAX_SUBTICKS_PER_RENDER_TICK) # max_ticks is for 'spiral of death' (e.g. use 3-5) while True: # main loop clock.wait_and_tick() ... io stuff ... for _ in clock.drain(): ... physics loop ... ... render loop ...

 1"""
 2clock.py - clock used for physics with fixed DT in main loops
 3Usage:
 4clock = Clock(dt=1 / FPS, max_ticks=MAX_SUBTICKS_PER_RENDER_TICK) # max_ticks is for 'spiral of death' (e.g. use 3-5)
 5while True: # main loop
 6    clock.wait_and_tick()
 7    ... io stuff ...
 8    for _ in clock.drain():
 9       ... physics loop ...
10    ... render loop ...
11"""
12import time
13
14class Clock:
15    """clock used for physics with fixed DT in main loops"""
16
17    def __init__(self, dt: float, max_ticks: int):
18        self.dt = dt
19        self.max_ticks = max_ticks
20        self.prev_time = time.perf_counter()
21        self.accumulator = 0
22        self.tick_count = -1
23
24    def tick(self):
25        """tick once by adding the delta between prev frame and now"""
26        now = time.perf_counter()
27        frame_time = now - self.prev_time
28        self.prev_time = now
29        self.accumulator += frame_time
30        self.tick_count += 1
31
32    def drain(self):
33        """drain the accumulator. in main loop: for _ in clock.drain(): ..."""
34        n_ticks = 0
35        while self.accumulator >= self.dt and n_ticks < self.max_ticks:
36            yield
37            self.accumulator -= self.dt
38            n_ticks += 1
39        self.accumulator = min(self.accumulator, self.dt) # Drop residual debt instead of it piling up across frames
40
41    def wait(self):
42        """waits the leftover time in case the previous tick ran too fast to maintain consistent FPS"""
43        time.sleep(max(self.dt - (time.perf_counter() - self.prev_time), 0))
44
45    def wait_and_tick(self):
46        """calls wait() then tick(). Put this at the beginning of the main loop :)"""
47        self.wait()
48        self.tick()
class Clock:
15class Clock:
16    """clock used for physics with fixed DT in main loops"""
17
18    def __init__(self, dt: float, max_ticks: int):
19        self.dt = dt
20        self.max_ticks = max_ticks
21        self.prev_time = time.perf_counter()
22        self.accumulator = 0
23        self.tick_count = -1
24
25    def tick(self):
26        """tick once by adding the delta between prev frame and now"""
27        now = time.perf_counter()
28        frame_time = now - self.prev_time
29        self.prev_time = now
30        self.accumulator += frame_time
31        self.tick_count += 1
32
33    def drain(self):
34        """drain the accumulator. in main loop: for _ in clock.drain(): ..."""
35        n_ticks = 0
36        while self.accumulator >= self.dt and n_ticks < self.max_ticks:
37            yield
38            self.accumulator -= self.dt
39            n_ticks += 1
40        self.accumulator = min(self.accumulator, self.dt) # Drop residual debt instead of it piling up across frames
41
42    def wait(self):
43        """waits the leftover time in case the previous tick ran too fast to maintain consistent FPS"""
44        time.sleep(max(self.dt - (time.perf_counter() - self.prev_time), 0))
45
46    def wait_and_tick(self):
47        """calls wait() then tick(). Put this at the beginning of the main loop :)"""
48        self.wait()
49        self.tick()

clock used for physics with fixed DT in main loops

Clock(dt: float, max_ticks: int)
18    def __init__(self, dt: float, max_ticks: int):
19        self.dt = dt
20        self.max_ticks = max_ticks
21        self.prev_time = time.perf_counter()
22        self.accumulator = 0
23        self.tick_count = -1
dt
max_ticks
prev_time
accumulator
tick_count
def tick(self):
25    def tick(self):
26        """tick once by adding the delta between prev frame and now"""
27        now = time.perf_counter()
28        frame_time = now - self.prev_time
29        self.prev_time = now
30        self.accumulator += frame_time
31        self.tick_count += 1

tick once by adding the delta between prev frame and now

def drain(self):
33    def drain(self):
34        """drain the accumulator. in main loop: for _ in clock.drain(): ..."""
35        n_ticks = 0
36        while self.accumulator >= self.dt and n_ticks < self.max_ticks:
37            yield
38            self.accumulator -= self.dt
39            n_ticks += 1
40        self.accumulator = min(self.accumulator, self.dt) # Drop residual debt instead of it piling up across frames

drain the accumulator. in main loop: for _ in clock.drain(): ...

def wait(self):
42    def wait(self):
43        """waits the leftover time in case the previous tick ran too fast to maintain consistent FPS"""
44        time.sleep(max(self.dt - (time.perf_counter() - self.prev_time), 0))

waits the leftover time in case the previous tick ran too fast to maintain consistent FPS

def wait_and_tick(self):
46    def wait_and_tick(self):
47        """calls wait() then tick(). Put this at the beginning of the main loop :)"""
48        self.wait()
49        self.tick()

calls wait() then tick(). Put this at the beginning of the main loop :)