Unity3D仿星露谷物语开发7之事件创建动画

1、目标

掌握事件通知的Publisher - Subscriber设计模式,并通过事件通知触发动画。

2、发布者/订阅者模式

首先,定义事件Event

然后,Publisher触发事件

最后,Subscriber订阅事件并进行处理

(1)创建动作Enum类

在Assets -> Scripts 下新建Enums目录,并且创建Enums脚本如下:

cs 复制代码
public enum ToolEffect
{
    none, 
    watering
}

(2)创建事件处理类EventHandler

cs 复制代码
public delegate void MovementDelegate(float inputX, float inputY, bool isWalking, bool isRunning, bool isIdle, bool isCarrying,
    ToolEffect toolEffect,
    bool isUsingToolRight, bool isUsingToolLeft, bool isUsingToolUp, bool isUsingToolDown,
    bool isLiftingToolRight, bool isLiftingToolLeft, bool isLiftingToolUp, bool isLiftingToolDown,
    bool isPickingRight, bool isPickingLeft, bool isPickingUp, bool isPickingDown,
    bool isSwingToolRight, bool isSwingToolLeft, bool isSwingToolUp, bool isSwingToolDown,
    bool idleUp, bool idleDown, bool idleLeft, bool idleRight
    );

public static class EventHandler
{

    public static event MovementDelegate MovementEvent;

    public static void CallMovementEvent(float inputX, float inputY, bool isWalking, bool isRunning, bool isIdle, bool isCarrying,
    ToolEffect toolEffect,
    bool isUsingToolRight, bool isUsingToolLeft, bool isUsingToolUp, bool isUsingToolDown,
    bool isLiftingToolRight, bool isLiftingToolLeft, bool isLiftingToolUp, bool isLiftingToolDown,
    bool isPickingRight, bool isPickingLeft, bool isPickingUp, bool isPickingDown,
    bool isSwingToolRight, bool isSwingToolLeft, bool isSwingToolUp, bool isSwingToolDown,
    bool idleUp, bool idleDown, bool idleLeft, bool idleRight)
    {
        if (MovementEvent != null)
        {
            MovementEvent(inputX, inputY, isWalking, isRunning, isIdle, isCarrying,toolEffect,
                isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,
                isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,
                isPickingRight, isPickingLeft, isPickingUp, isPickingDown,
                isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,
                idleUp, idleDown, idleLeft, idleRight);
        }
    }

}
  • 静态类EventHandler

定义了一个公开的静态类 EventHandler。静态类不能被实例化,并且它包含的所有成员都必须是静态的。这意味着你可以直接通过类名访问这些成员,而不需要创建类的实例。

  • 静态事件 MovementEvent

委托是一种特殊的类型,它定义了方法的类型,使得可以将方法作为参数进行传递。

  • 静态方法 CallMovementEvent

首先检查 MovementEvent 是否不为 null(即是否有事件订阅者)。如果有,就调用该事件,并传递所有参数。

  • 事件入参

事件的入参和动画(比如body)中的参数是一一对应的。

3、构建事件触发角色的动画

(1)思路

主要思路:

MovementAnimationParameterController脚本已经挂载到Player对象的每个部位中,那么就在这个脚本中订阅事件并处理事件,该脚本就相当于Subscriber。

创建一个PlayerAnimationTest脚本挂载到Player对象上,该脚本发布事件,作为Publisher。

这个是Publisher - Subscriber设计模式落地的大致思路。

考虑到动画的参数过多,创建一个Settings脚本放这些参数,每个变量放一个参数。后面通过操控变量进而操作参数。一方面,字符串比较非常耗性能,通过hash的变量提高了性能。另一方面,避免每次写很长的参数,容易写错。

(2)创建Settings脚本

cs 复制代码
using UnityEngine;

public static class Settings
{
    // Player Animation Parameters
    public static int xInput;
    public static int yInput;
    public static int isWalking;
    public static int isRunning;
    public static int toolEffect;
    public static int isUsingToolRight;
    public static int isUsingToolLeft;
    public static int isUsingToolUp;
    public static int isUsingToolDown;
    public static int isLiftingToolRight;
    public static int isLiftingToolLeft;
    public static int isLiftingToolUp;
    public static int isLiftingToolDown;
    public static int isSwingingToolRight;
    public static int isSwingingToolLeft;
    public static int isSwingingToolUp;
    public static int isSwingingToolDown;
    public static int isPickingRight;
    public static int isPickingLeft;
    public static int isPickingUp;
    public static int isPickingDown;

    // Shared Animation Parameters
    public static int idleUp;
    public static int idleDown;
    public static int idleLeft;
    public static int idleRight;


