首页 > 解决方案 > 如何使用 Photon 准确同步触发动画?

问题描述

根据 Photon 开发人员自己的说明,当您尝试使用触发参数设置 Photon Animator View 时,Photon Animator View 似乎无法很好地使用触发参数(我在某处读到它有 40% 的掉落率,并且我自己的测试显示更糟糕的结果),他们甚至建议我们使用我们自己的自定义代码,使用 RPC 或 RaiseEvents 来获得更好的结果。那么我们如何去同步触发参数触发的动画呢?

标签: c#unity3dphoton

解决方案


我想出了这个可以用来同步动画的类(任何动画,不仅仅是触发动画)。它使用 RaiseEvent(也可以使用 RPC 来完成)。

using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using ExitGames.Client.Photon;

namespace Workbench.Wolfsbane.Multiplayer
{
  [RequireComponent(typeof(Animator))]
  [RequireComponent(typeof(PhotonView))]
  public class NetworkedAnimation : MonoBehaviourPunCallbacks
  {
    #region private fields
    Animator anim;
    #endregion

    #region monobehaviours
    public override void OnEnable()
    {
      base.OnEnable();
      PhotonNetwork.NetworkingClient.EventReceived += OnEvent;
    }

    public override void OnDisable()
    {
      base.OnDisable();
      PhotonNetwork.NetworkingClient.EventReceived -= OnEvent;
    }

    private void Start()
    {
      anim = GetComponent<Animator>();
    }

    #endregion

    #region private methods
    private void OnEvent(EventData photonEvent)
    {
      byte eventCode = photonEvent.Code;
      if (eventCode == PlayAnimationEventCode)
      {
        object[] data = (object[])photonEvent.CustomData;
        int targetPhotonView = (int)data[0];
        if (targetPhotonView == this.photonView.ViewID)
        {
          string animatorParameter = (string)data[1];
          string parameterType = (string)data[2];
          object parameterValue = (object)data[3];
          switch (parameterType)
          {
            case "Trigger":
              anim.SetTrigger(animatorParameter);
              break;
            case "Bool":
              anim.SetBool(animatorParameter, (bool)parameterValue);
              break;
            case "Float":
              anim.SetFloat(animatorParameter, (float)parameterValue);
              break;
            case "Int":
              anim.SetInteger(animatorParameter, (int)parameterValue);
              break;
            default:
              break;
          }
        }
      }
    }
    #endregion

    #region public methods

    public const byte PlayAnimationEventCode = 1;

    public void SendPlayAnimationEvent(int photonViewID, string animatorParameter, string parameterType, object parameterValue = null)
    {
      object[] content = new object[] { photonViewID, animatorParameter, parameterType, parameterValue };
      RaiseEventOptions raiseEventOptions = new RaiseEventOptions { Receivers = ReceiverGroup.All }; 
      PhotonNetwork.RaiseEvent(PlayAnimationEventCode, content, raiseEventOptions, SendOptions.SendReliable);
    }

    #endregion
  }
}

用法:

    // in some other script (that is on the same object)
      GetComponent<NetworkedAnimation>().SendPlayAnimationEvent(photonView.ViewID, animation, "Trigger");

发送正确的 photonviewId 至关重要,因为在订阅事件的所有对象上都会调用 RaiseEvents,因此您需要一种方法来仅过滤适当的对象。在我的例子中,我需要同步播放器动画,因为每个播放器都有一个 photonView,所以我使用 photonView.viewID 作为过滤器机制。其他人可能会走不同的方向,不使用 photonView 过滤对象,但可能将 RaiseEvent 或 RPC 发送到管理器级别的对象,然后将动画命令发送到适当的对象。


推荐阅读