写过一篇 发表于 2024-8-11 07:53:09

《UE5_C++多人TPS完整教程》学习笔记32 ——《P33 动画蓝图(Animation Blu

本文为B站系列讲授视频 《UE5_C++多人TPS完整教程》 —— 《P33 动画蓝图(Animation Blueprint)》 的学习笔记,该系列讲授视频为 Udemy 课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。


P33 动画蓝图(Animation Blueprint)

本节课将在上节课的基础上为角色移动的创建动画,因此我们必要创建动画实例类(Ainminstance),它是一个基于动画蓝图的 C++ 类。
https://i-blog.csdnimg.cn/blog_migrate/18ae192b1908ba65f8ddade9628ae839.png
33.1 创建动画实例 C++ 类及蓝图类


[*] 在虚幻引擎内容欣赏器中的 “/Blaster/Character” 目次下新建一个 “Animinstance” C++ 类,命名为 “BlasterAnimInstance”。
https://i-blog.csdnimg.cn/direct/3038b1a3ecfc41c7a960fb11c87ef5b9.png
https://i-blog.csdnimg.cn/direct/998565b4c72b4538974b475b78160f17.png
https://i-blog.csdnimg.cn/direct/0dbc3bb9e0864206b0ef1a1f1247191b.png
[*] 在 Visual Studio 中打开头文件 “BlasterAnimInstance.h,声明重写原生(Native)动画类初始化函数和更新函数,以及使用动画实例的角色类,角色类实例的速度、是否在空中、是否在加速。
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"



#include "BlasterAnimInstance.generated.h"

/**
*
*/
UCLASS()
class BLASTER_API UBlasterAnimInstance : public UAnimInstance
{
        GENERATED_BODY()

/* P33 动画蓝图(Animation Blueprint)*/
public:
        virtual void NativeInitializeAnimation() override;                                                                                        // 原生(Native)动画类初始化函数 NativeInitializeAnimation() 重写
        // https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/Animation/UAnimInstance/NativeInitializeAnimation

        virtual void NativeUpdateAnimation(float DeltaTime) override;                                                                // 原生(Native)动画类更新函数 NativeUpdateAnimation() 重写
        // https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/Animation/UAnimInstance/NativeUpdateAnimation

private:
        // UPROPERTY:https://dev.epicgames.com/documentation/zh-cn/unreal-engine/unreal-engine-uproperties?application_version=5.4
        UPROPERTY(BlueprintReadOnly, Category = Character, meta = (AllowPrivateAccess = "true"))        // 属性说明符:仅在蓝图中可读,类别为 “Character”;
                                                                                                                                                                                                // 元数据说明符:由于变量为私有变量,需要设置 AllowPrivateAccess 为 true 才能在蓝图中可读
        class ABlasterCharacter* BlasterCharacter;        // 使用动画实例的角色类                                                       
       
        UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))                // 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;
        float Speed;                                                                // 运动速度

        UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))                // 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;
        bool bIsInAir;                                                                // 是否在空中

        UPROPERTY(BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))                // 属性说明符:仅在蓝图中可读,类别为 “Movemonet”;
        bool bIsAccelerating;                                                // 是否在加速
/* P33 动画蓝图(Animation Blueprint)*/
};


       从最基本的层次上讲,UnrealScript中有两种类型的类:native和非-native。Native类是一个具有native代码的简单的类,或者是使用引擎的native语言书写的代码,这里是C++。这不是说native类不能包含使用UnrealScript书写的代码,只是说native类中结合了C++代码。在游戏中出现的很多底层的类都是native类,由于它们有很多受益于native代码实行速度的复杂函数。非-native类是全部使用UnrealScript书写的。由于所有的类都是从native类Object继承而来,所以通过继承关系,这些非-native类有一些和它们相关的native代码,但是它们自己没有明显的native代码。您将要书写的所有类都是非-native类,由于创建native类必要重新编译引擎的源代码。
        ——《(转载)虚幻引擎3–第三章–Unreal中的类》
[*] 打开 “BlasterAnimInstance.cpp”,完成 “NativeInitializeAnimation()” 函数和 “NativeUpdateAnimation()” 函数的界说,然后进行编译。
// Fill out your copyright notice in the Description page of Project Settings.

/* P33 动画蓝图(Animation Blueprint)*/
#include "BlasterAnimInstance.h"        // 原来自动生成的代码是 #include "Character/BlasterAnimInstance.h",这里需要把 "Character/" 去掉,否则找不到文件 "BlasterAnimInstance.h"

#include "BlasterCharacter.h"

#include "GameFramework/CharacterMovementComponent.h"

