官方案例 Demo Boxes
红色玩家.png
蓝色玩家.png
知识点:
1.服务器的连接
2.服务器的连接的成功和失败的处理
3.玩家盒子的同步,每个客户端有属于自己颜色的盒子
4.服务器和客户端的一些常用的回调
5.单击改变盒子颜色,双击销毁自己创建的盒子,双击销毁其他客户端创建的盒子无效
6.GUI的工具栏,按钮,提示文本的绘制
7.文本提示闪烁切换效果的实现
8.俩种Sharder的模式
DemoBoxes.png
Sharder.png
//----------------ClickInstantiate.cs--------------------//
using UnityEngine;
///
/// 点击平面实例化要同步的方块
///
public class ClickInstantiate : MonoBehaviour
{
///
/// 要实例化的预制体
///
public GameObject prefab;
///
/// 标记平面的类型
///
public int InstantiateType;
///
/// 实例化方块的类型
///
private string[] instantiateTypeNames = new string[] { "我的", "房主专属" };
///
/// 是否显示GUI提示
///
public bool IsShowGUI;
private void OnGUI()
{
if (IsShowGUI)
{
//使用GUI显示一个工具栏
GUILayout.BeginArea(new Rect(Screen.width - 180f, 0, 180f,50f));
InstantiateType = GUILayout.Toolbar(InstantiateType, instantiateTypeNames);
GUILayout.EndArea();
}
}
///
/// 点击平面的方法
/// 该方法是由 PhotomServer 的 UtilityScripts 的 InputToEvent和PointedAtGameObjectInfo通过SendMessage调用
///
private void OnClick()
{
//玩家不在房间内,直接返回,不执行下面的代码
if (!PhotonNetwork.inRoom)
{
return;
}
switch (InstantiateType)
{
case 0:
//实例化预制体,所有客户端均可调用
PhotonNetwork.Instantiate(prefab.name, InputToEvent.inputHitPos + new Vector3(0, 5f, 0), Quaternion.identity, 0);
break;
case 1:
//只有当前房主才能调用的方法,且只有房主才能销毁
PhotonNetwork.InstantiateSceneObject(prefab.name, InputToEvent.inputHitPos + new Vector3(0f, 5f, 0), Quaternion.identity, 0, null);
break;
default:
break;
}
}
}
//-----------------知识点------------------//
// GUIBeginArea(new Rect(Screen.width-180f,0,180f,50f)); 开始绘制区域
// int instantiateType = GUILayout.Toolbar(int selected,string[] names); 绘制一个工具栏
// GUIEndArea(); 结束绘制
// bool res = PhotonNetwork.inRoom 玩家是否在房间内
//------------------ColorPerPlayer.cs--------------------//
using ExitGames.UtilityScripts;
using UnityEngine;
using Photon;
///
/// PunBehaviour是一个定义了各种事件的回调,你都可以重写他们 给玩家的提示文本选择一种颜色
///
public class ColorPerPlayer : PunBehaviour
{
///
/// 颜色的数组
///
public Color[] colors = new Color[] { Color.red,Color.blue,Color.yellow,Color.green};
///
/// 是否显示颜色的文本
///
public bool ShowColorLabel;
///
/// 颜色文本的区域
///
public Rect ColorLabelArea = new Rect(0, 50, 100, 50);
public Texture2D img;
///
/// 默认的颜色
///
public Color MyColor = Color.gray;
///
/// 标识颜色
///
public bool ColorPicked { get; set; }
///
/// 是否已经初始化
///
private bool isInitlized;
public void Reset()
{
this.MyColor = Color.gray;
this.ColorPicked = false;
}
private void OnEnable()
{
if(isInitlized==false)
{
Init();
}
}
private void Start()
{
if(isInitlized==false)
{
Init();
}
}
private void OnGUI()
{
//如果标识符为false,则不绘制GUI
if(this.ColorPicked==false || this.ShowColorLabel==false)
{
return;
}
GUILayout.BeginArea(ColorLabelArea);
GUILayout.BeginHorizontal();
//绘制图片加文本提示 可以使用Texture2d类型的图片
Color c = GUI.color;
GUI.color = this.MyColor;
GUILayout.Label(this.img);
GUI.color = c;
//绘制文字提示,提示是否是房主
string playerNote = PhotonNetwork.isMasterClient ? "是你的颜色\n你是牛逼的房主" : "你是加入这个房间的成员";
GUILayout.Label(playerNote);
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
private void OnDisable()
{
PlayerRoomIndexing.instance.OnRoomIndexingChanged -= Refresh;
}
///
/// 初始化
///
private void Init()
{
if(!isInitlized && PlayerRoomIndexing.instance != null)
{
PlayerRoomIndexing.instance.OnRoomIndexingChanged += Refresh;
isInitlized = true;
}
}
///
/// 刷新房间颜色标识的事件
///
private void Refresh()
{
int _Index = PhotonNetwork.player.GetRoomIndex();
if(_Index==-1)
{
this.Reset();
}
else
{
this.MyColor = colors[_Index];
this.ColorPicked = true;
}
}
///
/// 当玩家加入房间
///
public override void OnJoinedRoom()
{
if(isInitlized==false)
{
Init();
}
}
///
/// 当玩家离开房间
///
public override void OnLeftRoom()
{
this.Reset();
}
}
//---------------------知识点--------------------//
// 继承自PunBehaviour,她又继承了Unity的MonoBehaviour,而且可以重写回调事件
//建议管理的脚本继承自此类
//绘制图片+文本的GUI提示
// GUIBeginArea(Rect rect);
// GUIBeginHorizontal();
// GUI.color();
// GUILayout.Label(Texture2d img);
// GUILayout.Label(string text);
// GUIEndHorizontal();
// GUIEndArea();
//ExitGames.UtilityScripts.PlayerRoomIndexing.instance.OnRoomIndexingChanged 客户端房间索引改变事件(每一个客户端都有一个不唯一的房间索引)
//OnJoinedRoom(); 玩家加入房间回调
//OnLeftRoom(); 玩家离开房间回调
//---------------------ColorPerPlayerApply.cs-----------------------//
using ExitGames.UtilityScripts;
using Photon;
using UnityEngine;
///
/// 给玩家的物体选择一种颜色
///
public class ColorPerPlayerApply : PunBehaviour
{
///
/// ColorPerPlayer脚本的引用
///
public static ColorPerPlayer colorPerPlayerCache;
///
/// 渲染组件
///
private Renderer rendererComponent;
///
/// 渲染组件
///
private bool isInitlized;
private void Awake()
{
rendererComponent = GetComponent();
if(colorPerPlayerCache==null)
{
colorPerPlayerCache = FindObjectOfType() as ColorPerPlayer;
}
if(colorPerPlayerCache==null)
{
enabled = false;
}
if(photonView.isSceneView)
{
enabled = false;
}
}
private void OnEnable()
{
if(!isInitlized)
{
Init();
}
}
private void Start()
{
if (!isInitlized)
{
Init();
}
}
private void OnDestroy()
{
isInitlized = false;
if(PlayerRoomIndexing.instance != null)
{
PlayerRoomIndexing.instance.OnRoomIndexingChanged -= ApplyColor;
}
}
///
/// 当PhotonServer实例化的时候,调用此函数
///
///
public override void OnPhotonInstantiate(PhotonMessageInfo info)
{
this.ApplyColor();
}
///
/// 初始化,当房间索引改变的时候,也就是我们每创建一个玩家(房间),ID就要增加1
/// 也就是有四个玩家,房间ID分别是 0 1 2 3
///
private void Init()
{
if(isInitlized==false && PlayerRoomIndexing.instance!=null)
{
PlayerRoomIndexing.instance.OnRoomIndexingChanged += ApplyColor;
isInitlized = true;
}
}
///
/// 应用颜色
///
public void ApplyColor()
{
if (photonView.owner == null)
{
return;
}
int _Index = photonView.owner.GetRoomIndex();
Debug.Log("房间ID: " + _Index);
if(_Index>=0 && _Index<=colorPerPlayerCache.colors.Length)
{
//这里我们只是定义了四种颜色,然后上面对房间索引ID做了一层限制
rendererComponent.material.color = colorPerPlayerCache.colors[_Index];
}
}
}
//-------------------知识点-----------------//
// OnPhotonInstantiate(PhotonMessageInfo info); 实例化Photon回调,可以用来做每个客户端的初始化
// PhotonPlayer pp = photoView.owner; 得到客户端玩家的信息
// photonView.onwer.GetRoomIndex(); 得到当前客户端玩家的房间ID(每个客户端都是唯一的)
//--------------------ConnectAndJoinRandom.cs----------------------//
using UnityEngine;
///
/// 连接和加入随机房间
///
///
public class ConnectAndJoinRandom : Photon.MonoBehaviour
{
///
/// 是否自动连接服务器
///
public bool AutoConnect = true;
///
/// 游戏版本号,可加可不加
///
public byte Version = 1;
///
/// 是否在Update函数里连接
///
private bool ConnectInUpdate = true;
public virtual void Start()
{
//设置自动加入大厅为false
PhotonNetwork.autoJoinLobby = false;
}
public virtual void Update()
{
//如果服务器没有连接的情况下,就连接服务器
//每一帧都要检测,然后只要断开就会自动重连
if(ConnectInUpdate==true && AutoConnect==true && !PhotonNetwork.connected)
{
Debug.Log("开始连接到服务器");
ConnectInUpdate = false;
//连接服务器使用 PhotoNetwork.ConnectUsingSetting(string gameVersion) 进行连接
//其实就是使用Resources文件夹下的那个.assets的文件
PhotonNetwork.ConnectUsingSettings(Version + "." + SceneManagerHelper.ActiveSceneBuildIndex);
}
}
///
/// 成功连接到服务器的回调
///
public virtual void OnConnectedToMaster()
{
Debug.Log("已连接到服务器,尝试加入一个随机房间");
//加入一个随机的房间
PhotonNetwork.JoinRandomRoom();
}
///
/// 加入大厅的回调
///
public virtual void OnJoinedLobby()
{
Debug.Log("加入大厅功能,尝试加入玩家大厅");
PhotonNetwork.JoinRandomRoom();
}
///
/// 加入随机房间失败的回调
///
public virtual void OnPhotonRandomJoinFailed()
{
Debug.Log("加入房间失败,可能是没有房间,那我们自己创建一个房间");
//因为加入随机房间的失败的原因只能是服务器没有房间
//所以我们这里就直接创建一个房间
PhotonNetwork.CreateRoom(null, new RoomOptions() { MaxPlayers = 4 }, null);
}
///
/// 连接服务器失败的回调
///
///
public virtual void OnFailedToConnectToPhoton(DisconnectCause cause)
{
Debug.Log("连接服务器错误: " + cause);
}
///
/// 新玩家加入房间回调
///
public void OnJoinedRoom()
{
Debug.Log("一个玩家加入了房间");
}
}
//----------------------知识点---------------------//
//该脚本继承自Photon.MonoBehaviour类 这个类什么都没有,适合作为扩展类,自己去写虚方法
//而且这些虚方法要保证方法名正确才能够被调用
//如果不进行较大的扩展,建议继承自PonBehaviour类
// PhotonNetwork.autoJoinLobby = false; //设置是否自动加入大厅
// bool res = PhotonNetwork.connected //判断服务器是否是连接状态
// PhotonNetwork.ConnectUsingSettings(string gameVersion); //使用Resources文件夹下的PhotonServerSettings文件连接服务器)
// gameVersion参数可以不填写,也可以加上其他字符
// OnConnectedToMaster(); 成功连接到服务器回调
// OnJoinedLobby(); 成功加入大厅的回调
// OnJoinedRoom(); 新玩家加入房间回调
// OnPhotonRandomJoinFailed(); 加入随机房间失败回调
// OnFailedToConnectToPhoton(DisconnectCause cause) 连接服务器失败的回调 DisconnectCause 是一个枚举类 我们直接Switch就行
// PhotonNetwork.JoinRandomRoom(); 随机加入一个房间
// PhotonNetwork.CreateRoom(string roomName, RoomOptions roomOptions, TypedLobby typedLobby); 创建一个房间,typeLobby可为空
//------------------DemoBoxesGUI.cs---------------------//
//------------------此脚本实现了一个提示的轮滚的效果,我们可以运用在以后的项目中----------------//
using System.Collections;
using UnityEngine.UI;
using UnityEngine;
///
/// 游戏标题的切换的动画效果
///
public class DemoBoxesGUI : MonoBehaviour
{
///
/// 是否隐藏此效果
///
public bool HideUI;
///
/// 文本提示UI的引用
///
public Text TextTips;
///
/// 当前提示的索引,默认为0
///
private int tipsIndex;
///
/// 我们要提示的文本内容
///
private readonly string[] tips = new string[]
{
"随着空中的光影画面逐渐模糊,直至消散,也宣告了这段被封禁的记忆,已经尽数归入轩辕婉蓉的脑海中。",
"她整张脸失去了血色,苍白得吓人,双手微微颤抖着,呆愣在原地。",
"“妈的,这些畜生!”段七德为那群人所做之事感到无比震怒,大肆叫嚣“老子要盗空他们的宝库,以儆效尤!”",
"“算本神尊一份!”二狗子当即喊道。",
"“滚,没你份!”段七德怒瞪二狗子。",
"“哎哟我擦尼玛,本神尊行侠仗义碍你什么事了?”二狗子强势硬怼。",
"这一人一麒麟就在原地干瞪起眼来。",
"徐缺懒得掺和,目光好奇的落在轩辕婉蓉身上,打量着她。",
"从踏入这个记忆世界,遇到数万年前的这个轩辕婉蓉时,徐缺就感觉这女人比数万年后还要冷漠无情,但对她谈不上厌恶,当然也谈不上有好感。",
"他也知道轩辕婉蓉的身世挺可怜的,父母死后连魂魄都被拘谨在天宫院后山,可一直不知道究竟具体发生了什么。",
"如今亲眼目睹这段回忆,徐缺心里十分的别扭,也充满了愧疚。",
"他突然想起来,当初仗着自己救出轩辕婉蓉的父母魂魄,于是在轩辕婉蓉面前开了她父母的玩笑,毕竟是皮惯了,贱惯了,不作一下不得劲。",
"谁曾想那一个玩笑,轩辕婉蓉竟在那一瞬要对他下死手,完全就超脱了徐缺的底线。",
};
///
/// 每一个提示显示的时间
///
private const float TimePerTip = 3.0f;
///
/// 计时器
///
private float TimeSinceLastTip;
///
/// Alpha的渐变速度
///
private const float FadeSpeedForTip = 0.05f;
private void Update()
{
if (this.TextTips == null)
{
return;
}
//3s为间隔,累积时间
this.TimeSinceLastTip += Time.deltaTime;
if(this.TimeSinceLastTip > TimePerTip)
{
this.TimeSinceLastTip = 0;
StartCoroutine(SwapTip());
}
}
///
/// 改变提示文本的内容
///
///
private IEnumerator SwapTip()
{
//透明渐变为0
float alpha = 1.0f;
while (alpha > 0.0f)
{
alpha -= FadeSpeedForTip;
this.TimeSinceLastTip = 0;
this.TextTips.color = new Color(this.TextTips.color.r, this.TextTips.color.r, this.TextTips.color.r, alpha);
yield return null;
}
//设置文本提示
this.tipsIndex = (this.tipsIndex + 1) % tips.Length;
this.TextTips.text = tips[tipsIndex];
//透明度渐变为1
while (alpha < 1.0f)
{
alpha += FadeSpeedForTip;
this.TimeSinceLastTip = 0;
this.TextTips.color = new Color(this.TextTips.color.r, this.TextTips.color.r, this.TextTips.color.r, alpha);
yield return null;
}
}
private void OnGUI()
{
if(HideUI==true)
{
return;
}
GUILayout.BeginArea(new Rect(0,0,300,Screen.height));
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
if(!PhotonNetwork.connected)
{
if (GUILayout.Button("连接",GUILayout.Width(100)))
{
PhotonNetwork.ConnectUsingSettings(null);
}
}
else
{
if (GUILayout.Button("断开", GUILayout.Width(100)))
{
PhotonNetwork.Disconnect();
}
}
//绘制当前连接的状态
//PhotonNetwork.connectionStateDetailed 表示的是当前客户端的连接状态
//会返回一个枚举类型 ClientState
GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString());
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
}
//----------------知识点----------------//
// GUILayout.FiexibleSpace(); //四角对齐
// GUILayout.Button("buttoname",GUI.Width(float width)); 绘制一个按钮,一般是结合上面的 if 语句进行使用
// PhotonNetwork,connectionStateDatailed.ToString(); 得到服务器的连接状态的详细信息
//PhotonNetwork.Disconnect(); 断开服务器连接
//---------------------OnAwakePhysicsSetting.cs------------------------//
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 初始化物理设置
/// RequireComponent 这个特性的功能是当前在给游戏物体添加这个脚本时,会自动添加组价PhotonView
///
[RequireComponent(typeof(PhotonView))]
public class OnAwakePhysicsSetting : Photon.MonoBehaviour
{
private void Awake()
{
//得到要同步的游戏物体身上的3d或2d刚体
if(photonView.isMine==false)
{
//尝试得到一个3d刚体并设置动力学
Rigidbody rigidbody = GetComponent();
if(rigidbody!=null)
{
rigidbody.isKinematic = true;
}
else
{
//如果3D刚体为null,则尝试的得到2d的刚体并设置动力学
Rigidbody2D rigidbody2D = GetComponent();
if(rigidbody2D != null)
{
rigidbody2D.isKinematic = true;
}
}
}
}
}
//---------------------知识点---------------------//
//不管是继承自 Photon.MonoBehaviour 还是 PonBehaviour 都有一个photonView的属性
// bool res = photonView.isMine; 确定当前客户端是否是自己
//-------------------OnClickFlashRpc.cs---------------------//
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 点击方块的时候实现闪烁的效果
/// 所有放在游戏物体的脚本都要继承子Photon.MonoBehaviour,他说UnityEngine.MonoBehaviour进一步的封装
///
public class OnClickFlashRpc : Photon.MonoBehaviour
{
///
/// 当前方块的材质球
///
private Material originMat;
///
/// 初始的颜色,因为我们这里的效果是闪烁
///
private Color originCol;
///
/// 是否在闪烁
///
private bool isFlashing;
///
/// 这个方法同样和ClickInstantiate的方法一样,只不过点击的物体是方块
/// 该方法是由 PhotomServer 的 UtilityScripts 的 InputToEvent和PointedAtGameObjectInfo通过SendMessage调用
///
private void OnClick()
{
//目前此方法看来适用于协程
photonView.RPC("Flash", PhotonTargets.All);
}
[PunRPC]
private IEnumerator Flash()
{
//如果在闪烁,则返回
if(isFlashing)
{
yield break;
}
isFlashing = true;
//这边通过改变材质球的颜色实现一个闪烁的效果
this.originMat = GetComponent().material;
if(this.originMat.HasProperty("_Emission")==false)
{
Debug.Log("没有发现 _Emission 的属性,请检查材质球");
yield break;
}
//设置材质球的颜色颜色
this.originCol = this.originMat.GetColor("_Emission");
this.originMat.SetColor("_Emission", Color.green);
//通过插值运算回到之前的颜色
for (float i = 0.0f; i < 1.0f; i+=0.08f)
{
Color lerpCol = Color.Lerp(Color.green, this.originCol, i);
this.originMat.SetColor("_Emission", lerpCol);
yield return null;
}
//完全校正材质球的颜色
this.originMat.SetColor("_Emission",originCol);
//标识符设置为false
isFlashing = false;
}
}
//---------------------知识点---------------------//
// photonView.RPC("Flash", PhotonTargets.All); 目前看来是只适合执行一个协程
// 要执行的协程必须带有 ******** [PunRPC] ********* 这个特性
// Shrarder的类型是之前图片的VertextLit 可以运用到方块上,选择具体的贴图 ,她有一个 _Emission的属性可以修改颜色
//----------------OnDoubleclickDestroy.cs-----------------//
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 双击销毁游戏物体
///
public class OnDoubleclickDestroy : Photon.MonoBehaviour
{
private float timeOfLastClick;
private float clickDeltaForDoubleClick = 0.2f;
///
/// 此方法同之前的OnClick,同属于同一游戏物体,所以该游戏物体被点击都会调用
///
private void OnClick()
{
//确保点击的方块是自己创建的
if(photonView.isMine == false)
{
return;
}
//双击的一个方法,个人感觉很巧妙,我们可以运用到项目中
if(Time.time - timeOfLastClick < clickDeltaForDoubleClick)
{
PhotonNetwork.Destroy(this.gameObject);
}
else
{
this.timeOfLastClick = Time.time;
}
}
}
//------------------知识点----------------//
// 此脚本中有一个 双击检测点击的方法,我们可以运用到项目中