10个版本

0.3.1 2023年11月28日
0.3.0 2023年11月28日
0.2.0 2023年2月15日
0.1.7 2023年2月12日
0.1.4 2023年1月25日

#203 in 游戏开发

30 monthly downloads
用于 2 crate

MIT 许可证

25KB
362

漫步者

辅助创建基于tick的游戏引擎的库。基于tick的游戏引擎是一种所有游戏逻辑都在固定时间间隔运行的引擎。这种引擎相比更传统的游戏引擎有几个优点。例如,很容易将游戏逻辑和渲染分开到不同的线程。因为你的游戏逻辑的间隔,或者它的TPS,可能和你渲染时的FPS不同,所以可以使用插值来保持一切平滑。

目录

漫步者组件

漫步者有几个主要组件

  • TickLoop
  • 快照(s)
  • 插值

TickLoop

tick循环是漫步者的核心;它在设定的tick速率(TPS)下运行所有代码。如果你的代码运行时间超过了tick间隔,tick循环将尽可能快地运行,直到赶上。

快照

每次监听器运行时,它都会生成一个快照。快照是表示在特定tick的游戏状态的表示。然后快照被放入Snapshots中,用于在tick循环之外在快照之间进行插值。通常建议尽可能少地将数据放入快照中,因为它们在内存中移动相当频繁。

插值

漫步者提供了插值数据的实用工具。主要提供了一个Interpolate特质和许多常见的插值器。Interpolate特质已经在标准库中的许多类型上实现,包括所有数字原语和包含它们的向量。还提供了一个Interpolate derive proc宏,以简化使用,当使用derive功能时。插值对于使你的引擎中的游戏看起来平滑非常重要。没有它,你的游戏看起来会很卡,尤其是在低TPS时。

用法

使用漫步者的第一步是创建一个TickLoop。最简单的方法是调用TickLoop::init,它为你做了一些设置。

let (tick_loop, event_sender, ctrl, snapshots) = TickLoop::init(
    listener: move |dt, events, ctrl, time| {
        // Your engine logic goes here
        // Note that this won't work because we aren't returning a snapshot yet.
        todo!()
    },
    tps: 60.0, // This can be any positive float.
);

这个函数接受许多输入并返回许多输出。让我们逐一介绍它们。

输入

  • listener:这是一个FnMut闭包,将在每个tick调用并返回你的快照类型。它接受4个参数
    • dt:自上次tick以来的时间,以秒为单位。
    • events:自上次tick以来发送给循环的事件向量。
    • ctrl:一个可以用来控制tick循环状态的TickLoopControl
    • time:当前时间,用于创建快照(它们需要存储创建时间)。
  • tps:循环的TPS。

输出

  • tick_loop:tick循环本身。
  • event_sender:一个可以用来向tick循环发送事件的Sender
  • ctrl:一个可以用来从循环外部控制tick循环状态的TickLoopControl
  • 快照:一个包含由tick循环生成的所有快照的Snapshots

解决这些问题后,让我们创建我们的快照类型。这是一个非常简单的示例,但你可以将所需的数据放入你的快照中。

#[derive(Debug, Interpolate)]
struct ExampleSnapshot {
    time: Instant,
    value: f64,
}
impl Snapshot for ExampleSnapshot {
    fn get_time(&self) -> &Instant {
        &self.time
    }
}

现在我们可以使用它了!

let mut value = 0.0;

let _ = TickLoop::init(
    move |dt, events, ctrl, time| {
        value = 1.0 - value;
        ExampleSnapshot {
            time,
            value,
        }
    },
    ExampleSnapshot {
            time: Instant::now(),
            value: 0.0,
        },
    60.0,
);

最后,我们可以启动我们的tick循环!

tick_loop.start(snapshots);

启动tick循环会阻塞线程直到它停止。因此,你可能想在运行之前将tick循环发送到单独的线程。

现在你有一个正在运行的tick循环!你可以使用event_sender向它发送事件,并使用ctrl来控制它。

依赖关系

~0–7MB
~42K SLoC