void UBlasterAnimInstance::NativeInitializeAnimation()                                        // 原生(Native)类初始化函数 NativeInitializeAnimation() 重写
{
        Super::NativeInitializeAnimation();                                                                        // 调用父类 AnimInstance 的 NativeInitializeAnimation() 函数

        // https://blog.csdn.net/ttm2d/article/details/106550682
        BlasterCharacter = Cast<ABlasterCharacter>(TryGetPawnOwner());                // 获取动画蓝图实例所属,并向下强制转换(Cast)为 ABlasterCharacter 类

}

void UBlasterAnimInstance::NativeUpdateAnimation(float DeltaTime)                // 原生(Native)类更新函数 NativeUpdateAnimation() 重写,用于在每一帧调用以更新动画
{
        Super::NativeUpdateAnimation(DeltaTime);                                                        // 调用父类 AnimInstance 的 NativeUpdateAnimation() 函数

        if (BlasterCharacter == nullptr) {                                                                        // 检查 BlasterCharacter 是否声明
                BlasterCharacter = Cast<ABlasterCharacter>(TryGetPawnOwner());        // 获取动画蓝图实例所属,并向下强制转换(Cast)为 ABlasterCharacter 类
        }
        if (BlasterCharacter == nullptr) return;

       
        FVector Velocity = BlasterCharacter->GetVelocity();                                        // 获取角色速度向量
        Velocity.Z = 0.f;                                                                                                        // 不关心 Z 轴速度,设置为 0
        Speed = Velocity.Size();                                                                                        // 获取角色速度向量的模(大小)

        bIsInAir = BlasterCharacter->GetCharacterMovement()->IsFalling();        // 调用 GetCharacterMovement()->IsFalling() 函数判断角色是否掉落从而判断角色是否在空中,
                                                                                                                                                // 需要添加头文件 "GameFramework/CharacterMovementComponent.h"

        bIsAccelerating = BlasterCharacter->GetCharacterMovement()->GetCurrentAcceleration().Size() > 0 ? true : false;        // 调用 GetCharacterMovement()->GetCurrentAcceleration() 获取角色加速度
                                                                                                                                                                                                                                        // 判断加速度是否的维度是否大于 0 ,大于 0 说明某个方向上有加速度
}
/* P33 动画蓝图(Animation Blueprint)*/

[*] 在虚幻引擎内容欣赏器的 “/Blueprints/Character/” 目次下新建文件夹 “Animation”,创建动画蓝图(Animation Blueprint),在对话框中选择骨骼为 “SK_EpicCharacter_Skeleton”,动画蓝图重命名为 “BlasterAnimBP”。
https://i-blog.csdnimg.cn/direct/f4166c44e3c14af1b231075e6d3d5aba.png
https://i-blog.csdnimg.cn/direct/1d60f8fbbe544d60b2f2b636aceb62a3.png
33.2 在动画实例蓝图中创建状态机


[*] 双击 “BlasterAnimBP”,在动画蓝图编辑器中打开 “BlasterAnimBP” 的类设置(Class Settings)面板,设置父类(Parent Class)为 “BlasterAnimInstance”。
https://i-blog.csdnimg.cn/direct/d43e168770774e168452ce75b30342ce.png
https://i-blog.csdnimg.cn/direct/742dbf72784140cd9d66095bc0f879ec.png
[*] 在 “AnimGraph” 面板中添加新的动画状态机(State Machine)节点,重命名为 “Unequipped” ,并将该节点的输出引脚 “Animation Pose” 毗连到 “输出姿势”(Output Pose)节点的 “Result”引脚上。
https://i-blog.csdnimg.cn/direct/03d5d7688e0b4fd3822fc1d6aceefc89.png
https://i-blog.csdnimg.cn/direct/315f70cdd607423d820cf3a2143580ef.png
[*] 双击 “Unequipped” 节点,进入状态机编辑界面,按次序依次添加状态节点 “IdleWalkRun”、“JumpStart”、“Falling” 和 “JumpStop”。
https://i-blog.csdnimg.cn/direct/9d81161a91854c398c497d7c404cf2d4.png
[*] 双击 “IdleWalkRun to JumpStart” 转换https://i-blog.csdnimg.cn/direct/a2023ff9942e47339ed56ab491ca826c.png按钮,添加 “Get is in air” 节点,并将该节点的输出引脚和自动天生的 “Result” 节点的 “Can Enter Transition” 引脚毗连,这段蓝图体现如果角色在空中则转换为起跳状态。https://i-blog.csdnimg.cn/direct/a934f3f0d33d4d94b225e89257cfac7f.png
https://i-blog.csdnimg.cn/direct/2daff6ca026648f29f1269a5efc98218.png
https://i-blog.csdnimg.cn/direct/b09f6561a3a949edb773d060c940bf26.png
[*] 对于 “JumpStart to Falling” 状态转换,在右侧 “细节”(Details) 面板启用 “基于状态中的序列播放器的自动规则”(Automatic Rule Based on Sequence Player in State)选项,如许在状态 “JumpStart” 动画播放完毕状态结束后会自动过渡到状态 “Falling”。
https://i-blog.csdnimg.cn/direct/f4522012640b41428916644c20bc5a69.png
       基于状态中的序列播放器的自动规则(Automatic Rule Based on Sequence Player in State): 启用后,该转换节点将实验根据最相关资产播放器节点的剩余时间和 AutomaticRuleTriggerTime 属性的值自动开始转换,忽略内部时间。此属性可用于在第一次动画播放期间,通过更快地开始混淆,更动态地在状态转换之间混淆。
        —— 虚幻引擎官方文档 《转换规则》
