Unity-模拟网络游戏动画同步

文章目录[x]
  1. 0.1:一、准备
  2. 0.2:二、设置动画状态机
  3. 0.3:三、场景设置

这个方案采用同步一个动画状态机的各个参数来实现动画同步的目的,这里没有使用网络传输数据只是在本地模拟两个模型动画同步。

一、准备

1.从unity asset store上面下载一个免费的人形动画插件 Basic Motions FREE。
2.导入该插件,然后新建一个空场景,随便添加一些环境物体。

二、设置动画状态机

1.新建一个动画状态机。
2.添加两个float类型的参数XSpeed、YSpeed。
3.添加一个2D混合树,然后如图设置各个方向的动画。

三、场景设置

1.场景中添加模型,然后在Animator组件中使用上面创建的Animator Controller。
2.编写脚本,并挂在到模型上面。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class SyncAnimator : MonoBehaviour
{
    public Animator animator;
    public Animator targetAnim;
    AnimatorControllerParameter[] parameters;

    void Awake()
    {
        parameters = animator.parameters
            .Where(par => !animator.IsParameterControlledByCurve(par.nameHash))
            .ToArray();
    }

    private void Update()
    {
        animator.SetFloat("XSpeed", Input.GetAxis("Horizontal"));
        animator.SetFloat("YSpeed", Input.GetAxis("Vertical"));
    }
    int i = 0;
    private void FixedUpdate()
    {
        i++;
        if(i==2)
        {
            ReadParameters(WriteParameters());
            i = 0;
        }
    }

    void ReadParameters(byte[] steam)
    {
        bool animatorEnabled = targetAnim.enabled;
        int index = 0;
        for (int i = 0; i < parameters.Length; i++)
        {
            AnimatorControllerParameter par = parameters[i];
            if (par.type == AnimatorControllerParameterType.Int)
            {
                int newIntValue = BitConverter.ToInt32(steam, index);
                index += 4;
                if (animatorEnabled)
                    targetAnim.SetInteger(par.nameHash, newIntValue);
            }
            else if (par.type == AnimatorControllerParameterType.Float)
            {
                float newFloatValue = BitConverter.ToSingle(steam, index);
                index += 4;
                if (animatorEnabled)
                    targetAnim.SetFloat(par.nameHash, newFloatValue);
            }
            else if (par.type == AnimatorControllerParameterType.Bool)
            {
                bool newBoolValue = BitConverter.ToBoolean(steam, index);
                index += 1;
                if (animatorEnabled)
                    targetAnim.SetBool(par.nameHash, newBoolValue);
            }
        }
    }

    byte[] WriteParameters()
    {
        List<byte> steam = new List<byte>();
        for (int i = 0; i < parameters.Length; i++)
        {
            AnimatorControllerParameter par = parameters[i];
            if (par.type == AnimatorControllerParameterType.Int)
            {
                int newIntValue = animator.GetInteger(par.nameHash);
                steam.AddRange(BitConverter.GetBytes(newIntValue));
            }
            else if (par.type == AnimatorControllerParameterType.Float)
            {
                float newFloatValue = animator.GetFloat(par.nameHash);
                steam.AddRange(BitConverter.GetBytes(newFloatValue));
            }
            else if (par.type == AnimatorControllerParameterType.Bool)
            {
                bool newBoolValue = animator.GetBool(par.nameHash);
                steam.AddRange(BitConverter.GetBytes(newBoolValue));
            }
        }
        return steam.ToArray();
    }
    //设置同步位置
    private void CommandMove(Vector3 targetPos)
    {
       lerpStartPos = targetAnim.transform.position;
       lerpEndPos = targetPos;
       lerpTime = 0;
    }
    //位置插值移动
    void LerpPos()
    {
       targetAnim.transform.position = Vector3.SmoothDamp(targetAnim.transform.position, lerpEndPos, ref vel, receiveInterval);
    }
}
3.在场景中添加另外一个模型,同样指定Animator Controller。
4.为脚本赋值。
运行后通过 awsd 键控制动画,另外一个模型也会同步。这里两个模型都会移动是因为这个动画中应用了Root Motion,如果不想让动画控制模型位置,可以在Animator组件上不勾选Apply Root Motion。一般情况下我们使用的动画都是不带根节点移动的,模型的位置和方向通过网络传过来的数据进行同步。
注意:如果AnimatorController中使用了Trigger作为参数,就需要额外的消息进行同步。
点赞

发表回复

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像