【Unity工具,简单应用】Photon + PUN 2,做一个简单多人在线聊天室
【Unity工具,简单应用】Photon + PUN 2,做一个简单多人聊天室
- 前置知识,安装,及简单UI
- 大厅
- 聊天室
- 简单同步
- 较复杂同步
- 自定义同步
- 最终效果
前置知识,安装,及简单UI
- 【Unity工具,简单学习】PUN 2,多人在线游戏开发,初步使用
- 需要有一定 UNITY 使用经验的开发者可以顺利阅读。
大厅
- 简单搭建一下大厅UI。
给Laucher
节点一个Launcher
脚本
Launcher
脚本如下,具体功能看注释
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Photon.Pun;
using Photon.Realtime;public class Launcher : MonoBehaviourPunCallbacks
{public string RoomName = "Room"; // 设置房间名,确保每次连接到同一个房间private string gameVersion = "1";[SerializeField]private const byte maxPlayersPerRoom = 4; // 设置单房间最多玩家数[SerializeField]private GameObject controlPanel;[SerializeField]private GameObject progressLabel;void Awake(){// Then let master server can use PhotonNetwork.LoadLevel()// Everyone will see the same levelPhotonNetwork.AutomaticallySyncScene = true; // 确保该变量为 true,否则无法同步progressLabel.SetActive(false);controlPanel.SetActive(true);}public void Connect(){progressLabel.SetActive(true);controlPanel.SetActive(false);if (PhotonNetwork.IsConnected) // 若已连接,则直接加入到房间{PhotonNetwork.JoinOrCreateRoom(RoomName, new RoomOptions() { MaxPlayers = maxPlayersPerRoom }, default);}else{PhotonNetwork.ConnectUsingSettings(); // 用 PhotonServerSettings 来连接PhotonNetwork.GameVersion = gameVersion;}}public override void OnJoinRandomFailed(short returnCode, string message) // 若加入随机房间失败{Debug.Log("PUN Basics Tutorial/Launcher:OnJoinRandomFailed() was called by PUN. No random room available, so we create one.\nCalling: PhotonNetwork.CreateRoom");// #Critical: we failed to join a random room, maybe none exists or they are all full. No worries, we create a new room.PhotonNetwork.CreateRoom(RoomName, new RoomOptions() { MaxPlayers = maxPlayersPerRoom });}public override void OnJoinedRoom() // 若加入了某房间,则加载聊天室场景,不要使用 UNITY的加载场景方法{Debug.Log("PUN Basics Tutorial/Launcher: OnJoinedRoom() called by PUN. Now this client is in a room.");PhotonNetwork.LoadLevel("ChatingRoom");}public override void OnConnectedToMaster() // 运行ConnectUsingSettings()后会先连接到 Master节点,再创建或加载房间{Debug.Log("PUN Basics Tutorial/Launcher: OnConnectedToMaster() was called by PUN");PhotonNetwork.JoinOrCreateRoom(RoomName, new RoomOptions() { MaxPlayers = maxPlayersPerRoom }, default);}public override void OnDisconnected(DisconnectCause cause) // 若失去连接后{progressLabel.SetActive(false);controlPanel.SetActive(true);Debug.LogWarningFormat("PUN Basics Tutorial/Launcher: OnDisconnected() was called by PUN with reason {0}", cause);}}
- 需要注意的是
PhotonNetwork.JoinOrCreateRoom(RoomName, new RoomOptions() { MaxPlayers = maxPlayersPerRoom }, default);
方法,若该房间名的房间不存在则创建,否则加载该房间。 - 在输入框中,把该字符串赋值给
PhotonNetwork.NickName
即表示该玩家的名称,需非空
PhotonNetwork.NickName = defaultName;
聊天室
- 聊天室的UI如下
添加一个GameManagerPUN
的物体并给予它对应的脚本组件
GameManagerPUN
脚本如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Photon.Pun;
using Photon.Realtime;
public class GameManagerPUN : MonoBehaviourPunCallbacks
{public string DialogueText = "DialogueText";[SerializeField]private InputField _inputField;public GameObject Content;void Start(){GameObject go = PhotonNetwork.Instantiate("Sphere", Vector3.zero, Quaternion.identity);go.GetComponent<ChangeRandomPosition>().change(); // 用 PhotonNetwork.Instantiate 创建一个物体,让它的位置随机变一下,代表显示该房间内的玩家个数}public override void OnLeftRoom(){SceneManager.LoadScene("LobbyScene");}public void LeaveRoom() // 退出按钮的监听器直接调用该代码即可调用 OnLeftRoom() 方法,退出大厅{PhotonNetwork.LeaveRoom();}public override void OnPlayerEnteredRoom(Player other) // 自动监听是否有玩家进入{Debug.LogFormat("OnPlayerEnteredRoom() {0}", other.NickName); // not seen if you're the player connectingif (PhotonNetwork.IsMasterClient){Debug.LogFormat("OnPlayerEnteredRoom IsMasterClient {0}", PhotonNetwork.IsMasterClient); // called before OnPlayerLeftRoom}}public override void OnPlayerLeftRoom(Player other) // 自动监听是否有玩家退出{Debug.LogFormat("OnPlayerLeftRoom() {0}", other.NickName); // seen when other disconnectsif (PhotonNetwork.IsMasterClient){Debug.LogFormat("OnPlayerLeftRoom IsMasterClient {0}", PhotonNetwork.IsMasterClient); // called before OnPlayerLeftRoom}}public void SendMessage() // 发送消息,记录发送消息者名称,与它发送的消息,然后创建一个UI物体,加载到滚动content中{string res = PhotonNetwork.NickName + " : " + _inputField.text;GameObject obj = PhotonNetwork.Instantiate(DialogueText, Vector3.zero, Quaternion.identity);obj.GetComponent<Text>().text = res;obj.GetComponent<SentenceAsync>().str = res;obj.transform.SetParent(Content.transform);_inputField.text = ""; // 发送玩后清空输入框}
}
- 同步文本信息脚本
SentenceAsync
如下,需要实现IPunObservable
接口的OnPhotonSerializeView
方法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using UnityEngine.UI;
public class SentenceAsync : MonoBehaviourPunCallbacks, IPunObservable
{public string str;public GameManagerPUN gm;public GameObject pa;void IPunObservable.OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info){if (stream.IsWriting){stream.SendNext(str);}else{str = (string)stream.ReceiveNext();if (pa == null) // 用于在不同客户端的同步处理{pa = GameObject.Find("Content");this.gameObject.transform.SetParent(pa.transform);this.GetComponent<Text>().text = str;}}}
}
- 没错,该同步脚本纠结了我好几个小时,才成功处理对。为什么这么麻烦呢?
简单同步
- 同步的基本原理比较复杂, 所幸该
Photon + Pun2
给我们封装的差不多了,我们只需要知道基础的内容即可 - 简单来说,假设有客户端
A,B,C
,他们都连接服务器X
在客户端A
处,玩家创建了一个球,若只是普通创建,则只有A
处的玩家能看到该球。
若希望每个客户端都能看到该球,首先需要使用PhotonNetwork.Instantiate(str, Vector3, Quaternion)
方法进行同步创建。注意创建物体通过给定它的字符串str
,该预设体的路径必须在该PhotonUnityNetworking / Resources
文件夹下:
- 第二步,指定该物体需要同步什么信息。对于该球,我们只需要同步它的位置信息即可。
物体若需要同步信息,则必须创建Photon View
脚本。
物体若需要同步信息,则必须创建Photon View
脚本。
物体若需要同步信息,则必须创建Photon View
脚本。
重要的信息重复三次
物体若需要同步transform
信息,则直接给它Photon Transform View
脚本即可
- 这里,
Ownership
表示该物体的所有权是否转移,比如A
创建了该物体,是否允许B
更改它的信息。这里还设计到主机与从机的区别。但这里我们不更改别的信息,设置 fixed` 即可 - 这里
Synchronization
选择Unreliable On Change
即可,Observable Search
选择同步的观察信息,可以直接选择Auto Find Active
或者设置Manual
然后手动给予它需要同步信息的脚本即可。 - 能被观察到的脚本必须实现
IPunObservable
接口,实现IPunObservable.OnPhotonSerializeView
方法。
较复杂同步
- 我们搜索一下已经有的脚本,发现官方直接支持我们如下的同步脚本:
- 也就是说,若需要同步的观察信息只有
Animator, Rigidbody, Transform
那么可以直接挂载相应的脚本进行同步。 - 那如果我们需要同步某数值信息,比如
int, float, string
呢?该三种信息同步相应来说比较容易
仍然,我们需要实现IPunObservable
接口,实现IPunObservable.OnPhotonSerializeView
方法
void IPunObservable.OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info){if (stream.IsWriting) // 若流正在写,我们发送接下来要同步的信息 string str{stream.SendNext(str);}else{str = (string)stream.ReceiveNext(); // 接受流的下一个输出,并转化为 (string) 类型if (pa == null) // (1){pa = GameObject.Find("Content");this.gameObject.transform.SetParent(pa.transform);this.GetComponent<Text>().text = str;}}}
- 我们发现,其他脚本都很好懂,那么
(1)
处是什么意思呢?
这里每行对话物体,由于挂载在Canvas
中,且挂载Text
组件,所以必须为Rect Transform
组件
尽管Rect Transform
组件是继承自Transform
组件,但是它的信息与Transform
组件截然不同,所以官方提供的Photon Transform View
不能使用(你挂载就会出bug,可以F12查看该脚本原码) - 难道我们去实现能同步
RectTransform
或者Text
信息的脚本?写起来有点麻烦…
但是我们转念一想,我们需要同步的信息只有 发送信息的字符串而已
所以我们只同步字符串。由于PhotonNetwork.Instantiate()
方法已经为我们创建了该同步物体,所以别的组件都是创建出来的,只是其中的信息没有同步而已 - 我们还需要同步哪个信息?
RectTransform?
不需要,由于我们使用ScrollView
,自动处理它的各种坐标信息
Text?
我们只需给予其Text.text = str
即可,其他的字体啥信息都默认即可
还有一个隐藏的需要同步的信息 —— 该物体的父对象
。父对象由于我们没有同步,所以它默认会创建在世界根节点的下面,不会显示在ScrollView
下。
由于该ScrollView
下面的那些默认物体是每个玩家一开始就是一样的,所以我们只需要单纯通过(1)
指定,把自己挂载到该父对象上即可。
自定义同步
- 若需要自定义同步,由于
stream.ReceiveNext()
强转类型只支持默认的那三个int, float, string
,所以你想同步比如说Rect Transform
或者XXXscript
脚本,就需要去里面注册某物体类型 - 很麻烦,不怎么推荐,相当于指定某个类型的序列化和反序列化的规则,可以自己去搜集信息。
最终效果
- 我和室友,使用的是两台笔记本的两个客户端,在里面进行聊天
- 经过测试发现,若某玩家退出后,由该
owner
通过PhotonNetwork.Instantiate
创建的同步物体会自动Destroy
。猜测可以通过转移所有权的方式保留聊天记录。
相关文章:

【Unity工具,简单应用】Photon + PUN 2,做一个简单多人在线聊天室
【Unity工具,简单应用】Photon PUN 2,做一个简单多人聊天室前置知识,安装,及简单UI大厅聊天室简单同步较复杂同步自定义同步最终效果前置知识,安装,及简单UI 【Unity工具,简单学习】PUN 2&…...

程序员增加收入实战 让小伙伴们都加个鸡腿
文章目录前言1️⃣一、发外包平台💁🏻♂️二、朋友介绍✍️三、打造自己的个人IP👋🏿四、混群拉单🤳🏿五、面试拉单💻六、技术顾问🦴七、开发个人项目总结:前言 程序员…...

GPIO四种输入和四种输出模式
GPIO的结构图如下所示: 最右端为I/O引脚,左端的器件位于芯片内部。I/O引脚并联了两个用于保护的二极管。 输入模式 从I/O引脚进来就遇到了两个开关和电阻,与VDD相连的为上拉电阻,与VSS相连的为下拉电阻。再连接到TTL施密特触发…...

ChatGPT能够改变时代吗?一点点思考
都知道ChatGPT的出现对整个世界产生了剧烈的影响,前不久出的ChatGPT4更是在ChatGPT3.5的基础上展现了更强的功能。比如说同一个问题,ChatGPT3.5还是乱答的,ChatGPT4已经能给出正确解了。当然这只能说明技术是进步的。 虽然如此,很…...
Markdown如何使用详细教程
目录 一、Markdown 标题 二、Markdown 段落 三、Markdown 字体 四、Markdown 分隔线 五、Markdown 列表 六、Markdown 引用 七、Markdown 代码 八、Markdown 链接 九、Markdown 图片 十、Markdown 表格 前言 当前许多网站都广泛使用 Markdown 来撰写博客,…...

HTML5庆祝生日蛋糕烟花特效
HTML5庆祝生日蛋糕烟花特效 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>HTML5 Birthday Cake Fireworks</title><style>canvas {position: absolute;top: 0;left: 0;z-index: -1;}</style> </h…...

算法套路四——反转链表
算法套路四——反转链表 算法示例一:LeetCode206. 反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 初始化pre为空,cur为头指针 pre指针:记录当前结点的前一个结点 cur指针:记录当…...

多线程 (六) wait和notify
🎉🎉🎉点进来你就是我的人了 博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔🦾&am…...

React--》状态管理工具—Mobx的讲解与使用
目录 Mobx的讲解与使用 Mobx环境配置 Mobx的基本使用 Mobx计算属性的使用 Mobx监听属性的使用 Mobx处理异步的使用 Mobx的模块化 Mobx的讲解与使用 Mobx是一个可以和React良好配合的集中状态管理工具,mobx和react的关系相当于vuex和vue之间的关系࿰…...

有效的括号长按键入验证外星语词典字符的最短距离用栈实现队列
有效的括号来源:杭哥20. 有效的括号 - 力扣(LeetCode)bool isValid(char * s) {int szstrlen(s);char stack[sz];int k0;for (int i0;i<sz;i){if (s[i]( || s[i][ || s[i]{){stack[k]s[i];}else{if (k0){return false;}else if (s[i]} &am…...
《前端开发者的进阶之路》
前端作为Web开发的重要领域之一,不断地发展和演变着。除了基本的HTML、CSS、JavaScript技能,前端开发者需要掌握更多的进阶知识才能应对不断变化的需求。本文将介绍一些前端的进阶知识,帮助前端开发者进一步提高自己的技能水平。1.框架和库在…...

为什么说网络安全是风口行业?是IT行业最后的红利?
前言 “没有网络安全就没有国家安全”。当前,网络安全已被提升到国家战略的高度,成为影响国家安全、社会稳定至关重要的因素之一。 网络安全行业特点 1、就业薪资非常高,涨薪快 2021年猎聘网发布网络安全行业就业薪资行业最高人均33.77万&…...
使用shell 脚本,批量解压一批zip文件,解压后的文件放在以原zip文件名前10个字符的文件夹中的例子
#!/bin/bash for file in *.zip dofolder$(echo $file | cut -c 1-10)mkdir $folderunzip -q $file -d $folder doneecho "All zip files have been extracted." # 说明: # 1. for循环遍历当前目录下的所有zip文件 # 2. 使用cut命令提取zip文件名前10个字…...

01 | Msyql系统架构
目录MySQL系统架构连接器查询缓存分析器优化器执行器MySQL系统架构 大体来说,MySQL分为Server层和引擎层两部分。 Server层包含链接器、查询缓存、分析器、优化器和执行器,而引擎层负责的是数据的存储和读取,支持InnoDB、Myisam、Memory等多…...
Linux命令---设备管理
Linux setleds命令Linux setleds命令用来设定键盘上方三个 LED 的状态。在 Linux 中,每一个虚拟主控台都有独立的设定。语法setleds [-v] [-L] [-D] [-F] [{|-}num] [{|-}caps] [{|-}scroll]参数:-F:预设的选项,设定虚拟主控台的状…...

前端入门:HTML5+CSS3+JAAVASCRIPT
1、 初识HTML HTML:Hyper Text Markup Language(超文本标记语言) 。 超文本包括:文字、图片、音频、视频、动画等。 1.1、W3C标准 1.2、HTML基本结构 示例: <!-- DOCTYPE:告诉浏览器,我们要使用什么规划,这里是HTML --> …...

【头歌实验】课外作业一:开通ECS及使用Linux命令
文章目录一、完成下列实验并截图二、简要回答“课堂考核”内容三、在头歌、华为云或阿里云官网上,找出自己的课外学习资源,制定小组的课程学习计划、专业学习计划。四、习题1.10一、完成下列实验并截图 1、实验《ECS云服务器新手上路》 https://develo…...

CMSIS-RTOS2 RTX5移植到GD32L233
1、CMSIS-RTOS2是什么? 关于CMSIS-RTOS2的官方描述如下: CMSIS-RTOS v2 (CMSIS-RTOS2) 为基于 Arm Cortex 处理器的设备提供通用 RTOS 接口。它为需要RTOS功能的软件组件提供了一个标准化的API,因此为用户和软件行业带…...

[网络原理] 网络中的基本概念
人生,本就是苦乐参半,这样的生活才是丰富多彩. 文章目录前言1. IP地址2. 端口号3. 协议4. 五元组5. 协议分层6. OSI七层模型7. TCP/IP协议8. 封装和分用9. 客户端与服务端10. 请求与响应前言 本章开始,我们开启网络部分的知识大门. 1. IP地址 1.定义: IP地址主要用于表示网络…...

BeanPostProcessor原理分析
文章目录一、BeanPostProcessor的作用1. 源码2. 使用案例二、Spring生命周期中的BeanPostProcessor三、BeanPostProcessor对PostConstruct的支持四、BeanPostProcessor中的顺序性五、总结一、BeanPostProcessor的作用 BeanPostProcessor提供了初始化前后回调的方法,…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...