[*] 对于 “Falling to JumpStop” 的状态转换,绘制雷同于 “IdleWalkRun to JumpStart” 转换规则的蓝图,只不过必要添加一个 “NOT Boolean” 节点,体现角色不在空中跳跃结束。
https://i-blog.csdnimg.cn/direct/fe3eeb7d298b402f906244d381e3dc37.png
https://i-blog.csdnimg.cn/direct/77007e4a3b534b6bbfd2308ee0af08f3.png
[*] 返回 “Unequipped” 状态机编辑界面,双击 “JumpStart” 节点,进入 “JumpStart” 状态编辑器,在右侧 “资产欣赏器”(Asset Browser)中将动画 “EpicCharacter_JumpUp_Start” 拖拽至编辑器中天生节点,然后与自动天生 “Output Animation Pose” 节点进行毗连;同理,分别进入 “Falling” 和 “JumpStop” 状态编辑器,将动画 “EpicCharacter_JumpUp_Loop” 和 “EpicCharacter_JumpUp_End” 拖拽至编辑器中进行毗连。
https://i-blog.csdnimg.cn/direct/ac0f3cb06a7f4394ac100c40bf55ac59.png
https://i-blog.csdnimg.cn/direct/c59ee6efafea4528b50d7c316b82c3ff.png
https://i-blog.csdnimg.cn/direct/0ec12084d8fe4986aa419cf6e634b891.png
https://i-blog.csdnimg.cn/direct/a873d724e37f4f2a80fc3e91f3be3eae.png
[*] 返回 “Unequipped” 状态机编辑界面,双击 “JumpStop to ldleWalkRun” 转换按钮,添加 “Time Remaining (ratio)(EpicCharacter_JumpUp_End)” 和 “Less equal”(<=)节点并进行毗连,这段蓝图体现当 “JumpStop” 的动画播放到还剩下 10% 的时间时,就由状态 “JumpStop” 转至状态 “IdleWalkRun”。
https://i-blog.csdnimg.cn/direct/14cc4d8f301c4bc1aad4ac583b3f2c1b.png
https://i-blog.csdnimg.cn/direct/3588d4d21cc645d9b18bb9cb1fbba3b5.png
33.3 创建一维混淆空间


[*] 对于角色程度状态移动的状态机 “IdleWalkRun” 节点,我们必要创建混淆空间(Blend Space),从而实现人物由怠速至行走至跑步的自然过渡。在内容欣赏器的 “/Blueprints/Character/Animation” 目次下新建 “一维混淆空间”(Blend Space 1D),命名为 “UnequippedIdleWalkRun”。
https://i-blog.csdnimg.cn/direct/48bc426874d143d7a0076beac32f1282.png
https://i-blog.csdnimg.cn/direct/193752f325f6425693ed3b5080d5e12e.png
       混淆空间是一种资产,答应混淆多个动画或姿势,方法是将其绘制到一维或二维图表中。然后,该图表可以在动画蓝图中进行引用,此中混淆通过Gameplay输入或其他变量进行控制。通过使用混淆空间,几乎所有类型的混淆布局都可以用于你的动画。
你可以在此中沿图表上的各个点指定动画,称为 示例(samples) 。总体动画通过基于每个轴的输入值在图表上的点之间混淆来盘算。例如,可以创建一个在方向性移动和空闲动画之间混淆的移动体系。
        —— 虚幻引擎官方文档 《混淆空间》
