Game Loop

Intent: Decouple the progression of game time from user input and processor speed.

Acron Mapping

Acron’s ShowBase runs the game loop for you. Do not write a manual while True loop.

Loop concept

Acron equivalent

Main loop

ShowBase.run() — started automatically; calls taskMgr.step() each frame

Frame update

Tasks added with taskMgr.add(fn, 'name')

Delta time

globalClock.getDt() or task.dt inside a task function

Fixed timestep

Accumulator pattern implemented inside a single task

Frame rate cap

globalClock.setFrameRate(60) or sync-video true in Config.prc

Pause

taskMgr.remove('name') or freeze globalClock

How to Implement

  1. Subclass ShowBase. Call super().__init__() — this starts the loop.

  2. Register per-frame logic as tasks, not inline code: taskMgr.add(self.update, 'gameUpdate').

  3. Always use globalClock.getDt() (or task.dt) for time-based movement. Never hard-code pixel-per-frame values.

  4. If you need a fixed timestep (physics, deterministic simulation), implement an accumulator inside one task.

Code Sketch (Python)

from direct.showbase.ShowBase import ShowBase

FIXED_DT = 1 / 60  # 60 Hz physics tick

class Game(ShowBase):
    def __init__(self):
        super().__init__()
        self._accumulator = 0.0
        taskMgr.add(self._variable_update, 'variableUpdate', sort=10)
        taskMgr.add(self._fixed_update,    'fixedUpdate',    sort=5)

    def _variable_update(self, task):
        dt = globalClock.getDt()
        # Render-rate logic: camera, particles, animations
        self._update_camera(dt)
        return task.cont

    def _fixed_update(self, task):
        dt = globalClock.getDt()
        self._accumulator += dt
        while self._accumulator >= FIXED_DT:
            self._physics_step(FIXED_DT)
            self._accumulator -= FIXED_DT
        return task.cont

    def _physics_step(self, dt):
        # Deterministic physics — always called with FIXED_DT
        pass

    def _update_camera(self, dt):
        pass

game = Game()
game.run()  # starts the loop; blocks until window is closed

Pitfalls

  • Never time.sleep() inside a task: it blocks the entire loop including rendering and input. Use taskMgr.doMethodLater(seconds, fn, 'name') for deferred logic.

  • getDt() spike on first frame: on the very first frame getDt() may be large. Clamp it: dt = min(globalClock.getDt(), 0.05) inside the fixed-update accumulator.

  • Task sort order: Acron runs tasks in ascending sort order. Physics (sort=5) should run before rendering helpers (sort=10) and after input (sort=0). Use explicit sort values rather than relying on insertion order.

  • Variable timestep is non-deterministic: the book warns that using a raw variable dt directly in physics produces results that differ between fast and slow machines due to floating-point rounding — a particular problem for networked games. Use the fixed-timestep accumulator (shown above) for any simulation that must be deterministic or stable.