视频学习:2-9_哔哩哔哩_bilibili

整体结构

创建好后,编辑UWarriorCharacterAnimInstance类

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "AnimInstances/WarriorBaseAnimInstance.h"
#include "WarriorCharacterAnimInstance.generated.h"

class AWarriorBaseCharacter;
class UCharacterMovementComponent;
/**
 * 
 */
UCLASS()
class WARRIOR_API UWarriorCharacterAnimInstance : public UWarriorBaseAnimInstance
{
	GENERATED_BODY()
	
public:
	virtual void NativeInitializeAnimation() override;
	virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;

protected:
	UPROPERTY()
	AWarriorBaseCharacter* OwningCharacter;

	UPROPERTY()
	UCharacterMovementComponent* OwningMovementComponent;

	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "AnimData|LocomotionData")
	float GroundSpeed;//角色速度

	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "AnimData|LocomotionData")
	bool bHasAcceleration;//是否有加速度
};
  • NativeInitializeAnimation一次性的初始化设置。

  • NativeThreadSafeUpdateAnimation每帧进行的、线程安全的属性更新和逻辑计算。

// Fill out your copyright notice in the Description page of Project Settings.


#include "AnimInstances/WarriorCharacterAnimInstance.h"
#include "Characters/WarriorBaseCharacter.h"
#include "GameFramework/CharacterMovementComponent.h"

void UWarriorCharacterAnimInstance::NativeInitializeAnimation()
{
	OwningCharacter = Cast<AWarriorBaseCharacter>(TryGetPawnOwner());

	if (OwningCharacter)
	{
		OwningMovementComponent = OwningCharacter->GetCharacterMovement();
	}
}

void UWarriorCharacterAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
	if (!OwningCharacter || !OwningMovementComponent)
	{
		return;
	}

	GroundSpeed = OwningCharacter->GetVelocity().Size2D();

	bHasAcceleration = OwningMovementComponent->GetCurrentAcceleration().SizeSquared2D() > 0.f;

}

这里使用if而不是check检测原因

NativeInitializeAnimation 被调用的时机非常早。此时,OwningCharacter有可能还未被有效设置。这是一个合法的、暂时的运行时状态,而不是一个代码错误。使用 if 可以安全地跳过这一帧的初始化,等待下一帧或下一次更新时再尝试获取。

check 应该用于验证那些在逻辑上绝对不应该发生的情况。如果这些情况发生了,说明代码本身存在 bug,继续运行下去毫无意义,甚至更危险。

  1. GetVelocity():
    这个方法返回一个 FVector,表示角色的速度。一个 FVector 包含三个分量:

    • X:通常代表前后方向(Forward/Backward)。

    • Y:通常代表左右方向(Right/Left)。

    • Z:代表上下方向(Up/Down)。

    例如,一个角色以每秒 300 个单位的速度向前跑,并且每秒向下坠落 50 个单位(因为刚从悬崖跳下),它的速度向量可能是 (300, 0, -50)

  2. Size2D():

    它的计算公式是:√(X² + Y²)(即二维的勾股定理)。这个函数会忽略掉向量的 Z 分量,只使用 X 和 Y 分量来计算长度。

  3. GetCurrentAcceleration() 返回的是一个标准的 FVector,包含 X, Y, Z 三个分量。它表示的是角色移动组件在当前帧的加速度矢量。

  4. SizeSquared2D():计算向量二维长度的平方(X² + Y²)

创建动画蓝图

创建混合空间

设置最大速度,闲暇和跑步动画

回到动画蓝图创建状态机

特性 混合空间(Blend Space) 状态机(State Machine)
驱动方式 连续变化的参数(如 Speed, Direction) 离散的状态布尔条件(如 bIsJumping, bIsInCombat)
最佳用途 处理量变(平滑过渡,如速度变化、方向变化) 处理质变(状态切换,如落地、起跳、死亡)
核心思想 “混合” “切换”

最后给角色类mesh设置动画蓝图

使用c++完成状态过渡条件

添加一个状态

#pragma once

#include "CoreMinimal.h"
#include "AnimInstances/WarriorCharacterAnimInstance.h"
#include "WarriorHeroAnimInstance.generated.h"

class AWarriorHeroCharacter;
/**
 * 
 */
UCLASS()
class WARRIOR_API UWarriorHeroAnimInstance : public UWarriorCharacterAnimInstance
{
	GENERATED_BODY()
	
public:
	virtual void NativeInitializeAnimation() override;
	virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;

protected:
	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "AnimData|Refrences")
	AWarriorHeroCharacter* OwningHeroCharacter;

	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "AnimData|LocomotionData")
	bool bShouldEnterRelaxState;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "AnimData|LocomotionData")
	float EnterRelaxtStateThreshold = 5.f;

	float IdleElpasedTime;
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "AnimInstances/Hero/WarriorHeroAnimInstance.h"
#include "Characters/WarriorHeroCharacter.h"

void UWarriorHeroAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();

	if (OwningCharacter)
	{
		OwningHeroCharacter = Cast<AWarriorHeroCharacter>(OwningCharacter);
	}
}

void UWarriorHeroAnimInstance::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);

	if (bHasAcceleration) {
		IdleElpasedTime = 0.f;
		bShouldEnterRelaxState = false;
	}
	else
	{
		IdleElpasedTime += DeltaSeconds;
		bShouldEnterRelaxState = (IdleElpasedTime >= EnterRelaxtStateThreshold);
	}
}

就是记录空闲时间,是否达到阈值,达到就状态转移

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