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

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.rhkb.cn/news/493361.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

点击展示大图预览

原文链接在table表格里能够实现&#xff0c;点击里面的图片实现大图预览的效果&#xff1b; 一、先安装viewer — 使用npm安装 npm install v-viewer --save二、在main.js中引入 import Viewer from v-viewer //点击图片大图预览 import viewerjs/dist/viewer.css Vue.use(…

安全算法基础(一)

安全算法是算法的分支之一&#xff0c;还的依靠大量的数学基础进行计算&#xff0c;本文参照兜哥的AI安全样本对抗&#xff0c;做一个简单的算法安全概括&#xff0c;从零学习。 最新的安全算法对于我们常规的攻击样本检测&#xff0c;效果是不理想的&#xff0c;为了探究其原…

aioice里面candidate固定UDP端口测试

环境&#xff1a; aioice0.9.0 问题描述&#xff1a; aioice里面candidate固定UDP端口测试 解决方案&#xff1a; /miniconda3/envs/nerfstream/lib/python3.10/site-packages/aioice import hashlib import ipaddress import random from typing import Optional import…

UE UMG 多级弹出菜单踩坑

多级弹出菜单 https://www.bilibili.com/video/BV1ub411J7nA 运行时添加 widget 的方法 create widget 然后 add child 到某个组件&#xff0c;比如 canvas 运行时修改 widget 位置的方法 set widget slot position 用起来没效果 怀疑是因为我没有传入 slot 但是暂时不知…

金碟中间件-AAS-V10.0安装

金蝶中间件AAS-V10.0 AAS-V10.0安装 1.解压AAS-v10.0安装包 unzip AAS-V10.zip2.更新license.xml cd /root/ApusicAS/aas# 这里要将license复制到该路径 [rootvdb1 aas]# ls bin docs jmods lib modules templates config domains …

linux-----网络编程

网络编程基础概念 IP地址&#xff1a;是互联网协议地址&#xff0c;用于在网络中唯一标识一台设备。在IPv4中&#xff0c;地址是32位的二进制数&#xff0c;通常以点分十进制表示&#xff0c;如192.168.1.1。IPv6是128位的地址&#xff0c;用于提供更多的地址空间&#xff0c;格…

基于蓝牙通信的手机遥控智能灯(论文+源码)

1.系统设计 灯具作为人们日常生活的照明工具为人们生活提供光亮&#xff0c;本次基于蓝牙通信的手机遥控智能灯设计功能如下&#xff1a; &#xff08;1&#xff09;用户可以通过蓝牙通信模块的作用下&#xff0c;在手机端遥控切换智能灯不同的工作模式&#xff1b; &#x…

阿里云百炼大模型生成贪吃蛇小游戏

阿里云百炼大模型生成贪吃蛇小游戏 为了在贪吃蛇游戏中添加背景音乐&#xff0c;我们可以使用Pygame的mixer模块。以下是修改后的代码&#xff0c;包含了背景音乐的加载和播放功能&#xff1a; 安装Pygame&#xff08;如果你还没有安装&#xff09;&#xff1a; pip install p…

Pytorch | 利用FGSM针对CIFAR10上的ResNet分类器进行对抗攻击

Pytorch | 利用FGSM针对CIFAR10上的ResNet分类器进行对抗攻击 CIFAR数据集FGSM介绍算法原理算法特点应用场景局限性 FGSM代码实现FGSM算法实现攻击效果 代码汇总fgsm.pytrain.pyadvtest.py 之前已经针对CIFAR10训练了多种分类器&#xff1a; Pytorch | 从零构建AlexNet对CIFAR1…

3D Gaussian Splatting for Real-Time Radiance Field Rendering-简洁版

1. 研究背景与问题 传统的3D场景表示方法&#xff0c;如网格和点云&#xff0c;适合GPU加速的光栅化操作&#xff0c;但缺乏灵活性。而基于神经辐射场&#xff08;NeRF&#xff09;的表示方式&#xff0c;尽管质量高&#xff0c;但需要高成本的训练和渲染时间。此外&#xff0…

