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

server/2024/12/27 0:52:52/

1、目标

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

2、发布者/订阅者模式

首先,定义事件Event

然后,Publisher触发事件

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

(1)创建动作Enum类

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

public enum ToolEffect
{none, watering
}

(2)创建事件处理类EventHandler

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脚本

using UnityEngine;public static class Settings
{// Player Animation Parameterspublic 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 Parameterspublic static int idleUp;public static int idleDown;public static int idleLeft;public static int idleRight;// static constructorstatic 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使用的示例(单独的例子)

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订阅事件

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 updateprivate void AnimationEventPlayFootstepSound(){}
}
  • +=:订阅事件, -=:取消订阅事件

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

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

(4)创建Publisher发布事件

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

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

效果如下:


http://www.ppmy.cn/server/153470.html

相关文章

【每日学点鸿蒙知识】沙箱目录、图片压缩、characteristicsArray、gm-crypto 国密加解密、通知权限

1、HarmonyOS 如何创建应用沙箱目录&#xff1f; 下载文件&#xff0c;想下载到自己新建的应用沙箱目录&#xff0c;有什么方法实现吗&#xff1f; fs.mkdir可以创建目录 参考文档&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis…

解决element-ui table show-summary合计行不显示问题

在table的底部添加合计设置show-summary就可以了 但是给table加了一个固定高度话&#xff0c;就不显示了&#xff0c;其实这个合计是存在的 解决办法&#xff1a; 1.不设置固定高度 2.在vue的生命周期updated调用一下doLayout就可以了 updated () {this.$nextTick(() >…

upload-labs关卡记录5

这一关&#xff0c;卡了好久&#xff0c;这里我的靶场的源代码&#xff0c;和我看别的师傅发的不一样&#xff0c;我的这里的源码带有小写转换&#xff1a; 是不能通过大小写进行绕过&#xff0c;最后找到这个师傅的文章&#xff1a;upload-labs通关-CSDN博客 这里第五关用到…

2024最新教程Mac安装双系统

程序员Feri一名12年的程序员,做过开发带过团队创过业,擅长Java相关开发、鸿蒙开发、人工智能等,专注于程序员搞钱那点儿事,希望在搞钱的路上有你相伴&#xff01;君志所向,一往无前&#xff01; 0.前言 最近我的老Mac Pro&#xff0c;2016年的&#xff0c;Intel的芯片&#xf…

AR 模型的功率谱

功率谱密度&#xff08;Power Spectral Density, PSD&#xff09;的表达式是从信号的自相关函数和系统的频率响应推导出来的&#xff0c;特别是对于 AR&#xff08;Auto-Regressive&#xff0c;自回归&#xff09;模型。以下是推导的过程&#xff1a; 1. AR 模型的定义&#xf…

iOS 多个输入框弹出键盘处理

开发中遇到这样一个场景&#xff0c;有多个输入框 而不同的输入框&#xff0c;需要页面向上偏移的距离不一样 这个时候&#xff0c;需要我们在获取到键盘弹出通知的时候&#xff0c;需要 知道我们开始进行编辑的是哪一个输入框&#xff0c;这个时候 需要我们知道一个技术点&…

5G -- 空口关键技术

前言&#xff1a; 手机(UE)和5G基站(gNodeB)之间的空中接口 新技术的特点&#xff1a; 1、提升速率&#xff1a;大带宽、新编码、高阶调制、F-OFDM、M-MIMO 2、降低时延&#xff1a;灵活帧结构、自包含时隙、免授权调度、D2D 3、提升覆盖&#xff1a;上下行解耦、EN-DC(双连…

【微信小程序】3|首页搜索框 | 我的咖啡店-综合实训

首页-搜索框-跳转 引言 在微信小程序中&#xff0c;首页的搜索框是用户交互的重要入口。本文将通过“我的咖啡店”小程序的首页搜索框实现&#xff0c;详细介绍如何在微信小程序中创建和处理搜索框的交互。 1. 搜索函数实现 onClickInput函数在用户点击搜索框时触发&#x…