optic_loop/runtime.rs
1use crate::Game;
2
3/// The lifecycle trait for game logic.
4///
5/// Implement this trait to hook your application into the engine's event
6/// loop. Pass an instance to [`Game::new`] or [`Game::run`] and the engine
7/// drives the three lifecycle methods automatically.
8///
9/// | Method | Called | Purpose |
10/// |---|---|---|
11/// | [`start`](Runtime::start) | Once, before the first frame | One-time setup |
12/// | [`update`](Runtime::update) | Every frame | Input, logic, rendering |
13/// | [`end`](Runtime::end) | Once, on shutdown | Cleanup |
14///
15/// Each method receives `&mut Game`, giving you access to the renderer,
16/// camera, window, events, and time.
17///
18/// # Example
19///
20/// ```ignore
21/// use optic_loop::{Game, Runtime};
22///
23/// struct FpsCounter {
24/// frames: u64,
25/// }
26///
27/// impl Runtime for FpsCounter {
28/// fn start(&mut self, _game: &mut Game) {
29/// println!("Starting...");
30/// }
31///
32/// fn update(&mut self, game: &mut Game) {
33/// self.frames += 1;
34/// if self.frames % 60 == 0 {
35/// let fps = game.time.fps();
36/// println!("FPS: {:.1}", fps);
37/// }
38/// game.renderer.clear();
39/// // ... draw scene ...
40/// }
41///
42/// fn end(&mut self, _game: &mut Game) {
43/// println!("Rendered {} frames total", self.frames);
44/// }
45/// }
46///
47/// Game::run(FpsCounter { frames: 0 });
48/// ```
49///
50/// # Why three methods?
51///
52/// Separating `start` from `update` avoids re-initialising assets every
53/// frame. Separating `end` from `drop` gives you a predictable point to
54/// save state before the engine tears down subsystems.
55pub trait Runtime {
56 /// Called once before the first frame.
57 ///
58 /// Use this for one-time initialisation:
59 ///
60 /// - Load meshes, textures, and shaders via [`GPU::ship_*`]
61 /// - Set up initial game state
62 /// - Connect to a server (see [`Game::enable_networking`])
63 ///
64 /// The window is not yet visible when `start` runs. The engine calls
65 /// `start`, makes the window visible, then immediately enters the
66 /// update loop.
67 fn start(&mut self, game: &mut Game);
68
69 /// Called every frame.
70 ///
71 /// This is the main game loop body. Do all per-frame work here:
72 ///
73 /// 1. **Poll input** — check `game.events` for keyboard, mouse, gamepad
74 /// 2. **Update logic** — move entities, run physics, check collisions
75 /// 3. **Render** — clear the screen, bind shaders, draw meshes
76 ///
77 /// Use [`Game::exit`] to stop the loop from within `update`.
78 fn update(&mut self, game: &mut Game);
79
80 /// Called once on shutdown.
81 ///
82 /// Triggered by [`Game::exit`], window close, or `SIGINT`. Use this to:
83 ///
84 /// - Save persistent state (scores, settings)
85 /// - Disconnect from servers
86 /// - Release non-GPU resources
87 ///
88 /// The GPU and window are still alive during `end`; they are destroyed
89 /// after it returns.
90 fn end(&mut self, game: &mut Game);
91}