AnvilKitAnvilKit

anvilkit-app

游戏应用运行器 — 事件循环、帧生命周期、GameCallbacks

anvilkit-app 消除了应用程序的样板代码。游戏只需实现 GameCallbacks trait 并调用 AnvilKitApp::run(),即可获得完整的事件循环,包含输入转发、帧更新和窗口管理。

底层的 Appbevy_app::App(不是自定义实现)。GameCallbacks 是游戏的主要入口点。game_ctx!() 宏提供了在回调中快速访问常用上下文字段的方式。

注意: anvilkit-ecs 已不存在。调度、ECS 插件系统和状态机现在位于 anvilkit-app 中。

GameConfig

传递给 AnvilKitApp::run() 的配置结构体。提供了合理的默认值;只需覆盖你需要的部分。

字段类型默认值描述
titleString窗口标题(必填)
widthu321280初始窗口宽度(像素)
heightu32720初始窗口高度(像素)
vsyncbooltrue启用垂直同步
raw_mouse_inputbooltrue使用原始鼠标输入(DeviceEvent),适用于 FPS 风格的摄像机

构造器与建造器

use anvilkit_app::GameConfig;

// Minimal — uses all defaults
let config = GameConfig::new("My Game");

// Override window size
let config = GameConfig::new("My Game").with_size(1920, 1080);

GameCallbacks

游戏实现的主要 trait。所有方法都有默认的空实现,因此你只需覆盖需要的方法。

pub trait GameCallbacks {
    /// Called once after the window is created and the renderer is ready.
    fn init(&mut self, ctx: &mut GameContext) {}

    /// Called every frame after ECS update (app.update()) completes.
    fn post_update(&mut self, ctx: &mut GameContext) {}

    /// Called on RedrawRequested — issue draw calls here.
    fn render(&mut self, ctx: &mut GameContext) {}

    /// Called when the window is resized.
    fn on_resize(&mut self, ctx: &mut GameContext, width: u32, height: u32) {}

    /// Called for every window event. Return `true` to consume the event
    /// and prevent further processing.
    fn on_window_event(&mut self, ctx: &mut GameContext, event: &WindowEvent) -> bool {
        false
    }
}

GameContext

在回调中提供对 ECS AppRenderApp 的访问。

字段类型描述
app&mut AppECS 应用程序(世界、资源、系统)
render_app&mut RenderApp渲染端应用程序(窗口、GPU 状态)
方法签名描述
world(&self) -> &World对 ECS 世界的不可变访问
world_mut(&mut self) -> &mut World对 ECS 世界的可变访问

AnvilKitApp

应用运行器。一个静态方法,阻塞调用线程直到窗口关闭。

use anvilkit_app::{AnvilKitApp, GameConfig};

AnvilKitApp::run(config, app, game);
方法签名描述
run(config: GameConfig, app: App, game: impl GameCallbacks)启动事件循环。阻塞直到窗口关闭。

WindowSize

一个 ECS 资源(Resource),用于跟踪当前窗口尺寸。在窗口调整大小事件时自动更新。

字段类型描述
widthf32当前窗口宽度(逻辑像素)
heightf32当前窗口高度(逻辑像素)
方法签名描述
new(width: f32, height: f32) -> Self从指定尺寸创建
aspect_ratio(&self) -> f32返回 width / height
as_tuple(&self) -> (f32, f32)返回 (width, height)
fn camera_system(window: Res<WindowSize>) {
    let aspect = window.aspect_ratio();
    let (w, h) = window.as_tuple();
}

帧生命周期

下图展示了单帧内事件的执行顺序:

Resumed
  └─► init()                          (仅一次,在首次恢复时)

WindowEvent
  └─► on_window_event()
  └─► forward_input()                 (键盘、鼠标、滚轮)

AboutToWait
  └─► tick()
        ├─ DeltaTime 更新
        ├─ app.update()                (ECS 系统运行)
        ├─ post_update()
        └─ InputState::end_frame()

RedrawRequested
  └─► render()

Resized
  └─► on_resize()
  └─► WindowSize 资源更新

要点:

  • init 在窗口和渲染器就绪后仅触发一次。
  • forward_input 自动将操作系统事件转换为 InputState
  • post_update 在所有 ECS 系统运行之后执行,适合在渲染前读取查询结果。
  • WindowSizeon_resize 被调用之前更新,因此回调始终能看到新的尺寸。

最小示例

use anvilkit::prelude::*;
use anvilkit_app::{AnvilKitApp, GameConfig, GameCallbacks, GameContext};
use anvilkit_render::renderer::canvas2d::{Canvas2D, Canvas2DRenderer};

struct MyGame { renderer: Option<Canvas2DRenderer> }

impl GameCallbacks for MyGame {
    fn init(&mut self, ctx: &mut GameContext) {
        let device = ctx.render_app.render_device().unwrap();
        let format = ctx.render_app.surface_format().unwrap();
        self.renderer = Some(Canvas2DRenderer::new(device, format));
    }

    fn render(&mut self, ctx: &mut GameContext) {
        let Some(ref mut r) = self.renderer else { return };
        let Some(mut c) = Canvas2D::begin(ctx.render_app, r) else { return };
        c.clear([0.2, 0.2, 0.3, 1.0]);
        c.draw_rect(100.0, 100.0, 200.0, 50.0, [1.0, 0.5, 0.0, 1.0]);
        c.draw_text(110.0, 115.0, "Hello AnvilKit!", 24.0, [1.0, 1.0, 1.0, 1.0]);
        c.finish();
    }
}

fn main() {
    let mut app = App::new();
    app.add_plugins(DefaultPlugins::new().with_window(
        WindowConfig::new().with_title("Hello").with_size(640, 480),
    ));
    AnvilKitApp::run(GameConfig::new("Hello").with_size(640, 480), app, MyGame { renderer: None });
}

目录