当前位置: 首页 > news >正文

Unity 2022 LTS + Photon Fusion 2:手把手教你搭建第一个多人联机Demo(含完整代码)

Unity 2022 LTS Photon Fusion 230分钟打造多人联机射击原型当你在Unity 2022 LTS中首次打开Photon Fusion 2的文档时可能会被那些状态同步、客户端预测等术语吓到。但别担心我们今天要做的不是理论研究而是直接动手创建一个能让你和好友实时互射小球的多人游戏原型。这个过程中你会自然理解Fusion的核心机制。1. 环境准备与基础配置在开始编码前我们需要确保开发环境正确配置。打开Unity 2022 LTS建议使用2022.3.9f1版本创建一个新的3D核心项目。关键配置步骤Photon账号注册访问Photon官网注册账号在Dashboard中创建新应用选择Fusion类型。记下生成的App ID这相当于连接Photon服务的通行证。项目设置调整在Edit Project Settings Editor中将Asset Serialization模式改为Force Text。这是Fusion生成网络代码的必要设置。SDK导入下载最新Fusion SDK后通过Assets Import Package Custom Package导入。导入完成后会自动弹出Fusion Hub向导粘贴之前获取的App ID。提示如果导入后出现Mono Cecil缺失错误可通过Package Manager添加com.unity.nuget.mono-cecil1.10.22. 搭建基础联机框架2.1 创建网络管理器首先创建一个空对象命名为NetworkManager并挂载新建的NetworkManager.cs脚本using Fusion; using UnityEngine; public class NetworkManager : MonoBehaviour, INetworkRunnerCallbacks { private NetworkRunner _runner; public async void StartGame(GameMode mode) { _runner gameObject.AddComponentNetworkRunner(); _runner.ProvideInput true; await _runner.StartGame(new StartGameArgs() { GameMode mode, SessionName QuickDemoRoom, SceneManager gameObject.AddComponentNetworkSceneManagerDefault() }); } // 实现INetworkRunnerCallbacks接口方法 public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) { /* 后续实现 */ } public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) { /* 后续实现 */ } // ... 其他回调方法暂留空 }2.2 添加简易UI控制为快速测试我们添加简单的GUI按钮private void OnGUI() { if (_runner null) { if (GUI.Button(new Rect(0,0,200,40), Host)) StartGame(GameMode.Host); if (GUI.Button(new Rect(0,40,200,40), Join)) StartGame(GameMode.Client); } }此时运行游戏点击Host/Join按钮应该能看到控制台日志但还没有实际游戏内容。3. 实现玩家角色与移动3.1 创建玩家预制体新建空对象命名为PlayerPrefab添加组件NetworkObject核心网络标识NetworkCharacterController带预测的移动控制添加子对象Capsule作为视觉表现关键配置确保NetworkObject的Player Object选项勾选移除子对象上的碰撞体使用父对象的CharacterController3.2 玩家移动脚本创建PlayerMovement.csusing Fusion; using UnityEngine; public class PlayerMovement : NetworkBehaviour { private NetworkCharacterController _cc; [Networked] private Angle _yaw { get; set; } private void Awake() { _cc GetComponentNetworkCharacterController(); } public override void FixedUpdateNetwork() { if (GetInput(out NetworkInputData input)) { Vector3 moveDir Vector3.zero; if (input.IsDown(NetworkInputData.BUTTON_FORWARD)) moveDir Vector3.forward; if (input.IsDown(NetworkInputData.BUTTON_BACK)) moveDir Vector3.back; if (input.IsDown(NetworkInputData.BUTTON_LEFT)) moveDir Vector3.left; if (input.IsDown(NetworkInputData.BUTTON_RIGHT)) moveDir Vector3.right; _cc.Move(5 * transform.rotation * moveDir * Runner.DeltaTime); if (input.IsDown(NetworkInputData.BUTTON_FIRE)) GetComponentPlayerShooter().Fire(); } } }3.3 输入系统配置创建NetworkInputData.cs定义输入结构using Fusion; using UnityEngine; public struct NetworkInputData : INetworkInput { public const int BUTTON_FORWARD 1; public const int BUTTON_BACK 1 1; public const int BUTTON_LEFT 1 2; public const int BUTTON_RIGHT 1 3; public const int BUTTON_FIRE 1 4; public NetworkButtons buttons; }在NetworkManager中实现输入收集public void OnInput(NetworkRunner runner, NetworkInput input) { var data new NetworkInputData(); data.buttons.Set(NetworkInputData.BUTTON_FORWARD, Input.GetKey(KeyCode.W)); data.buttons.Set(NetworkInputData.BUTTON_BACK, Input.GetKey(KeyCode.S)); data.buttons.Set(NetworkInputData.BUTTON_LEFT, Input.GetKey(KeyCode.A)); data.buttons.Set(NetworkInputData.BUTTON_RIGHT, Input.GetKey(KeyCode.D)); data.buttons.Set(NetworkInputData.BUTTON_FIRE, Input.GetMouseButton(0)); input.Set(data); }4. 实现射击系统4.1 创建子弹预制体新建Sphere对象命名为BulletPrefab添加组件NetworkObjectNetworkTransform同步位置新建Bullet.cs脚本Bullet.cs核心代码using Fusion; using UnityEngine; public class Bullet : NetworkBehaviour { [Networked] private TickTimer life { get; set; } [Networked] private Vector3 direction { get; set; } public void Init(Vector3 fireDirection) { direction fireDirection; life TickTimer.CreateFromSeconds(Runner, 2f); } public override void FixedUpdateNetwork() { if (life.Expired(Runner)) { Runner.Despawn(Object); return; } transform.position direction * 10 * Runner.DeltaTime; } }4.2 玩家射击脚本创建PlayerShooter.csusing Fusion; using UnityEngine; public class PlayerShooter : NetworkBehaviour { [SerializeField] private NetworkPrefabRef bulletPrefab; [Networked] private TickTimer cooldown { get; set; } public void Fire() { if (cooldown.ExpiredOrNotRunning(Runner)) { cooldown TickTimer.CreateFromSeconds(Runner, 0.3f); Runner.Spawn(bulletPrefab, transform.position transform.forward, Quaternion.LookRotation(transform.forward), Object.InputAuthority, (runner, obj) { obj.GetComponentBullet().Init(transform.forward); }); } } }5. 玩家生成与同步回到NetworkManager完善玩家生成逻辑[SerializeField] private NetworkPrefabRef playerPrefab; private DictionaryPlayerRef, NetworkObject spawnedPlayers new DictionaryPlayerRef, NetworkObject(); public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) { if (runner.IsServer) { Vector3 spawnPos new Vector3((player.RawEncoded % 10) * 2 - 10, 0, 0); NetworkObject playerObj runner.Spawn(playerPrefab, spawnPos, Quaternion.identity, player); spawnedPlayers.Add(player, playerObj); } } public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) { if (spawnedPlayers.TryGetValue(player, out NetworkObject playerObj)) { runner.Despawn(playerObj); spawnedPlayers.Remove(player); } }6. 场景布置与最终试创建地面Plane对象为玩家和子弹添加简单材质区分确保所有预制体都已拖入Resources文件夹在NetworkManager中分配playerPrefab和bulletPrefab引用测试流程构建项目并运行两个实例一个实例点击Host另一个点击Join使用WASD移动鼠标左键射击观察子弹的同步效果和碰撞7. 性能优化技巧网络压缩在NetworkRunner组件上调整_runner.Config.Compression NetworkProjectConfig.CompressionType.LZ4;插值设置对移动对象调整NetworkTransformGetComponentNetworkTransform().InterpolationDataSource InterpolationDataSources.Snapshots;带宽优化在NetworkInputData中使用位域压缩public byte inputByte; // 每位代表一个按键状态这个原型虽然简单但已经包含了Fusion最核心的功能模块。你可以在此基础上继续扩展添加命中检测与分数系统实现房间列表与匹配机制加入更复杂的物理交互优化网络预测算法参数
http://www.zskr.cn/news/1374417.html

相关文章:

  • 告别硬编码!在UE Niagara中创建可复用的自定义模块库(以动态力场为例)
  • 拉格朗日平衡传播:动态系统的梯度估计新方法
  • TinyML模型压缩实战:SHAP特征选择与非结构化剪枝优化边缘AI检测
  • 时间序列预测实战:从LightGBM到GNN与强化学习的算法选型指南
  • vczh_toys Linq库进阶:复杂数据处理的8个实用案例指南
  • vue-axios-github实战:从零开始掌握前端登录拦截与路由守卫核心技术
  • 初识递归算法
  • 如何快速部署PostgreSQL数据建模工具:跨平台完整安装教程
  • vue-axios-github解密:5分钟理解axios拦截器实现请求/响应统一处理
  • Linux服务器升级OpenSSL 3.2.0后,为什么我的curl命令不能用了?一个软链接引发的‘血案’
  • 如何快速为你的爱车添加自动驾驶:openpilot完整实战指南
  • 专业演讲利器:Pympress双屏PDF演示工具深度解析
  • 3个必知技巧:用Obsidian日历插件打造高效笔记时间线
  • 告别音乐平台切换:开源音源聚合方案如何重塑你的听歌体验
  • 终极工作价值评估指南:如何科学计算你的工作性价比
  • 5分钟快速上手labelCloud:免费开源的3D点云标注终极指南
  • ComfyUI自动完成功能终极指南:如何提升AI绘画提示词效率300%
  • Atomic Layout嵌套布局最佳实践:构建复杂UI系统的完整指南
  • 幻兽帕鲁 - 服务器模组安装完全指南
  • 如何高效配置Wan2.2-I2V-A14B图像转视频模型:从环境搭建到生产部署的完整指南
  • 7天掌握OpenRocket:从零打造专业级火箭设计与仿真实战手册
  • InternAgent深度解析:如何构建长期自主科学发现系统的10个核心技术
  • 怎样高效开发Windows文件系统:WinFsp实战指南深度解析
  • 探索DeepPurpose预训练模型:10分钟实现SARS-CoV-3CL蛋白酶抑制剂虚拟筛选
  • June论坛系统:5分钟快速搭建Python Flask社区平台的终极指南
  • GHelper终极指南:轻量级华硕笔记本控制工具完整教程
  • Forge中的上下文压缩:处理长对话的高效方法
  • AI Agent的节能与绿色计算:优化计算资源消耗的算法与策略
  • 3步快速上手:终极AI图像增强工具Real-ESRGAN完全指南
  • AI Agent Harness Engineering 生态系统:基础设施、工具与应用层