36. UE5 RPG在激活技能时使用蒙太奇动画

news2025/1/14 18:15:34

在上一篇文章里面,我们实现了一个简单的火球术,创建了火球术的火球,以及能发射它的技能。很简陋,在技能触发的时候,直接在武器的位置生成火球发射出去。在一篇文章里,我们要实现使用技能时,角色会播放攻击动画,然后再某个动画帧,将火球发射出去。
所以,我们要使用播放蒙太奇动画来实现动画覆盖,以及动画通知来实现角色手向前攻击时触发火球射出。

播放动画

首先,我们要在技能蓝图里面,在激活蓝图后,使用PlayMontageAndWait节点去播放动画。
我们要实现在释放技能时,播放角色动画,需要使用到技能内置的节点,它是专门用于在技能内使用的版本。
在这里插入图片描述
先找到对应的动画序列,
在这里插入图片描述
创建对应的蒙太奇
在这里插入图片描述
它需要指定插槽,我们这里使用了默认插槽。
在这里插入图片描述
并且你ABP里面,需要设置对应的插槽,这样,插槽前面的动画输入后,会应用插槽内播放的蒙太奇动画,然后混合输出。
在这里插入图片描述
在技能中,设置对应的蒙太奇动画
在这里插入图片描述
我们可以查看一下播放蒙太奇后面的播放顺序,大家也可以想一想它的执行顺序是怎么样的。
在这里插入图片描述
如果一个蒙太奇正常播放完成,Activate不是异步的在技能触发后会直接执行,BlendOut(混出)在蒙太奇播放快播放完成,防止动画直接切换的混合时间,所以是第二个,混合完成后,则触发完成。
在这里插入图片描述
如果想知道UE里面是如何实现这个Task类的,我们可以在代码编辑器里面查看,比如在rider里面,双击Shift键,搜索
在这里插入图片描述
这个类推荐大家都自己看看,学习一下如何制作自定义的Task类,下面我简单讲解一下
类里面是将CreatePlayMontageAndWaitProxy作为一个蓝图可以调用的函数
meta = (…): 这部分包含了关于函数的元数据,这些元数据可以控制函数在蓝图编辑器中的显示和行为。

DisplayName=“PlayMontageAndWait”: 这定义了函数在蓝图编辑器中的显示名称。这可以是与函数实际名称不同的字符串,以便在蓝图中更容易理解其用途。

HidePin = “OwningAbility”: 这告诉蓝图编辑器隐藏名为"OwningAbility"的引脚(即输入参数)。在蓝图中创建节点时,这个参数将不会作为一个可连接的引脚出现,但函数内部仍然可以访问它。

DefaultToSelf = “OwningAbility”: 当在蓝图中创建此函数的节点时,如果未明确连接"OwningAbility"引脚,它将默认使用当前蓝图实例(通常是当前激活的能力或组件)作为此参数的值。

BlueprintInternalUseOnly = “TRUE”: 这意味着这个函数是为蓝图系统的内部使用而设计的,可能不适合在普通的蓝图逻辑中使用。它通常用于为蓝图提供底层支持或实现特定功能,而不是直接供设计者使用。

	/**
	在角色上开始播放一个动画组合,并等待其播放完毕
	如果StopWhenAbilityEnds为true,当能力正常结束时,此动画组合将被中止。当能力被明确取消时,它总是会被停止。
	在正常执行过程中,当动画组合混合退出时,会调用OnBlendOut;当动画组合完全播放完毕时,会调用OnCompleted
	如果另一个动画组合覆盖了此动画组合,则会调用OnInterrupted;如果能力或任务被取消,则会调用OnCancelled
	@param TaskInstanceName 设置以覆盖此任务的名称,以便后续查询
	@param MontageToPlay 要在角色上播放的动画组合
	@param Rate 更改播放动画组合的速度,使其更快或更慢
	@param StartSection 如果不为空,则动画组合将从指定的命名部分开始播放
	@param bStopWhenAbilityEnds 如果为true,当能力正常结束时,此动画组合将被中止。当能力被明确取消时,它总是会被停止
	@param AnimRootMotionTranslationScale 更改以修改根运动的大小,或设置为0以完全阻止它
	@param StartTimeSeconds 动画组合中的起始时间偏移量(以秒为单位),这将被StartSection参数覆盖(如果也设置了该参数)
	@param bAllowInterruptAfterBlendOut 如果为true,在OnBlendOut开始后,你可以收到OnInterrupted(否则,当中断时不会触发OnInterrupted,但你也不会收到OnComplete)。
	*/
	UFUNCTION(BlueprintCallable, Category="Ability|Tasks", meta = (DisplayName="PlayMontageAndWait", HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "TRUE"))
	static UAbilityTask_PlayMontageAndWait* CreatePlayMontageAndWaitProxy(UGameplayAbility* OwningAbility,
		FName TaskInstanceName, UAnimMontage* MontageToPlay, float Rate = 1.f, FName StartSection = NAME_None, bool bStopWhenAbilityEnds = true, float AnimRootMotionTranslationScale = 1.f, float StartTimeSeconds = 0.f, bool bAllowInterruptAfterBlendOut = false);

