PUN ☀️九、玩家网络实例化与昵称显示

简介: PUN ☀️九、玩家网络实例化与昵称显示

🟥 目标

1️⃣ 玩家网络实例化

即把玩家要操控的角色生成出来。GameManager里面Start时实例化角色。


2️⃣ 实现战斗数据不重置

原先只有两个人,再加入一个人,会切换到3人的场景。但原先的两个人当前战斗数据会被重置,这是不合理的。

我们来让之前的玩家数据不被重置。

a、PlayerManager 声明的静态字段LocalPlayerInstance 记录本地玩家实例化的。

Awake赋值,并在加载新场景时,不销毁当前角色。

b、但在重新加载新场景时,GameManager 脚本 Start时会再次实例化角色,于是一个玩家就有了两个角色。所以GameManager Start时根据判断 LocalPlayerInstance 是否为空,确定是否已经实例化了角色。


3️⃣ 位置重置

若从3人房切换到2人房,房间变小了。若切换后原先在地面上的角色悬空了,会出Bug。

所以增加判断:若切换后没踩在地板上,则位置回到出生位置(原点高空)。

方法:PlayerManager 脚本Start时增加加载完场景时的委托,增加的委托事件判断当前角色位置


4️⃣ 激光碰撞判断

解决当跳跃时触发激光,会造成自身减血的现象。

这是因为激光与自身角色控制器的触发器检测碰撞了,我们要检测激光不是接触的自身才行,避免自己碰撞掉血。解决办法:

  • 设置自身的Tag,触发检测Tag再决定是否减血。
  • PlayerManager脚本判断射线碰撞到的角色是否是自己,不是的话再减血(此处采用)
if (other.GetComponentInParent<PhotonView>().IsMine)
    return;
 
Health -= 0.1f;

现在我们可以测试看看,如果发现连接不上,就换成中国区Appid试试,我隔了一天申请通过了,现在你申请的应该也好了。


🟧 代码编写

🚩 PlayerManager

using UnityEngine;
using Photon.Pun;
 
public class PlayerManager : MonoBehaviourPunCallbacks, IPunObservable
{
    #region Parameters
 
    public GameObject beams;
 
    //记录本地玩家实例化
    public static GameObject LocalPlayerInstance;
 
    public float Health = 1f;
 
    //当用户开火时,为True
    bool IsFiring;
 
    #endregion
 
 
    #region Mono CallBacks
 
    void Awake()
    {
        beams.SetActive(false);
 
        if (PhotonNetwork.IsConnected == true && photonView.IsMine)
            LocalPlayerInstance = gameObject;
 
        DontDestroyOnLoad(gameObject);
    }
 
    void Start()
    {
        //当重新加载场景时,检测改脚本所赋值的角色是否踩在空中,若是,则回到出生点重新降落。
        UnityEngine.SceneManagement.SceneManager.sceneLoaded += OnSceneLoaded;
 
        if (PhotonNetwork.IsConnected && photonView.IsMine)
            GetComponent<Skode_CameraWork>().OnStartFollowing();
    }
 
    void Update()
    {
        if (Health <= 0f)
            GameManager.ins.Skode_LeaveRoom();
 
        if (PhotonNetwork.IsConnected == true && photonView.IsMine)
            ProcessInputs();
 
        if (IsFiring != beams.activeSelf)
            beams.SetActive(IsFiring);
    }
 
    void OnTriggerEnter(Collider other)
    {
        if (!photonView.IsMine && !other.CompareTag("beam"))
            return;
 
        Health -= 0.1f;
    }
 
    void OnTriggerStay(Collider other)
    {
        if (!photonView.IsMine && !other.CompareTag("beam"))
            return;
 
        //乘以增量时间,防止因为帧率FPS不同,扣血不同(举例:不乘,每帧执行一次扣血,卡的人比流畅的人扣血少)
        Health -= 0.1f * Time.deltaTime;
    }
 
    #endregion
 
 
    #region PUN CallBacks
 
    public override void OnDisable()
    {
        base.OnDisable();
        UnityEngine.SceneManagement.SceneManager.sceneLoaded -= OnSceneLoaded;
    }
 
    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting)
        {
            // 我们拥有这个角色:把我们的数据发送给其他人
            stream.SendNext(IsFiring);
            stream.SendNext(Health);
        }
        else
        {
            // 网络角色,接收数据
            IsFiring = (bool)stream.ReceiveNext();
            Health = (float)stream.ReceiveNext();
        }
    }
 
    #endregion
 
 
    #region Private Methods
 
    void OnSceneLoaded(UnityEngine.SceneManagement.Scene scene, UnityEngine.SceneManagement.LoadSceneMode loadingMode)
    {
        CalledOnLevelWasLoaded(scene.buildIndex);
    }
 
    void CalledOnLevelWasLoaded(int level)
    {
        // 检查我们是否在竞技场外面,如果是这样,移动到竞技场中心的安全地带
        if (!Physics.Raycast(transform.position, -Vector3.up, 5f))
        {
            transform.position = new Vector3(0f, 5f, 0f);
        }
    }
 
    void ProcessInputs()
    {
        //鼠标左键
        if (Input.GetButtonDown("Fire1"))
            IsFiring = true;
 
        if (Input.GetButtonUp("Fire1"))
            IsFiring = false;
    }
 
    #endregion
}


🚩 GameManager

using UnityEngine.SceneManagement;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
 
public class GameManager : MonoBehaviourPunCallbacks
{
    public static GameManager ins;
 
    [Tooltip("用来代表玩家的预置体")]
    public GameObject playerPrefab;
 
 
    #region Mono CallBacks
 
    private void Awake()
    {
        ins = this;
    }
 
