Photon Unity Networking基础教程 9 Player UI Prefab

本节将指导您创建Player UI系统。我们需要显示玩家的名称及其当前的健康状况。我们还需要管理UI位置来跟随玩家。

这部分与网络无关,但是,它提出了一些非常重要的设计模式,提供一些关于网络的高级特性,以及开发中的约束。

所以,UI不会做成联网的,仅仅是因为我们不需要,有许多方式可以去做这件事,避免占用带宽。如果你能实现一个功能而不需要联网,总是件好事,这些努力是值得的。

那么现在的问题是:我们如何为每个联网的玩家提供一个UI?

我们将有一个UI Prefab与专用PlayerUI脚本。我们的PlayerManager脚本将保存此UI Prefab的引用,并将在PlayerManager开始时简单地实例化此UI Prefab,并告诉预制跟随该玩家。

主要内容

  • 创建UI Prefab
  • PlayerUI脚本基础
  • 实例化和Player绑定
  • 跟随目标Player

创建UI Prefab

  1. 打开任何一个有UI Canvas的场景
  2. 向canvas添加Slider UI对象,将其命名为Player UI
  3. 将RectTransform垂直锚点设置为居中,将水平锚点设置为中心
  4. 将RectTransform宽度设置为80,高度设置为15
  5. 选择background子节点,将其图像组件颜色设置为红色
  6. 选择子节点"Fill Area/Fill",将其图像颜色设置为绿色
  7. 添加一个文本UI对象作为Player UI的子节点,将其命名为Player Name Text
  8. 将Player UI从Hierarchy拖动到Assets中的Prefab文件夹中,生成Prefab
  9. 删除场景中的实例,我们不再需要它了。

PlayerUI脚本基础

  1. 创建新的C#脚本,命名为PlayerUI

  2. 下面是PlayerUI脚本的框架

     using UnityEngine;
     using UnityEngine.UI;
     using System.Collections;
     
     namespace Com.MyCompany.MyGame
     {
         public class PlayerUI : MonoBehaviour
         {
             #region Public Properties
             [Tooltip("UI Text to display Player's Name")]
             public Text PlayerNameText;
     
             [Tooltip("UI Slider to display Player's Health")]
             public Slider PlayerHealthSlider;
             #endregion
     
             #region Private Properties
     
             #endregion
     
             #region MonoBehaviour Messages
     
             #endregion
     
             #region Public Methods
     
             #endregion
         }
     }
    
  3. 保存脚本

现在让我们编辑Prefab。

  1. 把PlayerUI脚本添加到PayerUI prefab上
  2. 把 "Player Name Text"对象拖拽到PlayerNameText公共字段上面
  3. 把Slider组件拖拽到公共字段PlayerHealthSlider上面

实例化和Player绑定

绑定PlayerUI到Player

PlayerUI脚本需要知道它代表了哪个Player,因为其中的一个原因是:能够显示它的健康和名称,让我们创建一个公共的方法来完成这个绑定。

  1. 打开PlayerUI脚本

  2. 在私有属性区块添加一个私有属性

     PlayerManager _target;
    

    我们在这里需要思考,我们会定期寻找Health,所以缓存一个Player Manager的引用以提高效率是有意义的。

  3. 在公共方法区块添加下面的代码

     public void SetTarget(PlayerManager target){
         if (target == null) {
             Debug.LogError("Missing PlayMakerManager target for PlayerUI.SetTarget.",this);
             return;
         }
         // Cache references for efficiency
         _target = target;
         if (PlayerNameText != null) {
             PlayerNameText.text = _target.photonView.owner.name;
         }
     }
    
  4. 在MonoBehaviour Messages区块添加这个方法

     void Update()
     {
         // Reflect the Player Health
         if (PlayerHealthSlider != null) {
             PlayerHealthSlider.value = _target.Health;
         }
     }
    
  5. 保存代码

有了这些,我们就可以显示目标Player的名字和health了。

实例化

OK,所以我们已经知道如何实例化这个Prefab。每次我们实例化,最好的方法是在PlayerManager的初始化过程中。

  1. 打开脚本PlayerManager

  2. 添加公共字段来持有Player UI的引用

     [Tooltip("The Player's UI GameObject Prefab")]
     public GameObject PlayerUiPrefab;
    
  3. 在Start()方法中添加下面的代码

  4. 保存脚本

所有这些都是标准的Unity编码。但请注意,我们正在向刚刚创建的实例发送消息。我们需要一个接收器,这意味着如果SetTarget没有找到响应它的组件,我们将被警告。另一种方法是从实例中获取PlayerUI组件,然后直接调用SetTarget。通常的建议是,直接使用组件,但是知道你可以以不同的方式实现同样的事情也很好。