    // static constructor
    static Settings()
    {
        xInput = Animator.StringToHash("xInput");
        yInput = Animator.StringToHash("yInput");
        isWalking = Animator.StringToHash("isWalking");
        isRunning = Animator.StringToHash("isRunning");
        toolEffect = Animator.StringToHash("toolEffect");
        isUsingToolRight = Animator.StringToHash("isUsingToolRight");
        isUsingToolLeft = Animator.StringToHash("isUsingToolLeft");
        isUsingToolUp = Animator.StringToHash("isUsingToolUp");
        isUsingToolDown = Animator.StringToHash("isUsingToolDown");
        isLiftingToolRight = Animator.StringToHash("isLiftingToolRight");
        isLiftingToolLeft = Animator.StringToHash("isLiftingToolLeft");
        isLiftingToolUp = Animator.StringToHash("isLiftingToolUp");
        isLiftingToolDown = Animator.StringToHash("isLiftingToolDown");
        isSwingingToolRight = Animator.StringToHash("isSwingingToolRight");
        isSwingingToolLeft = Animator.StringToHash("isSwingingToolLeft");
        isSwingingToolUp = Animator.StringToHash("isSwingingToolUp");
        isSwingingToolDown = Animator.StringToHash("isSwingingToolDown");
        isPickingRight = Animator.StringToHash("isPickingRight");
        isPickingLeft = Animator.StringToHash("isPickingLeft");
        isPickingUp = Animator.StringToHash("isPickingUp");
        isPickingDown = Animator.StringToHash("isPickingDown");
        idleUp = Animator.StringToHash("idleUp");
        idleDown = Animator.StringToHash("idleDown");
        idleLeft = Animator.StringToHash("idleLeft");
        idleRight = Animator.StringToHash("idleRight");
    }

}

在Unity中,Animator.StringToHash是一个用于将字符串转换为哈希值的方法。这个方法主要用于在动画状态机(Animator Controller)中引用状态、参数和触发器时,提高性能并减少字符串比较的开销。

好处:

  • 性能优化:在运行时,字符串比较相对耗时,因为需要逐个字符进行比较,而哈希值比较则通常更快。使用哈希值来引用动画状态、参数和触发器,可以显著减少性能开销,特别是在频繁访问的情况下。
  • 避免字符串拼写错误:在代码中硬编码字符串时,容易因为拼写错误导致运行时报错,而使用Animator.StringToHash可以在Animator Controller中直接引用变量,减少人为错误。

StringToHash使用的示例(单独的例子)

cs 复制代码
using UnityEngine;

public class Example : MonoBehaviour
{
    private Animator animator;

    void Start()
    {
        animator = GetComponent<Animator>();

        // 假设在Animator Controller中有一个参数名为 "Speed"
        int speedHash = Animator.StringToHash("Speed");
        animator.SetFloat(speedHash, 5.0f);

        // 假设在Animator Controller中有一个触发器名为 "Jump"
        int jumpHash = Animator.StringToHash("Jump");
        animator.SetTrigger(jumpHash);

        // 假设在Animator Controller中有一个状态名为 "Run"
        int runHash = Animator.StringToHash("Run");
        animator.Play(runHash);
    }
}

(3)Subscriber订阅事件

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MovementAnimationParameterControl : MonoBehaviour
{
    private Animator animator;

    private void Awake()
    {
        animator = GetComponent<Animator>();
    }

    private void OnEnable()
    {
        EventHandler.MovementEvent += SetAnimationParameters;
    }

    private void OnDisable()
    {
        EventHandler.MovementEvent -= SetAnimationParameters;
    }


    private void SetAnimationParameters(float xInput, float yInput, bool isWalking, bool isRunning, bool isIdle, bool isCarrying,
    ToolEffect toolEffect,
    bool isUsingToolRight, bool isUsingToolLeft, bool isUsingToolUp, bool isUsingToolDown,
    bool isLiftingToolRight, bool isLiftingToolLeft, bool isLiftingToolUp, bool isLiftingToolDown,
    bool isPickingRight, bool isPickingLeft, bool isPickingUp, bool isPickingDown,
    bool isSwingToolRight, bool isSwingToolLeft, bool isSwingToolUp, bool isSwingToolDown,
    bool idleUp, bool idleDown, bool idleLeft, bool idleRight)
    {
        animator.SetFloat(Settings.xInput, xInput);
        animator.SetFloat(Settings.yInput, yInput);
        animator.SetBool(Settings.isWalking, isWalking);
        animator.SetBool(Settings.isRunning, isRunning);
        animator.SetInteger(Settings.toolEffect, (int)toolEffect);

        if(isUsingToolRight)
            animator.SetTrigger(Settings.isUsingToolRight);
        if(isUsingToolLeft)
            animator.SetTrigger(Settings.isUsingToolLeft);
        if(isUsingToolUp)
            animator.SetTrigger(Settings.isUsingToolUp);
        if(isUsingToolDown)
            animator.SetTrigger(Settings.isUsingToolDown);

        if(isLiftingToolRight)
            animator.SetTrigger(Settings.isLiftingToolRight);
        if(isLiftingToolLeft)
            animator.SetTrigger(Settings.isLiftingToolLeft);
        if(isLiftingToolUp)
            animator.SetTrigger(Settings.isLiftingToolUp);
        if(isLiftingToolDown)
            animator.SetTrigger(Settings.isLiftingToolDown);

        if(isPickingRight)
            animator.SetTrigger(Settings.isPickingRight);
        if(isPickingLeft)
            animator.SetTrigger(Settings.isPickingLeft);
        if(isPickingUp)
            animator.SetTrigger(Settings.isPickingUp);
        if(isPickingDown)
            animator.SetTrigger(Settings.isPickingDown);

        if(isSwingToolRight)
            animator.SetTrigger(Settings.isSwingingToolRight);
        if(isSwingToolLeft)
            animator.SetTrigger(Settings.isSwingingToolLeft);
        if(isSwingToolUp)
            animator.SetTrigger(Settings.isSwingingToolUp);
        if (isSwingToolDown)
            animator.SetTrigger(Settings.isSwingingToolDown);

        if(idleRight)
            animator.SetTrigger(Settings.idleRight);
        if(idleLeft)
            animator.SetTrigger(Settings.idleLeft);
        if(idleUp)
            animator.SetTrigger(Settings.idleUp);
        if(idleDown)
            animator.SetTrigger(Settings.idleDown);


    }


    // Start is called before the first frame update
    private void AnimationEventPlayFootstepSound()
    {

    }
}
  • +=:订阅事件, -=:取消订阅事件

