UE4_官方动画内容示例1.2_动画蓝图——使用蓝图告知Actor播放动画

文章详细介绍了动画蓝图在游戏开发中的应用,包括创建、编辑、与Actor交互以及使用接口进行行为统一。涉及的关键点包括动画蓝图的结构、事件图表与动画图表的作用,以及如何通过接口让多个Actor执行相同的交互动作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

展示了两个示例:在其中一个示例中,使用蓝图告知Actor播放动画,在另外一个示例中,展示了告知Actor播放动画的动画蓝图(例如,此示例展示了如何将变量从蓝图传递给动画蓝图,并演示了如何将现有姿势混合到新动画中)。

一、相关知识点:

动画蓝图:动画蓝图是可视化脚本,可用于创建和控制复杂动画行为。

动画蓝图是专用蓝图,它控制骨架网格体的动画。你可在动画蓝图编辑器中编辑动画蓝图图表(你可以在这里执行动画混合,直接控制骨架的骨骼,或设置最终将定义每一帧要使用的骨架网格体的最终动画姿势的逻辑)。

动画蓝图创建:指导如何从骨架资源创建新的动画蓝图

1、

2、指定动画蓝图的目标骨架。选择一个骨架并单击 确定(OK) 

您还可以为动画蓝图选择一个 父类 以供使用。 大多数情况下,您不需要选择,而可以使用默认类。

动画蓝图编辑器:

具有与普通 蓝图编辑器 相似的功能,但是更适合于进行角色动画脚本编写。

使用动画蓝图:

动画蓝图中包含两个主要组件,它们配合工作,以为每帧 创建最终动画。具体来说, 事件图表 更新 动画图表 中所采用的数值,这些数值随后用于驱动状态机、混合空间或其它节点,实现多个动画序列或姿势之间的混合,从而能够向其它系统发出通知,并实现动态动画驱动效果。

(一)事件图表

基于事件的图表,用于更新动画蓝图和计算动画图表中使用的值。

每个动画蓝图都有单独的 事件图表 ,该图表是一个标准 图表 ,它使用一组与动画相关的特殊事件来初始化节点序列。事件图表最常见的用途是更新 混合空间 和其他混合节点使用的值,以驱动动画图表中的动画。

动画事件

动画系统调用各种事件来初始化和更新动画蓝图。您可以将它们放置在事件图表