[*] 双击 “UnequippedIdleWalkRun”,进入混淆空间编辑器。在右侧 “资产欣赏器”(Asset Browser)中将动画 “EpicCharacter_Idle” 、“EpicCharacter_Walk” 和 “EpicCharacter_Run” 拖拽至编辑器下方 “动画序列” 面板中。
https://i-blog.csdnimg.cn/direct/d64eef67573c416297180b8fb8ad6a13.png
https://i-blog.csdnimg.cn/direct/484b04677b214fb59075e29a15a7e061.png
https://i-blog.csdnimg.cn/direct/ddd5fc0f171d492f9d226d95290df811.png
[*] 在左侧 “资产细节”(Asset Details)面板设置 “程度坐标”(Horizontal Axis) 的“名称”(Name)为 “Speed”,“最大轴值”(Maximum Axis Value)为 350,并在 “动画序列” 面板中对 “EpicCharacter_Walk” 和 “EpicCharacter_Run” 进行拖拽、调整。
https://i-blog.csdnimg.cn/direct/411b1f10624447b28b608b4480f152c6.png
https://i-blog.csdnimg.cn/direct/a0635e2dbe31420798d81ab82f0905a2.png
[*] 返回 “Unequipped” 状态机编辑界面,双击 “IdleWalkRun” 状态机编辑器,绘制以下蓝图,这段蓝图体现通过获取角色移动速度巨细传递至混淆空间中,以决定播放角色的哪个动画。绘制完成后,进行编译并生存。
https://i-blog.csdnimg.cn/direct/2c10275a00ff49509d3afe4d70fc8d79.png
[*] 打开 “BP_BlasterCharacter” 蓝图编辑器,在左侧 “组件”(Components)中选择 “网格体”(CharacterMesh0)(骨骼网格体组件)组件,然后在右侧 “细节” 面板中的 “动画”(Animations)选项卡下设置 “动画模式”(Animation Mode)为 “使用动画蓝图”(Use Animation Blueprint),“动画类”(Anim Class)选择我们创建的动画实例蓝图类 “BlasterAnimBP”。
https://i-blog.csdnimg.cn/direct/acaed8f14fa94fd2a362d9008c8a59a4.png
[*] 回到关卡主界面,播放游戏进行测试。起首测试角色移动,可以看到角色可以怠速,而且按下移动键可以由怠速自然过渡到跑起来。https://i-blog.csdnimg.cn/direct/817206d6685a43ceba7fa1ca54e803df.pnghttps://i-blog.csdnimg.cn/direct/8c5823154f1e43e1b6533c00b0a51449.pnghttps://i-blog.csdnimg.cn/direct/99bf330783954c978a05675d0492b5b3.png
[*] 但是角色在进行左右方向的移动时,角色移动的朝向有些奇怪,由于人物移动朝向始终朝着镜头进行横移。
https://i-blog.csdnimg.cn/direct/4393fac22b74431a9c01477ddf451343.png
下面进行修改。在 Visual Studio 打开 “BlasterCharacter.cpp”,添加代码设置人物不跟随控制器(镜头)转向,而且角色在移动时向加速度方向旋转,这一步也可以在虚幻引擎中 “BP_BlasterCharacter” 蓝图编辑器的细节面板对相应选项进行勾选实现。
// Fill out your copyright notice in the Description page of Project Settings.


#include "BlasterCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"

/* P33 动画蓝图(Animation Blueprint)*/
#include "GameFramework/CharacterMovementComponent.h"
/* P33 动画蓝图(Animation Blueprint)*/