SetAnimationParameters时接收到事件时对应的处理方法。

  • OnEnable函数:在脚本组件被激活时调用。OnDisable函数:在脚本被禁用时调用。

(4)创建Publisher发布事件

在Assets -> Scripts -> Player下创建PlayerAnimationTest脚本如下,并将该脚本挂载到Player对象上。

cs 复制代码
using UnityEngine;

public class PlayerAnimationTest : MonoBehaviour
{
    public float inputX;
    public float inputY;
    public bool isWalking;
    public bool isRunning;
    public bool isIdle;
    public bool isCarrying;
    public ToolEffect toolEffect;
    public bool isUsingToolRight;
    public bool isUsingToolLeft;
    public bool isUsingToolUp;
    public bool isUsingToolDown;
    public bool isLiftingToolRight;
    public bool isLiftingToolLeft;
    public bool isLiftingToolUp;
    public bool isLiftingToolDown;
    public bool isPickingRight;
    public bool isPickingLeft;
    public bool isPickingUp;
    public bool isPickingDown;
    public bool isSwingToolRight;
    public bool isSwingToolLeft;
    public bool isSwingToolUp;
    public bool isSwingToolDown;
    public bool idleUp;
    public bool idleDown;
    public bool idleLeft;
    public bool idleRight;

    // Update is called once per frame
    void Update()
    {
        EventHandler.CallMovementEvent(inputX, inputY, isWalking, isRunning, isIdle, isCarrying, toolEffect,
                isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,
                isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,
                isPickingRight, isPickingLeft, isPickingUp, isPickingDown,
                isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,
                idleUp, idleDown, idleLeft, idleRight); 
    }
}

4、运行 程序

测试isWalking时,需要inputX有值,因为Walk动画的触发条件是xInput>0.01如下:

效果如下:

相关推荐
EQ-雪梨蛋花汤37 分钟前
【Part 3 Unity VR眼镜端播放器开发与优化】第四节|高分辨率VR全景视频播放性能优化
unity·音视频·vr
与火星的孩子对话5 小时前
Unity进阶课程【六】Android、ios、Pad 终端设备打包局域网IP调试、USB调试、性能检测、控制台打印日志等、C#
android·unity·ios·c#·ip
幻世界6 小时前
【Unity智能模型系列】Unity + MediaPipe + Sentis + ArcFace模型:构建高效人脸识别比对系统
unity·游戏引擎
漫游者Nova14 小时前
虚幻引擎Unreal Engine5恐怖游戏设计制作教程,从入门到精通从零开始完整项目开发实战详细讲解中英字幕
ue5·游戏引擎·虚幻·游戏开发完整教程·恐怖游戏开发
死也不注释1 天前
【Unity 编辑器工具开发:GUILayout 与 EditorGUILayout 对比分析】
unity·编辑器·游戏引擎
小赖同学啊1 天前
物联网中的Unity/Unreal引擎集成:数字孪生与可视化控制
物联网·unity·游戏引擎
Zlzxzw1 天前
使用unity创建项目,进行动画制作
unity·游戏引擎
X_StarX2 天前
【Unity笔记01】基于单例模式的简单UI框架
笔记·ui·unity·单例模式·游戏引擎·游戏开发·大学生
九班长2 天前
Golang服务端处理Unity 3D游戏地图与碰撞的详细实现
3d·unity·golang
ysn111112 天前
NGUI实现反向定位到层级面板结点
unity