效果展示

导入资源
先创建一个第三人称的新手工程项目。 打开AdvancedLocomotionSystemV项目,将一些动画资源导入到我们项目的Content的内容下。  还有摄影机骨骼和模型也要。  打开项目的文件夹,将输入配置复制替换到我们的文件中。 
创建角色
由于第三人称的模板已经给我们了一些代码,所以直接用他的类就好了。 打开第三人称角色模板C++文件,将一些不需要的东西删除,其中最主要的就是将摄像机给删除了,添加了一些保存人物状态的变量。
为什么要将摄像机给删除了? 因为当前这个项目的摄像头是跟着胶囊体移动的,如果我们要进行第一人称第三人称视角替换的话就需要多个摄像头,并且如果我们想要摄像机跟着角色状态变化的话就有点麻烦。所以就直接另外弄一个摄像机进行统一管理。
以下是我删好了的代码:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "ALSVLearnDemoCharacter.generated.h"
UCLASS(config=Game)
class AALSVLearnDemoCharacter : public ACharacter
{
GENERATED_BODY()
public:
AALSVLearnDemoCharacter();
protected:
void MoveForward(float Value);
void MoveRight(float Value);
protected:
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual void Tick(float DeltaSeconds) override;
public:
UPROPERTY(BlueprintReadOnly, Category="Essential Information")
bool bIsMoving;
UPROPERTY(BlueprintReadOnly, Category="Essential Information")
bool bHasMovementInput;
UPROPERTY(BlueprintReadOnly, Category="Essential Information")
float Speed;
UPROPERTY(BlueprintReadOnly, Category="Essential Information")
float MovementInputAmount;
UFUNCTION(BlueprintCallable, Category="Essential Information")
void GetEssentialValues(FVector& Velocity, bool& IsMoving, bool& HasMovementInput, float& fSpeed);
UFUNCTION(BlueprintCallable, Category="Essential Information")
void SetEssentialValues();
};
#include "ALSVLearnDemoCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
AALSVLearnDemoCharacter::AALSVLearnDemoCharacter()
{
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f);
GetCharacterMovement()->JumpZVelocity = 600.f;
GetCharacterMovement()->AirControl = 0.2f;
}
void AALSVLearnDemoCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
check(PlayerInputComponent);
PlayerInputComponent->BindAction("JumpAction", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("JumpAction", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAxis("MoveForward/Backwards", this, &AALSVLearnDemoCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight/Left", this, &AALSVLearnDemoCharacter::MoveRight);
PlayerInputComponent->BindAxis("LookLeft/Right", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp/Down", this, &APawn::AddControllerPitchInput);
}
void AALSVLearnDemoCharacter::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
SetEssentialValues();
}
void AALSVLearnDemoCharacter::GetEssentialValues(FVector& Velocity, bool& IsMoving, bool& HasMovementInput,
float& fSpeed)
{
Velocity = GetVelocity();
IsMoving = bIsMoving;
HasMovementInput = bHasMovementInput;
fSpeed = Speed;
}
void AALSVLearnDemoCharacter::SetEssentialValues()
{
FVector LocVelocity = GetVelocity();
Speed = FVector(LocVelocity.X, LocVelocity.Y, 0.f).Size();
bIsMoving = Speed > 1.f;
MovementInputAmount = GetCharacterMovement()->GetCurrentAcceleration().Size() / GetCharacterMovement()->
GetMaxAcceleration();
bHasMovementInput = MovementInputAmount > 0.f;
}
void AALSVLearnDemoCharacter::MoveForward(float Value)
{
if ((Controller != nullptr) && (Value != 0.0f))
{
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
void AALSVLearnDemoCharacter::MoveRight(float Value)
{
if ((Controller != nullptr) && (Value != 0.0f))
{
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
}
创建它的蓝图子类,命名为:ALs_AnimMan_CharacterBP 。 将角色导入到其中,调整到角色朝前的位置。
创建角色动画C++实例类
创建一个AnimInstance 类,命名为ALSAnimInstance 。
.h
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "ALSAnimInstance.generated.h"
class AALSVLearnDemoCharacter;
USTRUCT(BlueprintType)
struct FVelocityBlend
{
GENERATED_BODY()
public:
FVelocityBlend(float f = 0.f, float b = 0.f, float l = 0.f, float r = 0.f) : F(f), B(b), L(l), R(r)
{
}
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float F;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float B;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float L;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float R;
};
UCLASS()
class ALSVLEARNDEMO_API UALSAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
virtual void NativeBeginPlay() override;
UFUNCTION(BlueprintCallable)
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
UPROPERTY(BlueprintReadOnly)
AALSVLearnDemoCharacter* Character;
UFUNCTION(BlueprintCallable)
void UpdateCharacterInfo();
UFUNCTION(BlueprintCallable)
void UpdateMovementValues();
UPROPERTY(BlueprintReadOnly)
FVector Velocity;
UFUNCTION(BlueprintCallable)
FVelocityBlend CalculateVelocityBlend();
UFUNCTION(BlueprintCallable)
FVelocityBlend InterpVelocityBlend(FVelocityBlend Current, FVelocityBlend Target, float InterpSpeed,
float DeltaTime) const;
UFUNCTION(BlueprintCallable)
float CalculateStrideBlend() const;
UPROPERTY(BlueprintReadOnly, Category="Character Information")
bool bIsMoving;
UPROPERTY(BlueprintReadOnly, Category="Character Information")
bool bHasMovementInput;
UPROPERTY(BlueprintReadOnly, Category="Character Information")
float Speed;
UPROPERTY(BlueprintReadWrite, Category = "Anim Graph|Grounded")
FVelocityBlend VelocityBlend;
UPROPERTY(BlueprintReadWrite, Category = "Anim Graph|Grounded")
bool bShouldMove;
UPROPERTY(BlueprintReadWrite, Category = "Anim Graph|Grounded")
float StrideBlend;
UFUNCTION(BlueprintCallable, Category="Grounded")
bool ShouldMoveCheck() const;
UPROPERTY()
float DeltaTimeX;
UPROPERTY(EditAnywhere, Category="Config")
float VelocityBlendInterpSpeed;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Blend Curves")
UCurveFloat* StrideBlendNWalk;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Blend Curves")
UCurveFloat* StrideBlendNRun;
protected:
UFUNCTION()
float GetAnimCurveClamped(FName Name, float Bias, float ClampMin, float ClampMax) const;
};
.cpp
#include "ALSAnimInstance.h"
#include "ALSVLearnDemo/ALSVLearnDemoCharacter.h"
#include "Kismet/KismetMathLibrary.h"
void UALSAnimInstance::NativeBeginPlay()
{
Super::NativeBeginPlay();
Character = Cast<AALSVLearnDemoCharacter>(TryGetPawnOwner());
VelocityBlendInterpSpeed = 12.f;
}
void UALSAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
if (!Character) return;
DeltaTimeX = DeltaSeconds;
UpdateCharacterInfo();
bShouldMove = ShouldMoveCheck();
UpdateMovementValues();
}
void UALSAnimInstance::UpdateMovementValues()
{
VelocityBlend = InterpVelocityBlend(VelocityBlend, CalculateVelocityBlend(), VelocityBlendInterpSpeed, DeltaTimeX);
StrideBlend = CalculateStrideBlend();
}
void UALSAnimInstance::UpdateCharacterInfo()
{
Character->GetEssentialValues(Velocity, bIsMoving, bHasMovementInput, Speed);
}
FVelocityBlend UALSAnimInstance::CalculateVelocityBlend()
{
FVector LocRelativeVelocityDir = UKismetMathLibrary::LessLess_VectorRotator(
UKismetMathLibrary::Normal(Velocity, .1f), Character->GetActorRotation());
float Sum = FMath::Abs(LocRelativeVelocityDir.X) + FMath::Abs(LocRelativeVelocityDir.Y) + FMath::Abs(
LocRelativeVelocityDir.Z);
FVector RelativeDirection = LocRelativeVelocityDir / Sum;
return FVelocityBlend(
UKismetMathLibrary::FClamp(RelativeDirection.X, 0.f, 1.f),
FMath::Abs(UKismetMathLibrary::FClamp(RelativeDirection.X, -1.f, 0.f)),
FMath::Abs(UKismetMathLibrary::FClamp(RelativeDirection.Y, -1.f, 0.f)),
UKismetMathLibrary::FClamp(RelativeDirection.Y, 0.f, 1.f));
}
FVelocityBlend UALSAnimInstance::InterpVelocityBlend(FVelocityBlend Current, FVelocityBlend Target, float InterpSpeed,
float DeltaTime) const
{
return FVelocityBlend(
UKismetMathLibrary::FInterpTo(Current.F, Target.F, DeltaTime, InterpSpeed),
UKismetMathLibrary::FInterpTo(Current.B, Target.B, DeltaTime, InterpSpeed),
UKismetMathLibrary::FInterpTo(Current.L, Target.L, DeltaTime, InterpSpeed),
UKismetMathLibrary::FInterpTo(Current.R, Target.R, DeltaTime, InterpSpeed));
}
float UALSAnimInstance::CalculateStrideBlend() const
{
float WalkValue = StrideBlendNWalk->GetFloatValue(Speed);
float RunValue = StrideBlendNRun->GetFloatValue(Speed);
return UKismetMathLibrary::Lerp(WalkValue, RunValue,
GetAnimCurveClamped(FName(TEXT("Weight_Gait")), -1.f, 0.f, 1.f));
}
bool UALSAnimInstance::ShouldMoveCheck() const
{
return bIsMoving && bHasMovementInput || Speed;
}
float UALSAnimInstance::GetAnimCurveClamped(FName Name, float Bias, float ClampMin, float ClampMax) const
{
return UKismetMathLibrary::FClamp(GetCurveValue(Name) + Bias, ClampMin, ClampMax);
}
走的时候运动曲线为1:  跑步的时候运动曲线为2:  冲刺的时候运动曲线为3: 
创建动画蓝图
创建继承动画骨骼,C++动画实例的动画蓝图。  点击创建动画层  命名为:(N)CycleBlending
在细节面板中添加输入姿势: 
  将输入进来的动画进行混合处理,使得他们的衔接更加流畅。 

输出循环动画  将循环动画放入循环混合动画中, 
控制运动的细节  
控制运动的状态  主输出动画:  在角色蓝图中把该动画蓝图加入其中。 
创建摄像机管理器
逻辑很简单,就是玩家控制器调用摄像机管理器的OnPossess并传递Pawn参数,摄像机管理器将Pawn类指针保存在自己的类中,在它子类蓝图中根据Pawn类的位置设置摄像机的位置。
创建继承PlayerCameraManager 类的ALS_CameraManager 。 .h
#pragma once
#include "CoreMinimal.h"
#include "Camera/PlayerCameraManager.h"
#include "ALS_CameraManager.generated.h"
UCLASS()
class ALSVLEARNDEMO_API AALS_CameraManager : public APlayerCameraManager
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite)
APawn* ControllerPawn;
UFUNCTION(BlueprintCallable)
void OnPossess(APawn* NewPawn);
};
.cpp
#include "ALS_CameraManager.h"
void AALS_CameraManager::OnPossess(APawn* NewPawn)
{
ControllerPawn = NewPawn;
}
继承该类创建一个蓝图类:  
创建一个玩家控制器:ALS_Player_Controller
.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "ALS_Player_Controller.generated.h"
UCLASS()
class ALSVLEARNDEMO_API AALS_Player_Controller : public APlayerController
{
GENERATED_BODY()
public:
virtual void OnPossess(APawn* InPawn) override;
};
.cpp
#include "ALS_Player_Controller.h"
#include "ALSVLearnDemo/CameraSystem/ALS_CameraManager.h"
void AALS_Player_Controller::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn);
AALS_CameraManager* CameraManager = Cast<AALS_CameraManager>(PlayerCameraManager);
if (CameraManager)
{
CameraManager->OnPossess(InPawn);
}
}
创建一个游戏模式蓝图
继承项目自带的游戏模式,打开世界设置:  运行就OK啦!
|