    private void Start()
    {
        //若本地玩家第一次进游戏场景,还未生成角色,则生成一个。
        //且这个角色通过使用PhotonNetwork.Instantiate进行同步
        if (PlayerManager.LocalPlayerInstance == null)
            PhotonNetwork.Instantiate(playerPrefab.name, new Vector3(0f, 5f, 0f), Quaternion.identity, 0);
    }
 
    #endregion
 
 
    #region Public Methods
 
    //离开服务器房间Btn
    public void Skode_LeaveRoom()
    {
        //不允许主客户端离开房间
        if (!PhotonNetwork.IsMasterClient)
            PhotonNetwork.LeaveRoom();
    }
 
    #endregion
 
 
    #region PUN Callbacks
 
    /// <summary>
    /// 本地玩家离开服务器房间时回调
    /// </summary>
    public override void OnLeftRoom()
    {
        // 加载大厅场景
        SceneManager.LoadScene(0);
    }
 
    /// <summary>
    /// 其他玩家连接房间时的回调(不是自己)
    /// </summary>
    public override void OnPlayerEnteredRoom(Player other)
    {
        print(other.NickName + "    加入房间");
 
        //根据当前玩家的人数,主客户端加载对应场景
        if (PhotonNetwork.IsMasterClient)
            LoadArena();
    }
 
    //当玩家离开房间或变得不活动时调用。
    public override void OnPlayerLeftRoom(Player other)
    {
        print(other.NickName + "    离开了房间");
 
        //根据当前玩家的人数,主客户端加载对应场景
        if (PhotonNetwork.IsMasterClient)
            LoadArena();
    }
 
    #endregion
 
 
    #region Private Methods
 
    void LoadArena()
    {
        //Photon分为主客户端、其他客户端。只有主客户端才可进行加载房间。
        //根据当前房间人数加载对应场景。该方法会使其他玩家会自动加入主客户端的房间
        PhotonNetwork.LoadLevel("Room for " + PhotonNetwork.CurrentRoom.PlayerCount);
    }
 
    #endregion
}


🟨 血条、昵称显示

1、创建UI

在人物下创建3D Slider血条,并且有文本框用于显示名字。

若是滑动条即使是满值也不到头,只需改下滑动条下Fill Area 的 Right值就好了。

2、代码赋值

将下代码挂到机器人身上,并赋值。

using UnityEngine;
using UnityEngine.UI;
 
public class PlayerUI : MonoBehaviour
{
    #region  Parameters
 
    [Tooltip("显示玩家名字的UI文本")]
    public Text playerNameText;
 
    [Tooltip("健康值Slider")]
    public Slider playerHealthSlider;
 
    [Tooltip("用于获取生命值赋值给血条")]
    public PlayerManager target;
 
    #endregion
 
 
    #region Mono Callbacks
 
    void Start()
    {
        playerNameText.text = target.photonView.Owner.NickName;
    }
 
    void Update()
    {
        playerHealthSlider.value = target.Health;
    }
 
    #endregion
}

若现在还没把机器人放到 Resources里,现在就要放进去了。已经放进去的Apply一下。

将 Resources 里的机器人预制体赋值给GameManager,删掉面板的机器人。

各个场景的 GameManager都赋值一下,接下来要进行测试了。

还有,记得关闭离线模式哦


🟩 最终效果展示:

落头上了哈哈

好啦,本系列暂告一段落🧸


相关文章
|
2月前
|
开发工具 图形学
PUN☀️一、基础配置、搭建大厅程序
PUN☀️一、基础配置、搭建大厅程序
|
2月前
|
图形学
PUN☀️三、服务器大厅、服务器房间
PUN☀️三、服务器大厅、服务器房间
|
10月前
|
存储 监控 数据库
大师学SwiftUI第18章Part2 - 存储图片和自定义相机
在前面的示例中,我们在屏幕上展示了图片,但也可以将其存储到文件或数据库中。另外有时使用相机将照片存储到设备的相册薄里会很有用,这样可供其它应用访问。UIKit框架提供了如下两个保存图片和视频的函数。 •
317 0
|
11月前
|
小程序
小程序获取的用户头像怎么做成圆形
小程序获取的用户头像怎么做成圆形
69 0
|
C语言 C++ Windows
【c++】设置控制台窗口字体颜色和背景色(system和SetConsoleTextAttribute函数 )(内含超好玩的c++游戏链接)
【c++】设置控制台窗口字体颜色和背景色(system和SetConsoleTextAttribute函数 )(内含超好玩的c++游戏链接)
536 0
【c++】设置控制台窗口字体颜色和背景色(system和SetConsoleTextAttribute函数 )(内含超好玩的c++游戏链接)
机房重构用一个Button实现上下机功能
机房重构用一个Button实现上下机功能
60 0
|
前端开发
前端 解决input框被禁用后无法添加点击事件的bug 简单易懂,快来围观
在开发中,我们经常会遇到需要禁用input框的情况,但是禁用后无法添加点击事件的问题。这个问题可能会让你感到十分困扰,但是不用担心,本文将会为您介绍一种解决这个问题的方法
|
存储 缓存 Java
通过这个文章重新再深入认识认识String吧!!文末附有三个小小面试题,一起来试一试吧
通过这个文章重新再深入认识认识String吧!!文末附有三个小小面试题,一起来试一试吧
111 0
通过这个文章重新再深入认识认识String吧!!文末附有三个小小面试题,一起来试一试吧
|
Java
Java初学者作业——定义英雄类(Hero),英雄类中的属性包括:姓名、攻击力、防御力、生命值和魔法值;方法包括:攻击、介绍。
Java初学者作业——定义英雄类(Hero),英雄类中的属性包括:姓名、攻击力、防御力、生命值和魔法值;方法包括:攻击、介绍。
1349 1