然后看一下实现,我们就是节点在蓝图中被创建后所做的内容,它创建了并返回了一个新的Task实例并将参数设置给实例。

UAbilityTask_PlayMontageAndWait* UAbilityTask_PlayMontageAndWait::CreatePlayMontageAndWaitProxy(UGameplayAbility* OwningAbility,
	FName TaskInstanceName, UAnimMontage *MontageToPlay, float Rate, FName StartSection, bool bStopWhenAbilityEnds, float AnimRootMotionTranslationScale, float StartTimeSeconds, bool bAllowInterruptAfterBlendOut)
{

	UAbilitySystemGlobals::NonShipping_ApplyGlobalAbilityScaler_Rate(Rate);

	UAbilityTask_PlayMontageAndWait* MyObj = NewAbilityTask<UAbilityTask_PlayMontageAndWait>(OwningAbility, TaskInstanceName);
	MyObj->MontageToPlay = MontageToPlay;
	MyObj->Rate = Rate;
	MyObj->StartSection = StartSection;
	MyObj->AnimRootMotionTranslationScale = AnimRootMotionTranslationScale;
	MyObj->bStopWhenAbilityEnds = bStopWhenAbilityEnds;
	MyObj->bAllowInterruptAfterBlendOut = bAllowInterruptAfterBlendOut;
	MyObj->StartTimeSeconds = StartTimeSeconds;
	
	return MyObj;
}

还有就是节点执行时,其实是调用的ASC身上的PlayMontage去执行动画进行的后续的操作。这里我就简单的讲一下,推荐大家都看一下代码实现。

