虚幻引擎UE5专用服务器游戏开发-07创建动画实例

1.创建动画基类:

2.重写三个函数:


	//本机初始化覆盖点
	virtual void NativeInitializeAnimation() override;

	//本机更新覆盖点。在这一步中简单地收集数据通常是个好主意
	//因为大部分工作都是在NativeThreadSafeUpdateAnimation中完成的。
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

	//本机线程安全更新覆盖点。在图更新之前在工作线程上执行
	//对于链接的anim实例,仅在宿主节点相关时调用
	virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;

在虚幻引擎的动画系统中,这三个函数是动画蓝图的核心更新点,分别承担不同的职责:

NativeInitializeAnimation()

  • 在动画蓝图首次创建时调用
  • 用于初始化动画状态和变量
  • 适合执行一次性初始化操作,如缓存常用组件引用

NativeUpdateAnimation(float DeltaSeconds)

  • 每帧在主游戏线程调用
  • 主要职责是收集角色状态数据(如速度、是否跳跃等)
  • 应避免复杂计算,仅做数据准备

NativeThreadSafeUpdateAnimation(float DeltaSeconds)

  • 在工作线程异步执行
  • 实际执行动画混合、状态机更新等计算密集型操作
  • 必须保证线程安全,避免访问非线程安全对象

典型实现模式:

  • NativeUpdateAnimation中获取角色移动速度、是否落地等基础状态
  • NativeThreadSafeUpdateAnimation中进行动画混合和状态过渡计算
  • 通过动画曲线和混合空间驱动最终动画表现

3.创建两个变量:

private:
	//所有者角色
	UPROPERTY()
	TObjectPtr<ACharacter> OwnerCharacter;
	//所有者移动组件
	UPROPERTY()
	TObjectPtr<UCharacterMovementComponent> OwnerMovementComponent;

变量初始化:

void UCAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();
	OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner());
	if (OwnerCharacter)
	{
		OwnerMovementComponent = OwnerCharacter->GetCharacterMovement();
	}
}

4.编译,打开UE编辑器:

角色添加动画蓝图:

视口显示:

运行游戏:

5.在C类添加变量速度:

	//速度
	UPROPERTY()
	float Speed;

赋值:

void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);
	if (OwnerCharacter)
	{
		Speed = OwnerCharacter->GetVelocity().Length();		//获取速度值
	}
}

添加函数:

	UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))
	FORCEINLINE float GetSpeed() const {return Speed;}
	
	UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))
	FORCEINLINE bool IsMoving() const {return Speed != 0;}
	
	UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))
	FORCEINLINE bool IsNotMoving() const {return Speed == 0;}
	

源代码:

Source/Crunch/Public/Animation/CAnimInstance.h:

// Copyright@ChenChao

#pragma once

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

class UCharacterMovementComponent;
/**
 * 
 */
UCLASS()
class CRUNCH_API UCAnimInstance : public UAnimInstance
{
	GENERATED_BODY()

public:

	//本机初始化覆盖点
	virtual void NativeInitializeAnimation() override;

	//本机更新覆盖点。在这一步中简单地收集数据通常是个好主意
	//因为大部分工作都是在NativeThreadSafeUpdateAnimation中完成的。
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

	//本机线程安全更新覆盖点。在图更新之前在工作线程上执行
	//对于链接的anim实例,仅在宿主节点相关时调用
	virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;

	UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))
	FORCEINLINE float GetSpeed() const {return Speed;}
	
	UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))
	FORCEINLINE bool IsMoving() const {return Speed != 0;}
	
	UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))
	FORCEINLINE bool IsNotMoving() const {return Speed == 0;}
	
private:
	//所有者角色
	UPROPERTY()
	TObjectPtr<ACharacter> OwnerCharacter;
	//所有者移动组件
	UPROPERTY()
	TObjectPtr<UCharacterMovementComponent> OwnerMovementComponent;
	//速度
	UPROPERTY()
	float Speed;
};

Source/Crunch/Private/Animation/CAnimInstance.cpp:

// Copyright@ChenChao


#include "Animation/CAnimInstance.h"

#include "GameFramework/Character.h"

void UCAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();
	OwnerCharacter = Cast<ACharacter>(TryGetPawnOwner());
	if (OwnerCharacter)
	{
		OwnerMovementComponent = OwnerCharacter->GetCharacterMovement();
	}
}

void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);
	if (OwnerCharacter)
	{
		Speed = OwnerCharacter->GetVelocity().Length();		//获取速度值
	}
}

void UCAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);
}

6.编译,到动画蓝图里

游戏就动起来啦!

7.为了是动画更自然,我们加入慢跑启动动画:

在慢跑状态里添加状态机:

为了使慢跑开始动画和慢跑的动画腿部相匹配,添加动画同步组:

过渡规则:

8.我们再添加一个慢跑停止的动画:

右边规则:

左边规则:

9.我们再加入前进时的倾斜动画:

(1)建立混合空间:

拖入3个前进动画:

将混合空间添加到:

(2)再到C++里设置参数:

	//偏航速度
	UPROPERTY()
	float YawSpeed;
	//平滑后的偏航速度
	UPROPERTY()
	float SmoothedYawSpeed;
	//平滑插值速度
	UPROPERTY(EditAnywhere,Category="Animation")
	float InterpSpeed = 1.f;	//一个用于平滑处理偏航角速度(Yaw Rate)的算法参数,其核心功能是通过插值(Lerp)实现旋转速度的平滑过渡,避免突变带来的不稳定性
	//身体上一帧旋转
	UPROPERTY()
	FRotator BodyPreviousRotation;

getter方法:

//获取偏航速度
	UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))
	FORCEINLINE float GetYawSpeed() const {return YawSpeed;}
	
	//获取平滑偏航速度
	UFUNCTION(BlueprintCallable, meta=(BlueprintThreadSafe))
	FORCEINLINE float GetSmoothedYawSpeed() const {return SmoothedYawSpeed;}

在NativeUpdateAnimation()里:

		// 获取身体旋转
		FRotator BodyRotation = OwnerCharacter->GetActorRotation();
		//计算当前身体旋转和上一帧身体旋转的差值
		FRotator BodyRotationDelta = UKismetMathLibrary::NormalizedDeltaRotator(BodyRotation,BodyPreviousRotation);
		BodyPreviousRotation = BodyRotation;

		//计算偏航速度
		YawSpeed = BodyRotationDelta.Yaw / DeltaSeconds;
		//平滑偏航速度
		SmoothedYawSpeed = UKismetMathLibrary::FInterpTo(SmoothedYawSpeed, YawSpeed, DeltaSeconds, InterpSpeed);

NormalizedDeltaRotator作用

  • 计算两个旋转量之间的最小角度差(标准化到[-180,180]范围)
  • 避免360°环绕问题(如从350°到10°会返回20°而非-340°
//平滑偏航速度
		SmoothedYawSpeed = UKismetMathLibrary::FInterpTo(SmoothedYawSpeed, YawSpeed, DeltaSeconds, InterpSpeed);

这段代码实现了虚幻引擎中偏航速度(Yaw Speed)的平滑过渡处理,是角色运动控制的关键技术。其核心原理和参数作用如下:

一、技术实现原理

  1. 指数平滑算法
    采用Current + (Target - Current) * (1 - exp(-InterpSpeed * DeltaTime))公式实现自然减速效果
  2. 帧率自适应
    通过DeltaSeconds参数自动适配不同帧率设备,确保相同InterpSpeed下过渡时间一致
  3. 运动学优化
    消除原始角速度YawSpeed的突变噪声,提升运动连贯性

二、参数配置建议

参数类型推荐值作用说明
InterpSpeedfloat3.0-8.0值越大响应越快,适用于需要快速转向的场景
DeltaSecondsfloat自动获取通过GetWorld()->GetDeltaSeconds()获取
SmoothedYawSpeedfloat动态值存储平滑处理后的角速度结果

三、典型应用场景

  1. 角色转向控制
    用于调整角色动画的转向混合权重,避免突然转身的僵硬感
  2. 相机跟随系统
    消除快速转向时的镜头抖动现象
  3. 载具物理模拟
    为转向力矩计算提供稳定的输入参数

四、相关函数对比

  • FInterpTo:标准浮点插值
  • RInterpTo:旋转量专用插值版本
  • FInterpConstantTo:线性匀速插值变体

编译,打开UE:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值