<think>我们正在讨论的是在UE5中使用纯C++播放动画,而不通过动画蓝图。根据引用[2]中的描述,我们可以通过C++直接控制动画播放。下面将介绍如何实现。 在UE中,动画通常通过`UAnimInstance`类来处理,但如果我们不想使用动画蓝图(即不想创建`UAnimInstance`的蓝图子类),我们可以直接在角色或组件的C++代码中操作动画。 步骤: 1. 确保你的角色或Actor拥有一个`USkeletalMeshComponent`。 2. 在C++类中,我们需要获取到`USkeletalMeshComponent`,然后通过它来播放动画。 3. 使用`UAnimationAsset`(具体来说是`UAnimSequence`)来指定要播放动画。 注意:我们需要在C++中引用动画资源。可以通过以下方式之一: - 在C++中硬编码动画路径,使用`ConstructorHelpers::FObjectFinder`来加载(仅适用于构造函数)。 - 通过编辑器将动画资源作为`UPROPERTY(EditAnywhere)`暴露,然后在编辑器中设置。 - 在运行时动态加载(例如,从已知路径或数据资产中加载)。 下面是一个示例,假设我们有一个角色类`AMyCharacter`,它有一个`USkeletalMeshComponent`组件。 首先,在头文件中声明: ```cpp #include "Animation/AnimSequence.h" UCLASS() class MYPROJECT_API AMyCharacter : public ACharacter { GENERATED_BODY() public: // 声明一个可编辑的动画序列,这样我们可以在编辑器中指定动画 UPROPERTY(EditAnywhere, Category = "Animation") UAnimSequence* IdleAnimation; // 或者,我们可以通过代码加载,但这里我们使用可编辑的方式 // 我们还可以添加其他动画,比如WalkAnimation, JumpAnimation等 // 播放动画的函数 void PlayIdleAnimation(); }; ``` 在源文件中: ```cpp AMyCharacter::AMyCharacter() { // 在构造函数中,我们可以尝试加载动画(可选,如果不想在编辑器中设置,可以在这里硬编码路径) // 注意:硬编码路径可能在项目迁移时出现问题,且无法在编辑器中覆盖 static ConstructorHelpers::FObjectFinder<UAnimSequence> IdleAnimFinder(TEXT("/Game/Animations/IdleAnimation")); if (IdleAnimFinder.Succeeded()) { IdleAnimation = IdleAnimFinder.Object; } } void AMyCharacter::PlayIdleAnimation() { // 确保我们有骨骼网格体组件 USkeletalMeshComponent* SkeletalMesh = GetMesh(); if (SkeletalMesh && IdleAnimation) { // 播放动画 SkeletalMesh->PlayAnimation(IdleAnimation, true); } } ``` 但是,请注意:`USkeletalMeshComponent::PlayAnimation`函数在UE4/5中已经被标记为废弃(deprecated)。在较新版本中,推荐使用动画实例(`UAnimInstance`)来控制动画,但如果我们不想使用动画蓝图,我们可以直接使用`UAnimInstance`的C++类。 另一种方法是直接使用`UAnimInstance`的C++类,而不创建蓝图。我们可以这样: 1. 创建一个C++类继承自`UAnimInstance`。 2. 在这个类中,我们可以实现动画状态机、混合空间等逻辑,但全部用C++代码编写。 3. 然后,在我们的角色中设置这个`UAnimInstance`类。 但是,用户要求不使用动画蓝图(即不想通过蓝图来编辑动画状态机),所以我们可以考虑在C++中创建`UAnimInstance`的子类,并完全用C++控制动画。 然而,根据引用[3]中的代码,我们看到在动画蓝图的C++类中,我们可以通过重写`NativeInitializeAnimation`和`NativeUpdateAnimation`函数来更新动画属性。但用户要求不通过动画蓝图,所以我们这里不使用自定义的`UAnimInstance`,而是直接使用`USkeletalMeshComponent`的`PlayAnimation`(已废弃)或者使用`UAnimInstance`的简单播放函数。 在UE5中,推荐使用`UAnimInstance`来播放动画,即使我们不想用蓝图,我们也可以创建一个纯C++的`UAnimInstance`子类,然后在其中播放动画。 下面介绍纯C++方式(不使用动画蓝图资源,但使用C++的`UAnimInstance`子类): 步骤: 1. 创建一个C++类,继承自`UAnimInstance`。 2. 在这个类中,我们可以定义动画状态和播放函数。 3. 在角色类中,设置骨骼网格体组件的`AnimClass`为我们创建的C++类。 但是,用户可能希望连`UAnimInstance`的子类都不使用,那么我们可以使用`USkeletalMeshComponent`的`PlayAnimation`的替代方法:使用`UAnimMontage`或者直接使用`UAnimInstance`的`PlaySlotAnimationAsDynamicMontage`等函数,但前提是我们有一个`UAnimInstance`对象。 实际上,`USkeletalMeshComponent`内部有一个`UAnimInstance`,我们可以通过`GetAnimInstance()`获取它,然后直接在这个实例上播放动画。 因此,我们可以这样实现(不使用自定义的`UAnimInstance`子类,而是使用默认的`UAnimInstance`): ```cpp void AMyCharacter::PlayIdleAnimation() { USkeletalMeshComponent* SkeletalMesh = GetMesh(); if (SkeletalMesh) { UAnimInstance* AnimInstance = SkeletalMesh->GetAnimInstance(); if (AnimInstance && IdleAnimation) { // 使用PlayAnimation函数(注意:这个函数在UE5中可能已被移除,我们需要使用其他方法) // 在UE5中,我们可以使用Montage播放,或者使用AnimInstance的PlaySlotAnimationAsDynamicMontage // 方法1:直接播放动画序列(不通过蒙太奇) - 但UE5中可能没有直接播放序列的函数,所以我们可以使用蒙太奇的方式 // 创建一个动态蒙太奇并播放 AnimInstance->PlaySlotAnimationAsDynamicMontage(IdleAnimation, "DefaultSlot", 0.25f, 0.25f, 1.0f, 1, 0.0f); } } } ``` 注意:`PlaySlotAnimationAsDynamicMontage`需要一个插槽名称(Slot Name),这里我们使用"DefaultSlot",这个插槽在大多数动画蓝图中都存在。如果你的骨骼网格体没有这个插槽,可能需要先创建一个。 但是,这种方法实际上是在播放一个动态蒙太奇,它会在指定的插槽中播放动画。如果你想要停止当前动画并立即播放新的动画,这个方法很有效。 另外,我们也可以使用`UAnimInstance::Montage_Play`来播放一个蒙太奇资源,但我们需要先有一个`UAnimMontage`。如果我们只有`UAnimSequence`,那么`PlaySlotAnimationAsDynamicMontage`是一个方便的选择。 总结:在UE5中,纯C++播放动画序列(不使用动画蓝图)的步骤: 1. 在角色类中,通过`UPROPERTY(EditAnywhere)`引用一个`UAnimSequence`资源(或者通过其他方式获取)。 2. 在需要播放动画的地方,获取骨骼网格体组件的`UAnimInstance`。 3. 调用`UAnimInstance::PlaySlotAnimationAsDynamicMontage`来播放动画序列。 注意:这种方法不需要创建动画蓝图资源,但需要确保骨骼网格体组件使用动画实例(`UAnimInstance`)具有指定的插槽(Slot)。默认情况下,如果角色使用的是`UAnimInstance`(而不是自定义的子类),那么它可能没有插槽管理器。因此,我们可能需要一个自定义的`UAnimInstance`类(纯C++)来提供插槽,但这样又需要创建C++子类。 为了避免创建自定义的`UAnimInstance`子类,我们可以使用引擎默认的`UAnimInstance`,它默认支持"DefaultSlot"吗?实际上,默认的`UAnimInstance`没有初始化任何插槽管理器,所以我们需要在角色初始化时确保插槽管理器存在。 因此,更简单的方式是:我们创建一个非常简单的`UAnimInstance`子类(C++),它只初始化插槽管理器。这样我们就可以使用插槽了。 创建自定义的`UAnimInstance`子类(C++): ```cpp // 头文件 #pragma once #include "CoreMinimal.h" #include "Animation/AnimInstance.h" #include "MyAnimInstance.generated.h" UCLASS() class MYPROJECT_API UMyAnimInstance : public UAnimInstance { GENERATED_BODY() public: UMyAnimInstance(); }; ``` ```cpp // 源文件 #include "MyAnimInstance.h" UMyAnimInstance::UMyAnimInstance() { // 在构造函数中,我们可以初始化插槽管理器吗?实际上,插槽管理器在初始化时创建,我们不需要额外操作。 // 但是,我们需要确保有一个插槽。通常,插槽管理器会在PostInitAnimTree中初始化,但我们也可以手动添加插槽。 } ``` 然后,在角色的骨骼网格体组件中设置这个动画实例类: ```cpp AMyCharacter::AMyCharacter() { // 假设骨骼网格体组件是继承自ACharacter的GetMesh() GetMesh()->SetAnimInstanceClass(UMyAnimInstance::StaticClass()); } ``` 这样,我们就可以使用`PlaySlotAnimationAsDynamicMontage`了,因为`UAnimInstance`默认会有一个插槽管理器(在初始化时创建)。 但是,如果我们不想创建这个自定义的`UAnimInstance`子类,我们可以尝试在运行时添加插槽: ```cpp void AMyCharacter::PlayIdleAnimation() { USkeletalMeshComponent* SkeletalMesh = GetMesh(); if (SkeletalMesh) { UAnimInstance* AnimInstance = SkeletalMesh->GetAnimInstance(); if (AnimInstance && IdleAnimation) { // 检查插槽管理器是否存在,如果不存在则创建 if (!AnimInstance->GetSlotManager()) { AnimInstance->InitSlotManager(); } // 现在可以播放 AnimInstance->PlaySlotAnimationAsDynamicMontage(IdleAnimation, "DefaultSlot", 0.25f, 0.25f, 1.0f, 1, 0.0f); } } } ``` 但是,`InitSlotManager`是`UAnimInstance`的受保护函数,我们不能直接调用。因此,我们只能通过自定义`UAnimInstance`子类来确保插槽管理器被正确初始化。 因此,推荐的方法是创建一个自定义的`UAnimInstance`子类(纯C++),然后在角色中设置使用这个类。这样,我们就可以使用插槽播放动画了。 结论: 在UE5中,使用纯C++播放动画序列(不使用动画蓝图资源)的最直接方法是: 1. 创建一个自定义的`UAnimInstance`的C++子类(不需要任何额外代码,只需要存在即可)。 2. 在角色或拥有骨骼网格体的Actor中,设置骨骼网格体组件的`AnimClass`为我们自定义的`UAnimInstance`类。 3. 在需要播放动画的地方,通过骨骼网格体组件获取`UAnimInstance`,然后调用`PlaySlotAnimationAsDynamicMontage`播放动画序列。 示例代码: 自定义AnimInstance类(仅用于提供插槽管理器): ```cpp // MyAnimInstance.h #pragma once #include "CoreMinimal.h" #include "Animation/AnimInstance.h" #include "MyAnimInstance.generated.h" UCLASS() class MYPROJECT_API UMyAnimInstance : public UAnimInstance { GENERATED_BODY() }; ``` 在角色中设置: ```cpp // 在角色头文件中 #include "MyAnimInstance.h" // 在构造函数中 AMyCharacter::AMyCharacter() { // 设置自定义的AnimInstance类 GetMesh()->SetAnimInstanceClass(UMyAnimInstance::StaticClass()); } ``` 播放动画: ```cpp void AMyCharacter::PlayIdleAnimation() { if (USkeletalMeshComponent* Mesh = GetMesh()) { if (UAnimInstance* AnimInstance = Mesh->GetAnimInstance()) { if (IdleAnimation) { // 播放动画 AnimInstance->PlaySlotAnimationAsDynamicMontage(IdleAnimation, "DefaultSlot", 0.25f, 0.25f, 1.0f, 1, 0.0f); } } } } ``` 这样,我们就实现了纯C++播放动画序列,且没有使用动画蓝图资源(仅使用了C++类)。 注意:这种方法播放动画会覆盖在插槽上,因此不会影响基础姿势(例如,如果你同时有移动动画,那么移动动画会被覆盖)。如果你希望动画混合,可以调整混合参数。 另外,停止动画可以使用: ```cpp AnimInstance->StopSlotAnimation(BlendOutTime, "DefaultSlot"); ``` 以上就是在UE5中使用纯C++播放动画的方法。 引用说明: 根据引用[2]中提到的“24K纯C++”实现动画播放,我们通过自定义`UAnimInstance`的C++类以及直接调用播放函数来实现[^2]。 根据引用[3],我们在动画蓝图的C++类中更新动画属性,但这里我们并没有使用动画蓝图,所以没有使用到[^3]。 因此,我们主要参考了引用[2]的思路。 相关问题: 1. 如何在UE5中创建自定义的AnimInstance C++类? 2.UE5中,如何通过C++动态加载动画资源? 3. 如何控制动画的混合和过渡? 4. 怎样停止正在播放动画?</think>在 Unreal Engine 5 中,完全不使用动画蓝图实现纯 C++ 动画播放需要直接操作骨骼网格组件和动画资源。以下是详细实现步骤: ### 核心步骤与代码实现 #### 1. **准备动画资源** 在头文件中声明动画资源引用: ```cpp // MyCharacter.h #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "MyCharacter.generated.h" UCLASS() class MYPROJECT_API AMyCharacter : public ACharacter { GENERATED_BODY() public: // 声明可编辑的动画资源 UPROPERTY(EditAnywhere, Category = "Animation") UAnimSequence* IdleAnimation; UPROPERTY(EditAnywhere, Category = "Animation") UAnimSequence* RunAnimation; // 播放动画的函数 void PlayAnimation(UAnimSequence* Animation); }; ``` #### 2. **实现动画播放逻辑** 在源文件中实现播放功能: ```cpp // MyCharacter.cpp #include "MyCharacter.h" #include "Animation/AnimInstance.h" void AMyCharacter::PlayAnimation(UAnimSequence* Animation) { if (!Animation || !GetMesh()) return; UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance(); if (!AnimInstance) return; // 直接播放动画序列 AnimInstance->PlaySlotAnimationAsDynamicMontage( Animation, // 动画资源 "DefaultSlot",// 动画插槽 0.25f, // 混合进入时间 0.25f, // 混合退出时间 1.0f, // 播放速率 1, // 循环次数 0.0f // 开始时间 ); } // 示例调用 void AMyCharacter::BeginPlay() { Super::BeginPlay(); PlayAnimation(IdleAnimation); // 初始播放待机动画 } void AMyCharacter::Move() { PlayAnimation(RunAnimation); // 移动时播放跑步动画 } ``` #### 3. **编辑器设置** 1.内容浏览器创建动画资源 2. 在角色蓝图中: - 将动画资源拖拽到`IdleAnimation`和`RunAnimation`属性槽 - 确保骨骼网格组件已正确设置 ### 替代方案:动态加载资源 ```cpp // 动态加载动画资源 void AMyCharacter::LoadAndPlay(FString Path) { UAnimSequence* Animation = LoadObject<UAnimSequence>( nullptr, *Path, nullptr, LOAD_None, nullptr ); if (Animation) PlayAnimation(Animation); } // 调用示例 LoadAndPlay("/Game/Animations/RunAnimation.RunAnimation"); ``` ### 关键要点 1. **直接操作`UAnimInstance`**:通过骨骼网格组件获取动画实例直接控制播放 2. **插槽系统**:使用`PlaySlotAnimationAsDynamicMontage`在指定插槽播放动画 3. **资源引用**:通过`UPROPERTY(EditAnywhere)`在编辑器设置动画资源 4. **动态加载**:使用`LoadObject`在运行时加载资源(需准确路径) 5. **避免蓝图**:完全绕过动画蓝图和状态机 > 此方法直接操作底层动画系统,性能优于蓝图方案,特别适合需要批量管理动画或动态生成动画逻辑的场景[^2]。 ### 注意事项 1. 动画路径格式:`/Game/文件夹/资源名.资源名` 2. 确保骨骼兼容:动画资源必须与角色骨骼匹配 3. 插槽名称:`"DefaultSlot"`是通用插槽,可自定义 4. 混合参数:调整混合时间实现平滑过渡
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Growthofnotes

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值