void UAbilityTask_PlayMontageAndWait::Activate()
{
	if (Ability == nullptr)
	{
		return;
	}

	bool bPlayedMontage = false;

	if (UAbilitySystemComponent* ASC = AbilitySystemComponent.Get())
	{
		const FGameplayAbilityActorInfo* ActorInfo = Ability->GetCurrentActorInfo();
		UAnimInstance* AnimInstance = ActorInfo->GetAnimInstance();
		if (AnimInstance != nullptr)
		{
			if (ASC->PlayMontage(Ability, Ability->GetCurrentActivationInfo(), MontageToPlay, Rate, StartSection, StartTimeSeconds) > 0.f)
			{

到这里,我们实现了动画播放,火球虽然现在还在激活技能时直接触发,这不是我们想要的效果,我们想在角色攻击的那一刻,火球射出,所以,接下来,实现在动画的关键帧实现这个效果。

创建动画通知

要实现角色在攻击动画攻击的那一刻触发火球术,我们接下来要做的就是通过动画通知去触发一个通知事件,然后使用GameplayTag标签去通知在通知中发送对应的事件标签,然后使用监听事件去获取标签,如果触发对应标签,在武器上生成火球。
接下来,我们先添加蒙太奇的通知标签,由于这些内容不需要在c++里面用,我们可以直接在UE里面创建。
在这里插入图片描述
创建一个蓝图类,基于AnimNotify
在这里插入图片描述
打开蓝图,我们要覆盖父函数 已接收通知
在这里插入图片描述
在函数中,我们通过骨骼网格体组件获取所有者actor,然后通过SendGameplayEventToActor发送标签通知,为了能让这个标签通用,我们将标签设置为变量,Send节点,Payload属性可以设置额外数据用于传递,这里不需要,我只用展示,使用时记得删除。
在这里插入图片描述
标签变量的眼睛要打开,这样就可以在外部设置
在这里插入图片描述
制作完成,我们在蒙太奇动画中对应攻击那一刻,添加一个动画通知,直接设置我们创建的动画通知类。
在这里插入图片描述
选中添加的通知,我们可以在右侧细节设置它的标签,这样在蒙太奇播放的时候,就可以触发标签事件了。
在这里插入图片描述
在蓝图中,我们使用wait Gameplay Event节点来监听事件,并将标签设置,在接收到通知后打印一个字符串用于测试。
在这里插入图片描述
运行测试,成功打印就代表事件触发了。
在这里插入图片描述
下面,我们将修改创建火球术的逻辑,不能够在激活时直接触发火球术,而是在接收到通知时触发。
创建一个新的函数,在蓝图中可调用

	UFUNCTION(BlueprintCallable, Category="Projectile")
	void SpawnProjectile();

将实现火球术的代码逻辑移到函数内,代码唯一变的事判断是否为服务器运行修改为了GetAvatarActorFromActorInfo()->HasAuthority()

void UProjectileSpell::SpawnProjectile()
{
	const bool bIsServer = GetAvatarActorFromActorInfo()->HasAuthority(); //判断此函数是否在服务器运行
	if (!bIsServer) return;

	if (ICombatInterface* CombatInterface = Cast<ICombatInterface>(GetAvatarActorFromActorInfo()))
	{
		FTransform SpawnTransform;
		SpawnTransform.SetLocation(CombatInterface->GetCombatSocketLocation());
		SpawnTransform.SetRotation(GetAvatarActorFromActorInfo()->GetActorQuat());
		
		//SpawnActorDeferred将异步创建实例,在实例创建完成时,相应的数据已经应用到了实例身上
		AProjectile* Projectile = GetWorld()->SpawnActorDeferred<AProjectile>(
			ProjectileClass,
			SpawnTransform,
			GetOwningActorFromActorInfo(),
			Cast<APawn>(GetOwningActorFromActorInfo()),
			ESpawnActorCollisionHandlingMethod::AlwaysSpawn);

		//TODO:给 Projectile添加一个GE 去实现伤害

		//确保变换设置被正确应用
		Projectile->FinishSpawning(SpawnTransform);
	}
}

接着编译,打开UE,将技能蓝图修改为接收到事件后,调用生成火球,然后结束技能
在这里插入图片描述
这样,就完成了相应的功能
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1609128.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录:二叉树11-12

目录 222.完全二叉树的节点个数 题目 代码&#xff08;层序迭代&#xff09; 代码&#xff08;后序递归&#xff09; 代码&#xff08;满二次树递归&#xff09; 总结 110.平衡二叉树 题目 代码&#xff08;后序递归&#xff09; 代码&#xff08;层序迭代&#xff0…

关基网络战时代,赛宁网安电力网络攻防靶场全面提升电网安全防护力

随着网络空间成为与陆地、海洋、天空、太空同等重要的人类活动新领域&#xff0c;自网络空间向物理电网发起攻击&#xff0c;破坏电力等国家关键基础设施成为当前大国博弈、大规模战争的重要手段和常态进攻形式。同时&#xff0c;新型电力系统建设发展驱动电力系统形态和控制方…

nginx installed inLinux

yum install nginx [rootmufeng ~]# yum install nginx CentOS系列&#xff1a;【Linux】CentOS7操作系统安装nginx实战&#xff08;多种方法&#xff0c;超详细&#xff09; ———————————————— 版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC …

LLAMA 3的测试之旅:在GPT-4的阴影下前行

Meta终于发布了他们长期期待的LLAMA 3模型&#xff0c;这是一个开源模型&#xff0c;实际上提供了一系列新的功能&#xff0c;使得模型在回答问题时表现得更好。这对AI社区来说是一个真正的里程碑事件。 Meta正在发布新版本的Meta AI&#xff0c;这是一种可以在他们的应用程序和…

原型和原型链--图解

https://juejin.cn/post/7255605810453217335 prototype是函数的属性&#xff08;一个对象&#xff09;&#xff0c;不是对象的属性&#xff0c;普通函数和构造函数的prototype属性是空对象&#xff5b;&#xff5d;&#xff08;其实有2个属性&#xff0c;一个是constructor&a…

PVE grub resue错误修复 lvmid BUG

服务器断电后启动不起来&#xff0c;显示grub resue 找了半天没有找到修复方法。看官方文档有一处Recovering from grub “disk not found” error when booting from LVM 极为类似。https://pve.proxmox.com/wiki/Recover_From_Grub_Failure 下面是处理过程。 使用PVE 6.4启…

汽车研发项目进度管理的挑战与优化策略

随着汽车行业的快速发展和市场竞争的加剧&#xff0c;新车型研发项目的进度管理成为车企赢得市场的关键。然而&#xff0c;由于汽车研发项目通常具有投资大、周期长、技术难度高、参与方众多等特点&#xff0c;项目进度管理面临着诸多挑战。为了提升车型研发效率、缩短研发周期…

数据结构|树形结构|并查集

数据结构|并查集 并查集 心有猛虎&#xff0c;细嗅蔷薇。你好朋友&#xff0c;这里是锅巴的C\C学习笔记&#xff0c;常言道&#xff0c;不积跬步无以至千里&#xff0c;希望有朝一日我们积累的滴水可以击穿顽石。 有趣的并查集剧情演绎&#xff1a;【算法与数据结构】—— 并…

多个路由器连接的PC端进行ping通信需要做的事

实验环境&#xff1a; 三台PC三台路由器&#xff0c;并且配置好IP 拓扑图&#xff1a; 需求描述&#xff1a; 在PC0进行与PC2的ping通信&#xff1a; 需求步骤&#xff1a; 1.1首先配置ip&#xff08;略过&#xff09; 1.2我们首先查看在只配置了IP的情况下&#xff0c;P…

跨境电商指南:防关联浏览器和云主机有什么区别?

跨境电商的卖家分为独立站卖家和平台卖家。前者会自己开设独立站点&#xff0c;比如通过 shopify&#xff1b;后者则是入驻亚马逊或 Tiktok 等平台&#xff0c;开设商铺。其中平台卖家为了扩大收益&#xff0c;往往不止开一个店铺&#xff0c;或者有店铺代运营的供应商&#xf…

java音乐播放器系统设计与实现springboot-vue

后端技术 SpinrgBoot的主要优点有&#xff1a; 1、为所有spring开发提供了一个更快、更广泛的入门体验&#xff1b; 2、零配置&#xff1b; 3、集成了大量常用的第三方库的配置&#xff1b; Maven: 项目管理和构建自动化工具&#xff0c;用于java项目。 java: 广泛使用的编程语…

Docker Desktop打开一直转圈的解决办法

安装Docker Desktop之前确保你的Hyper-V已经打开 开启后需要重新安装重新安装重新安装这是最关键的一步&#xff0c;博主自己看了很多教程&#xff0c;最后试着重装了一下解决了 安装DockerDesktop的时候我的电脑根本就没有Hyper-V这个功能选项&#xff0c;可能是这个问题 如…

鸿蒙系列--ArkTS

一、ArkUI开发框架 ArkUI框架提供开发者两种开发方式&#xff1a;基于ArkTS的声明式开发范式和基于JS扩展的类Web开发范式。声明式开发范式更加简洁&#xff0c;类 Web 开发范式对 Web 及前端开发者更友好 二、ArkTS声明式开发范式 对比类 Web 开发范式代码更为精简&#xf…

Redis中的Lua脚本(五)

Lua脚本 脚本复制 复制EVALSHA命令 EVALSHA命令式所有与Lua脚本有关的命令中&#xff0c;复制操作最复杂的一个&#xff0c;因为主服务器与从服务器载入Lua脚本的情况可能有所不同&#xff0c;所以主服务器不能像复制EVAL命令、SCRIPT LOAD命令或者SCRIPT FLUSH命令那样&…

TaskWeaver使用记录

TaskWeaver使用记录 1. 基本介绍2. 总体结构与流程3. 概念细节3.1 Project3.2 Session3.3 Memory3.4 Conversation3.5 Round3.6 Post3.7 Attachment3.8 Plugin3.9 Executor 4. 代码特点5. 使用过程5.1 api调用5.2 本地模型使用5.3 添加插件 6. 存在的问题与使用体验6.1 判别模型…

C语言 | Leetcode C语言题解之第40题组合总和II

题目&#xff1a; 题解&#xff1a; int** ans; int* ansColumnSizes; int ansSize;int* sequence; int sequenceSize;int** freq; int freqSize;void dfs(int pos, int rest) {if (rest 0) {int* tmp malloc(sizeof(int) * sequenceSize);memcpy(tmp, sequence, sizeof(int…

Web前端 JavaScript笔记7

js的执行机制 js是单线程 同步&#xff1a;前面一个任务执行结束之后&#xff0c;执行后一个 异步&#xff1a;异步任务&#xff0c;引擎放在一边&#xff0c;不进入主线程&#xff0c;而进入任务队列的任务 js通过浏览器解析&#xff0c;浏览器靠引擎解析 回调函数同步任务执行…

锂电池寿命预测 | Matlab基于GRU门控循环单元的锂电池寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于GRU门控循环单元的锂电池寿命预测 Matlab基于GRU的锂电池剩余寿命预测 基于GRU的锂电池剩余寿命预测&#xff08;单变量&#xff09; 运行环境Matlab2020及以上 锂电池的剩余寿命预测是…

探索早期投资的奥秘:符文(Runes)生态系统的崛起

随着加密市场的迅速发展&#xff0c;投资者们对早期项目的关注越来越高。在这个充满变数和机遇的领域里&#xff0c;抢占先机意味着可能获得巨大的回报。符文&#xff08;Runes&#xff09;生态系统作为近期备受瞩目的项目之一&#xff0c;引发了众多投资者的兴趣。本文将深入探…

Java数据结构-二叉树

目录 1. 树与二叉树1.1 树1.2 二叉树1.3 树的相关概念1.4 特殊的二叉树1.5 二叉树性质1.6 二叉树的存储与表示方法 2. 二叉树遍历2.1 前序遍历2.2 中序遍历2.3 后序遍历2.4 层序遍历 3. 二叉树基本操作3.1 求树的所有结点个数3.2 求叶子结点个数3.3 求第k层结点个数3.4 求二叉树…