#rng #rand #hash #gamedev #random-seed #game

froggy-rand

为游戏提供的无状态随机数生成

3 个不稳定版本

0.2.1 2022 年 6 月 6 日
0.2.0 2022 年 1 月 23 日
0.1.0 2022 年 1 月 8 日

#2333算法

MIT 许可证

11KB
92

为游戏提供的无状态随机数生成。

关于伪随机数生成器(RNG)的一种思考方式是将其视为由初始种子定义的数字列表。每次我们生成一个随机数时,都会递增这个列表的索引。假设你正在构建一个让用户可以分享种子的类似地牢的游戏。一个设计目标可能是保持地图生成在更新之间的一致性。使用传统的 RNG 时,你必须非常小心地添加额外的 RNG 调用,因为这样做会影响之后所有 RNG 调用。

这个库通过从 RNG 中移除可变状态来解决这个问题。


以一个具体的例子来说,假设我们正在填充一个关卡中的敌人。

let mut rng = ExampleRng::from_seed(seed);
let mut enemies = vec![];
for id in 0..100 {
  let x = rng.next();
  let y = rng.next();
  enemies.push(Enemy::new(id, x, y));
}

现在假设在更新中我们想要增加多样性,所以给敌人随机选择武器。我们可能这样做:

let mut rng = ExampleRng::from_seed(seed);
let mut enemies = vec![];
for id in 0..100 {
  let x = rng.next();
  let y = rng.next();
  let weapon_type = rng.next();
  enemies.push(Enemy::new(id, x, y, weapon_type));
}

然而,我们刚刚改变了所有敌人(除了第一个)的位置!一个解决方案是为基于初始种子生成的武器类型初始化一个新的随机数生成器,但这会很麻烦。


另一种方法可能是接受“随机数列表”的视角,并将有状态的 RNG 转换为一个索引函数。

fn random(seed : SeedValue, i : usize) -> RandomValue
{ ... }

但这将要求用户在某个地方显式地跟踪索引。 FroggyRand 使用两阶段方法,首先从其输入参数生成一个哈希值。然后它与种子结合生成一个索引。

以下是如何使用 FroggyRand 与上述示例的示例:

let froggy_rand = FroggyRand::new(seed);
let mut enemies = vec![];
for id in 0..100 {
  // We want the x position to be based on two things:
  //   The hash of the string "enemy_x" to make sure its different to the y value
  //   The enemy id
  let x = froggy_rand.gen(("enemy_x", id));
  let y = froggy_rand.gen(("enemy_y", id));
  let weapon_type = froggy_rand.gen(("weapon_type", id));
  enemies.push(Enemy::new(id, x, y, weapon_type));
}

现在我们可以添加尽可能多的新参数,而不会相互影响。

更详细的信息请参见 这个讲座。

依赖项

~770KB