然而这远远不够,我们需要处理删除Player时,不能有孤独的UI实例,所以当UI实例发现被分配到的目标不存在的话,我们就需要销毁它。

  1. 打开PlayerUI脚本

  2. 在Update()中添加代码

     // Destroy itself if the target is null, It's a fail safe when Photon is destroying Instances of a Player over the network
     if (_target == null) {
         Destroy(this.gameObject);
         return;
     }
    
  3. 保存PlayerUI脚本

这个代码,虽然容易,但实际上是相当方便。由于Photon删除联网实例的方式,如果发现目标引用为空,则UI实例更容易简单地销毁自身。这避免了很多潜在的问题,并且非常安全,无论为什么目标丢失的原因,相关的UI会自动破坏自己,非常方便快捷。

但等等...当一个新的级别被加载,UI正在被销毁,但我们的Player还在...所以我们需要实例化它,当我们知道一个级别被加载时,让我们这样做:

  1. 打开PlayerManager脚本

  2. 在CalledOnLevelWasLoaded()方法中添加

     GameObject _uiGo = Instantiate(this.PlayerUiPrefab) as GameObject;
     _uiGo.SendMessage("SetTarget", this, SendMessageOptions.RequireReceiver);
    
  3. 保存PlayerManager脚本

注意,有更复杂/强大的方法来处理这一点,UI可以使用单例模式实现,但它会很快变得复杂,因为其他Players加入和离开房间也需要处理他们的UI。在我们的实现中,这是直接的,代价是我们实例化我们的UI预制的重复。作为一个简单的练习,您可以创建一个私有方法来实例化和从各个地方发送“SetTarget”消息,调用该方法,而不是复制代码。

Player绑定

Unity UI系统的一个非常重要的约束是,任何UI元素必须放置在Canvas对象下面。因此当这个PlayerUI Prefab被实例化时,我们需要处理这个约束,我们将在PlayerUI的初始化期间这样做。

  1. 打开PlayerUI脚本

  2. 在MonoBehaviour Messages区块添加下面的方法

     void Awake(){
         this.GetComponent().SetParent (GameObject.Find("Canvas").GetComponent());
     }
    
  3. 保存PlayerUI脚本

为什么要蛮力查找Canvas这种方式? 因为当场景要被加载和卸载时,我们的Prefab和Canvas每一次都不相同。为了避免更复杂的代码结构,我们将采用最快的方式。真的不建议使用“Find”,因为这是一个缓慢的操作。实现更复杂的处理方法超出了本教程的范围。当你觉得习惯于Unity和脚本,并能找到编写更好的管理Canvas元素引用、并考虑到加载和卸载的方法,这会是一个很好的练习。

跟随目标Player

这是一个有趣的部分,我们需要Player UI跟随屏幕上的Player目标。这意味着几个小问题要解决:

  • UI是2d元素,而播放器是3d元素。 在这种情况下我们如何匹配位置?
  • 我们不想让UI稍微高于Player,我们如何在屏幕上从Player的位置偏移?
  1. 打开PlayerUI脚本

  2. 在Public Properties 区块添加公共属性

     [Tooltip("Pixel offset from the player target")]
     public Vector3 ScreenOffset = new Vector3(0f,30f,0f);
    
  3. 在Private Properties区块添加下面私有属性

     float _characterControllerHeight = 0f;
     Transform _targetTransform;
     Vector3 _targetPosition;
    
  4. 在SetTarget()方法中_target设置之后,添加下面代码

     CharacterController _characterController = _target.GetComponent ();
     // Get data from the Player that won't change during the lifetime of this Component
     if (_characterController != null){
     _characterControllerHeight = _characterController.height;
     }
    

我们知道我们的Player是基于一个CharacterController,它有一个Height属性,我们需要这个做一个适当的偏移,使得UI元素在player上面。

  1. 在Public Methods区块添加公共方法

     void LateUpdate () {
         // #Critical
         // Follow the Target GameObject on screen.
         if (_targetTransform!=null)
         {
             _targetPosition = _targetTransform.position;
             _targetPosition.y += _characterControllerHeight;
             this.transform.position = Camera.main.WorldToScreenPoint (_targetPosition) + ScreenOffset;
         }
     }
    
  2. 保存PlayerUI脚本

因此,将2d位置与3d位置匹配的诀窍是使用Camera的WorldtoScreenPoint函数。由于在我们的游戏中我们只有一个相机,所以可以依赖于访问Unity场景的默认设置主相机 。

注意,我们如何在几个步骤中设置偏移:首先我们获得目标的实际位置,然后添加_characterControllerHeight,最后,在我们推导出Player顶部的屏幕位置之后,我们添加屏幕偏移。

原文

http://doc.photonengine.com/en-us/pun/current/tutorials/pun-basics-tutorial/player-ui-prefab

你可能感兴趣的:(Photon Unity Networking基础教程 9 Player UI Prefab)