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如下:

效果如下:

相关推荐
ue星空1 分钟前
虚幻引擎结构之ULevel
游戏引擎·虚幻
向宇it15 分钟前
【从零开始入门unity游戏开发之——unity篇01】unity6基础入门开篇——游戏引擎是什么、主流的游戏引擎、为什么选择Unity
开发语言·unity·c#·游戏引擎
神洛华3 小时前
Y3地图制作1:水果缤纷乐、密室逃脱
编辑器·游戏引擎·游戏程序
向宇it6 小时前
【从零开始入门unity游戏开发之——C#篇26】C#面向对象动态多态——接口(Interface)、接口里氏替换原则、密封方法(`sealed` )
java·开发语言·unity·c#·游戏引擎·里氏替换原则
神码编程12 小时前
【Unity功能集】TextureShop纹理工坊(五)选区
unity·游戏引擎·shader·ps选区
m0_7482517217 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
林枫依依20 小时前
Unity2021.3.16f1可以正常打开,但是Unity2017.3.0f3却常常打开闪退或者Unity2017编辑器运行起来就闪退掉
unity
虾球xz20 小时前
游戏引擎学习第57天
学习·游戏引擎
异次元的归来1 天前
Unity DOTS中的share component
unity·游戏引擎