Game Loop¶
The game loop is the core component that drives your game's execution. It manages the timing of updates and rendering, ensuring consistent gameplay regardless of the system's performance.
GameLoop¶
class GameLoop:
def __init__(
self,
update_func: Callable[[float], None],
render_func: Callable[[], None],
config: Optional[GameLoopConfig] = None
) -> None
Parameters¶
update_func: Function called each frame to update game state. Takes delta time in seconds.render_func: Function called each frame to render the game.config: Optional configuration for the game loop.
Methods¶
run()¶
Start the game loop. This will run untilstop() is called.
stop()¶
Stop the game loop.run_one_frame()¶
Process a single frame of the game loop.Properties¶
average_fps¶
Get the current average FPS over the lastfps_sample_size frames.
running¶
Check if the game loop is currently running.GameLoopConfig¶
Configuration options for the game loop.
@dataclass
class GameLoopConfig:
fps: int = 60
fixed_time_step: float = 1.0 / 60.0
max_frame_time: float = 0.25
fps_sample_size: int = 60
Fields¶
fps: Target frames per second (default: 60)fixed_time_step: Time step for physics/fixed updates (default: 1/60 second)max_frame_time: Maximum time to process in a single frame (default: 0.25 seconds)fps_sample_size: Number of frames to sample for FPS calculation (default: 60)
PerformanceMetrics¶
Performance monitoring data for the game loop.
@dataclass
class PerformanceMetrics:
fps: float = 0.0
frame_time: float = 0.0
min_frame_time: float = float("inf")
max_frame_time: float = 0.0
avg_frame_time: float = 0.0
fixed_update_time: float = 0.0
update_time: float = 0.0
render_time: float = 0.0
idle_time: float = 0.0
Fields¶
fps: Current frames per secondframe_time: Time taken for the last framemin_frame_time: Minimum frame time recordedmax_frame_time: Maximum frame time recordedavg_frame_time: Average frame timefixed_update_time: Time spent in fixed updateupdate_time: Time spent in updaterender_time: Time spent renderingidle_time: Time spent idle
Example Usage¶
from src.core.game_loop import GameLoop, GameLoopConfig
# Create configuration
config = GameLoopConfig(
fps=60,
fixed_time_step=1/60,
max_frame_time=0.25
)
# Define update and render functions
def update(dt: float) -> None:
# Update game state
pass
def render() -> None:
# Render game
pass
# Create and run game loop
game_loop = GameLoop(update, render, config)
game_loop.run()
Best Practices¶
-
Fixed Time Step: Use the fixed time step for physics and gameplay logic that needs to be consistent.
-
Frame Independent Movement: Always multiply movement by delta time:
-
Performance Monitoring: Monitor the performance metrics to identify bottlenecks:
-
Maximum Frame Time: Set a reasonable
max_frame_timeto prevent the "spiral of death" when the game falls behind.
Common Issues¶
Low FPS¶
- Check for expensive operations in the update or render functions
- Consider using sprite batching
- Profile the game using the performance metrics
Inconsistent Physics¶
- Ensure physics calculations are done in the fixed time step
- Don't modify physics state directly in the render function
- Use interpolation for smooth rendering between physics updates
High CPU Usage¶
- Use frame limiting when appropriate
- Don't perform unnecessary calculations each frame
- Consider using spatial partitioning for large numbers of objects