安卓环境配置及打开新项目教程,2024年12月20日最新版

1.去官网下载最新的Android Studio&#xff0c;网址&#xff1a;https://developer.android.com/studio?hlzh-cn 2.下载加速器&#xff0c;注册账号&#xff0c;开启加速器。网址&#xff1a;放在文末。 3.下载安卓代码&#xff0c;项目的路径上不能有中文&#xff0c;特别是…

不会心理描写,神态描写怎么办?

不会心理描写&#xff0c;神态描写怎么办&#xff1f; 文学创作&#xff0c;精微之处在于心理与神态之描绘。 一、夯实基础&#xff0c;积累素材。 欲使心理与神态描写生动&#xff0c;需先厚积薄发。博览群书&#xff0c;尤重经典之作。 “读万卷书&#xff0c;行万里路。…

【使用MCP协议连接本地和远程数据——以Claude的Windows客户端为例】

本博客内容主要是如何在Windows系统上为Claude客户端&#xff08;无需开通会员&#xff09;配置模型上下文协议(Model Context Protocol, MCP)服务器。 为什么选择 MCP&#xff1f; MCP 可帮助您在 LLM 之上构建代理和复杂的工作流程。LLM 经常需要与数据和工具集成&#xff0…

React:闭包陷阱产生和解决

在 React 中&#xff0c;闭包陷阱是一个常见的问题&#xff0c;尤其是在处理异步操作、事件处理器、或是定时器时。理解闭包的工作原理以及它在 React 中如何与状态和渲染交互&#xff0c;可以帮助你避免陷入一些常见的错误。 一、闭包陷阱的产生 1、什么是闭包陷阱&#xff1…

使用xjar 对Spring-Boot JAR 包加密运行

1 Xjar 介绍 Spring Boot JAR 安全加密运行工具&#xff0c;同时支持的原生JAR。 基于对JAR包内资源的加密以及拓展ClassLoader来构建的一套程序加密启动&#xff0c;动态解密运行的方案&#xff0c;避免源码泄露或反编译。 功能特性 无需侵入代码&#xff0c;只需要把编译好的…

[LeetCode-Python版] 定长滑动窗口1(1456 / 643 / 1343 / 2090 / 2379)

思路 把问题拆解成三步&#xff1a;入-更新-出。 入&#xff1a;下标为 i 的元素进入窗口&#xff0c;更新相关统计量。如果 i<k−1 则重复第一步。更新&#xff1a;更新答案。一般是更新最大值/最小值。出&#xff1a;下标为 i−(k-1) 的元素离开窗口&#xff0c;更新相关…

【AIGC-ChatGPT进阶副业提示词】末日生存指南 2.0:疯狂科学家的荒诞智慧

引言 在这个不断变化的世界中&#xff0c;末日似乎总是lurking在角落。但是&#xff0c;亲爱的幸存者们&#xff0c;不要害怕&#xff01;因为我&#xff0c;疯狂科学家2099&#xff0c;正在这里为你们带来最新版本的末日生存指南。这不是你祖母的应急手册&#xff0c;而是一本…

Web3.0安全开发实践:探索比特币DeFi生态中的PSBT

近年来&#xff0c;部分签名比特币交易&#xff08;PSBT&#xff09;在比特币生态系统中获得了显著关注。随着如Ordinal和基于铭文的资产等创新的兴起&#xff0c;安全的多方签名和复杂交易的需求不断增加&#xff0c;这使得PSBT成为应对比特币生态不断发展中不可或缺的工具。 …

Edge Scdn防御网站怎么样?

酷盾安全Edge Scdn&#xff0c;即边缘式高防御内容分发网络&#xff0c;主要是通过分布在不同地理位置的多个节点&#xff0c;使用户能够更快地访问网站内容。同时&#xff0c;Edge Scdn通过先进的技术手段&#xff0c;提高了网上内容传输的安全性&#xff0c;防止各种网络攻击…

oracle client linux服务器安装教程

p13390677_112040_Linux-x86-64_4of7.zip 安装前&#xff0c;确认/etc/hosts文件已配置正确 cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 10.2…