PhotonServer插件教程

官方案例 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;
        }
    }
}

//------------------知识点----------------//
// 此脚本中有一个 双击检测点击的方法,我们可以运用到项目中

你可能感兴趣的:(PhotonServer插件教程)