React-three实现3D游戏(七)-多人同屏
在一个基本的可交互的3D游戏中,人物和环境是不可或缺的元素。接下来,我们将逐步迭代我们的项目,使其更像一个真正的可交互的3D游戏。
3D模型在线预览提供多种低代码平台3D模型在线预览解决方案,实现了将多种3D模型格式无缝集成到低代码业务表单中。这意味着用户可以在不离开低代码平台的情况下,直接查看和操作3D模型,极大地提升了数据可视化的效果和用户交互体验。
多人同屏分析
上一次我们为游戏添加了小地图,但整个场景上依然只有一个机器人在孤单的游荡。是时候,添加些其他存在了。
为了让场景热闹起来,我们决定添加些其他玩家!这次我们将仅仅使用最简单的状态同步来实现多人同屏在线。
我们的场景中有2种玩家,一种是我们可以控制的,一种是被同步数据更新并驱动的玩家。这2种玩家的组件是有差异的。我们把自己控制的角色称为玩家,其他玩家控制的统一称为其他玩家。
玩家组件,只有一个角色模型且响应本地用户的输入,并且把输入同步到所有其他玩家的本地。
其他玩家组件,有复数的角色模型,而且不响应本地用户的任何输入,它响应各自玩家的输入。
我们需要一个中转站将本地玩家的信息同步到其他玩家的本地,并且获取其他玩家本地的状态信息。
服务
我们的重心不是服务端,所以我们选用nodejs来实现最简同步服务,它只有一个js文件。 需要安装的npm包只有express和 socket.io
我们只是简单的做个中转,把所有的socket消息转发给其他所有人。例如有人发了一条消息,服务端会广播给所有人,有人断开连接也会广播给所有人。
服务端可以进一步优化,例如按频道分发消息,这样玩家就可以创建和加入不同的频道等等。
但我认为这些优化可以以后再做,作为服务的起始阶段,这些已经足够了。
前端部分
连接socket
安装socket.io-client
包,初始化socket。
进入utils.tsx文件中初始并连接socket:
在config文件中中配置socket地址
然后将后端服务启动。前端刷新下,后端就会打印玩家的链接id和在线人数了。 在config文件中定义socket事件,和服务端保持一致一共有2个事件分别是玩家断开连接和玩家同步位置信息。
同步玩家数据
这一步我们将当前玩家的位置同步到socket服务端。 进入到models文件夹下的player.tsx文件 添加以下代码:
将当前的玩家位置同步到服务端。
获取其他玩家信息
在models下新建others文件夹,分别添加index.tsx和actor.tsx文件。其中actor.tsx是单个玩家的逻辑,index.tsx中会渲染出所有的其他玩家。
单个其他玩家组件
该组件根据socket同步到的其他玩家的位置信息,渲染其他玩家模型。
使用memo防止该组件被反复重复渲染。
使用SkeletonUtils.clone对模型进行克隆,防止引用模型导致多个玩家共用同一个模型。
在useFrame中对玩家位置同步更新,因为我们只能获得位置信息。所以通过位置的变化判断玩家当前的移动方向和运动状态,并进行旋转和播放相关动画。
这里我们使用线性插值(lerp)和球面线性插值(slerp)变化来分别进行更平滑的玩家移动和旋转。
其他玩家集合
我们初始化socket监听,监听socket推送的消息。并相应改变本地的players。这里我选择使用map对象来存放player,因为它存取性能较高。
当有新玩家加入时,判断该玩家id是否已经存在,存在则更新,否则以id为键新增玩家。 当有玩家离开时,直接删除id对应的玩家即可。
再将players循环渲染出所有的其他玩家。
这里我为了压缩socket的消息体积,事件使用的是数字。同步传参时的位置数据也是简单的数组包数字。同时使用toFixed函数处理浮点数默认保留3位。当前的id用的是服务端的socketid,它本身也比较长,可以替换掉。你可以在玩家上传时使用和生成一些较短的唯一id。