静态的元宇宙是没有任何生气的,真实世界的角色们总是处于不断的移动中,在我们分治的基于区域和临近角色列表的世界中,角色因为移动在区域之间穿梭,在别人的视野中出现和消失。
虚拟世界中的移动是一个很复杂的问题,特别是涉及到客户端和服务器端同步的时候,因为网络延时会带来 lag,为了让客户端看起来移动平滑,人们提出了很多补偿和平滑的方案,我们暂时不考虑这些复杂的方案,采取一个最基本的原则,每次同步的时候,同步服务器角色当前的位置和目标,这样只要我们的进入和离开消息准确,角色最终的位置,服务器和客户端应该是一致的。
为了方便计算,二维世界的移动可以分解为 x 和 y 方向上的分量,我们定义 Movement 结构如下:
#[derive(Debug, Clone)]
pub struct Movement {
pub x: f32,
pub y: f32,
pub x_speed: f32,
pub y_speed: f32,
}
复制代码
其中 x,y 是移动目标的坐标。
在经过一个特定的时间间隔之后,我们需要更新角色的位置
fn step(&mut self, mills: f32) {
self.pos.x += self.action.x_speed * mills;
if self.action.x_speed > 0.0 && self.pos.x > self.action.x { self.pos.x = self.action.x; }
else if self.action.x_speed < 0.0 && self.pos.x < self.action.x { self.pos.x = self.action.x; }
self.pos.y += self.action.y_speed * mills;
if self.action.y_speed > 0.0 && self.pos.y > self.action.y { self.pos.y = self.action.y; }
else if self.action.y_speed < 0.0 && self.pos.y < self.action.y { self.pos.y = self.action.y; }
if self.action.x == self.pos.x { self.action.x_speed = 0.0 }
if self.action.y == self.pos.y { self.action.y_speed = 0.0 }
}
复制代码
mills 是 经历的毫秒数,如果移动已经到了目的地,我们需要设置速度为 0.0
整个 World 需要为每个角色执行 一次上述 step 函数
let changes = self.roles.iter_mut().fold(Vec::new(), |mut v, (id, role)| {
if role.is_moving() {
let (old_region, _) = role.pos.center_quadrant(region_size);
role.step(mills);
let (new_region, _) = role.pos.center_quadrant(region_size);
if old_region != new_region {
v.push((*id, old_region, new_region));
}
}
v
});
复制代码
比较异动前后的 region,发现有改变 区域的角色,放入列表中。
最后完成实际区域切换的工作
for change in changes {
self.regions.get_mut(&change.1).unwrap().roles.remove(&change.0);
self.enter_region(change.0, change.2);
}
复制代码
World 需要定期执行 遍历所有角色的 step 函数,今后可能包括宠物和 Boss ,甚至人工智能控制的 NPC
评论