// Sets default values
ABlasterCharacter::ABlasterCharacter()
{
        // Set this character to call Tick() every frame.You can turn this off to improve performance if you don't need it.
        PrimaryActorTick.bCanEverTick = true;

        // 创建弹簧臂对象 CameraBoom 并设置 CameraBoom 的默认属性
        CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));        // 基于弹簧臂组件类创建对象
        CameraBoom->SetupAttachment(GetMesh());                                                                                        // 设置弹簧臂附加到角色的骨骼网格体组件,如果附加到胶囊体上,角色在做蹲下的动作时,由于胶囊体的大小和路线会发生改变,弹簧臂的高度也会发生改变(弹簧臂将会移动)
        CameraBoom->TargetArmLength = 600.f;                                                                                        // 设置弹簧臂长度
        CameraBoom->bUsePawnControlRotation = true;                                                                                // 设置弹簧臂跟随角色控制器旋转

        // 创建摄像机对象 FollowCamera 并设置 FollowCamera 的默认属性
        FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));        // 基于摄像机组件类创建对象
        FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);                // 将摄像机附加到弹簧臂 CameraBoom 上,并指定插槽名为虚幻引擎摄像机组件成员变量 SocketName
        FollowCamera->bUsePawnControlRotation = false;                                                                        // 设置摄像机不跟随角色控制器旋转

        // 调整弹簧臂和摄像机的相对位置(也可以在虚幻引擎的蓝图编辑器中进行设置)
        CameraBoom->SetRelativeLocation(FVector(0, 0, 88));                                                                // 设置弹簧臂和摄像机在蓝图类 “BP_BlasterCharacter” 的相对位置为 (0, 0, 88),以避免它们与地面相撞

        /* P33 动画蓝图(Animation Blueprint)*/
        bUseControllerRotationYaw = false;                                                                                                // 设置人物不跟随控制器(镜头)转向,也可以在 BP_BlasterCharacter 蓝图编辑器中实现
        GetCharacterMovement()->bOrientRotationToMovement = true;                                                // 获取角色移动组件,角色移动时向加速度方向旋转角色,也可以在 BP_BlasterCharacter 蓝图编辑器中实现
        /* P33 动画蓝图(Animation Blueprint)*/
}
https://i-blog.csdnimg.cn/direct/c1e7d1ee9e20416ca815b896412d26e4.png
https://i-blog.csdnimg.cn/direct/1a69208562d94e389f4c210b92b29afa.png
https://i-blog.csdnimg.cn/direct/e3145ebaf8c841d599c3c81217e03a6d.png
       User Controller Rotaion Yaw与User Controller Desired Rotation都将人物与镜头视角绑定,即让人物始终跟随镜头转向,区别在于第二个会让转向更加平滑的过分,也可以设置转向的速度,第一个就直接跟鼠标或手柄移动一致转向,如果想让转向更平滑可以设置第二个bool值为true即可,Orient Rotaion to Movement则在没有wasd这种move输入的时候不会让人物转向,在人物跑动的时候会有转向效果但是没有今后跑的效果,按s键人物正面就会朝着玩家,前两个bool值则相反。
        —— 《虚幻4人物转向题目{User Controller Rotaion Yaw,User Controller Desired Rotation与Orient Rotation to Movement}》
          在TPS(第三人称)游戏中,我设定bAim为true 举枪射击,bUseControllerRotationYaw为真,则我可以按s后退 这时我们必要将bOrientRotationToMovement设置为false,即不旋转玩家朝向后转身,我瞄准时肯定要保持朝向射击方向……
需牢记 若使用 二者 则bUseControllerRotationYaw 与 bOrientRotationToMovement 布尔值要相反。
        —— 《UE4笔记 之 bOrientRotationToMovement 》
[*] 接着,测试角色跳跃,可以发现角色在起跳和落下的动画都没题目,但是在落地时会播放两次 “EpicCharacter_JumpUp_End” 动画,而且第二次播放得很快,看起来非常奇怪。
https://i-blog.csdnimg.cn/direct/c7b2807ead3a436d9699d6cb375c094c.png
https://i-blog.csdnimg.cn/direct/13369bf73cce43948526c688df533c12.png
https://i-blog.csdnimg.cn/direct/782f3dda2708438797e0146bb0126701.png
下面进行修改。我们只必要在 “JumpStop” 状态机编辑器右侧 “细节” 面板中取消勾选 “设置”(Settings)选项卡下的 “重复动画”(Loop Animation) 复选框即可,最后进行编译和生存,再次测试会发现角色跳跃动画正常。
https://i-blog.csdnimg.cn/direct/d5fff6c1c7f445d687fa15e274748a3b.png
https://i-blog.csdnimg.cn/direct/670631dd8db94f5498f0c2849230a0b4.png
33.4 Summary

本节课为了让角色移动时能播放相应的动画,我们起首创建了动画实例 C++ 类 “BlasterAnimInstance”,基于这个 C++ 类进一步创建动画实例蓝图类 “BlasterAnimBP”;接着,为在 “BlasterAnimBP” 蓝图编辑器中创建状态机,以实现角色程度方向运动和竖直方向跳跃之间的状态转换;然后,为了实现角色在程度方向运动时从怠速、走路以及跑步的自然过渡,我们创建了以角色移动速度巨细为参量的一维混淆空间;最后我们将动画实例蓝图类 “BlasterAnimBP”运用到角色蓝图类 “BP_BlasterCharacter” 中,并对角色程度方向运动和竖直方向跳跃进行了测试,修复了角色在程度方向运动出现的朝向题目以及在竖直方向跳跃时落地动画重复播放的题目。
https://i-blog.csdnimg.cn/direct/96b989790cd54cc9bd50a1ea984c7af4.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 《UE5_C++多人TPS完整教程》学习笔记32 ——《P33 动画蓝图(Animation Blu