UEC++ 虚幻5 “类魂”游戏笔记(二)

UI与角色行为制作(一)

定义体力值机制

  • 我们需要在SoulBaseCharacterl类中定义角色的各项机制变量,例如体力值、最大体力值、消耗体力值、增长体力值
//角色机制
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
	float Stamina;//体力值
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
	float MaxStamina;//最大体力值
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
	float MeleeAttackSubStamina;//攻击消耗体力值
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
	float IncreaseStamina;//增长体力值


MaxStamina = 100.f;
Stamina = MaxStamina;
MeleeAttackSubStamina = 10.f;
IncreaseStamina = 1.f;
  • 这些机制值定义好了后,就开始约束写的逻辑,在角色类PlayerCharacter类中CanMeleeAttack()是否能攻击函数,应该加上条件,就是体力值大于等于消耗的体力值时的状态下才能攻击
bool APlayerCharacter::CanMeleeAttack()
{
	if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= MeleeAttackSubStamina)
	{
		return true;
	}
	return false;
}

  • 然后在拳法攻击中,每次攻击就得减少体力
void APlayerCharacter::MeleeAttack()
{
	if (CanMeleeAttack())
	{
		UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
		if (CurAnimInstance)
		{
			//将状态改变为攻击状态
			PlayerBehavior = EPlayerBehavior::EPB_ATTACK;

			//将状态改变为备战状态
			MeleeState = EMeleeState::EMS_PREPARE;
			MeleeBehaviorStateWarToCommon = 10.f;//每次攻击后重新置为10秒计时

			//每次攻击都得减去体力值
			Stamina -= MeleeAttackSubStamina;

			//播放随机动画
			int32 AttackAnimIndex = UKismetMathLibrary::RandomIntegerInRange(0, MeleeAttackAnim.Num() - 1);
			if (AttackAnimIndex != LastMeleeIndex)
			{
				LastMeleeIndex = AttackAnimIndex;//更新上一个动作
				CurAnimInstance->Montage_Play(MeleeAttackAnim[AttackAnimIndex]);
			}
			else
			{
				if (AttackAnimIndex == 0)
				{
					int32 AddIndexNum = UKismetMathLibrary::RandomIntegerInRange(0, MeleeAttackAnim.Num() - 2);
					AttackAnimIndex += AddIndexNum;
					LastMeleeIndex = AttackAnimIndex;
					CurAnimInstance->Montage_Play(MeleeAttackAnim[AttackAnimIndex]);
				}
				else
				{
					AttackAnimIndex--;
					LastMeleeIndex = AttackAnimIndex;
					CurAnimInstance->Montage_Play(MeleeAttackAnim[AttackAnimIndex]);
				}
			}		
		}
	}
	
}
  • 然后在Tick中抒写体力值增加逻辑,当角色体力值小于100时并且角色没有进行攻击与防御,角色体力值才能慢慢恢复
void APlayerCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	//角色非攻击防御状态,体力小于100的时候才能开始恢复状态
	if (Stamina < 100.f && PlayerBehavior != EPlayerBehavior::EPB_ATTACK && PlayerBehavior != EPlayerBehavior::EPB_DEFENCE)
	{
		Stamina += DeltaTime * IncreaseStamina;
		GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red,
			FString::Printf(TEXT("Stamina:%f"), Stamina));
	}
	
	//记录攻击倒计时,10秒之后无攻击动作才能解除备战状态
	if (MeleeState == EMeleeState::EMS_PREPARE && PlayerBehavior != EPlayerBehavior::EPB_ATTACK)
	{
		MeleeBehaviorStateWarToCommon -= DeltaTime;
		/*
		GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red,
			FString::Printf(TEXT("MeleeBehaviorStateWarToCommon:%f"), MeleeBehaviorStateWarToCommon));
		*/
		if (MeleeBehaviorStateWarToCommon <= 0)
		{
			MeleeState = EMeleeState::EMS_COMMON;
			MeleeBehaviorStateWarToCommon = 10.f;
		}
	}
}

体力值UI控件配置

  • 新建一个HUD类,用于加载到GameMode里注册,然后将UI在HUD里进行加载创建
    在这里插入图片描述
  • 然后创建一个主UI的基类,然后其他的UI继承这个基类进行创建
    在这里插入图片描述
    在这里插入图片描述
  • 然后在这个基类上创建子类战斗的UI界面
    在这里插入图片描述
  • UI_FightMain类中编写组件
  • meta = (BindWidget):BindWidget是用于将C++类与UMG(Unreal Motion Graphics)界面蓝图中的UI小部件进行绑定的一种机制
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "DarkSoulUserWidgetRule.h"
#include "UI_FightMain.generated.h"

/**
 * 
 */
UCLASS()
class DARKSOULSGAME_API UUI_FightMain : public UDarkSoulUserWidgetRule
{
	GENERATED_BODY()
	
public:
	//UI
	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
	class UProgressBar* StaminaBar;//体力条
	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
	UProgressBar* PlayerHPBar;//血条
	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
	class UTextBlock* Text_HP;//体力文本
	UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
	UTextBlock* Text_Stamina;//体力文本

};
  • 然后建立这个UI_FightMain的用户组件进行编辑UI界面
    在这里插入图片描述
  • 因为进行了绑定,所以要创建和你C++编写名称一样的组件
    在这里插入图片描述
    在这里插入图片描述

体力值数据接入UI表现

  • SoulBaseCharacterl类中添加两个共有接口,获取自身的体力与最大体力,方便在UI控件蓝图中去调用使用
	UFUNCTION(BlueprintCallable,BlueprintPure)
	float GetCurStamina();//获取体力
	UFUNCTION(BlueprintCallable, BlueprintPure)
	float GetMaxStamina();//获取最大体力

float ASoulBaseCharacter::GetCurStamina()
{
	return Stamina;
}

float ASoulBaseCharacter::GetMaxStamina()
{
	return MaxStamina;
}
  • 然后在GameMode里面注册我们新建的HUD这个类
// Copyright Epic Games, Inc. All Rights Reserved.


#include "DarkSoulsGameGameModeBase.h"
#include "DarkSoulPlayerController.h"
#include "Characters/SoulBaseCharacter.h"
#include "DarkSoulHUD.h"

ADarkSoulsGameGameModeBase::ADarkSoulsGameGameModeBase()
{
	//加载PlayerCharacter的蓝图
	static ConstructorHelpers::FClassFinder<ASoulBaseCharacter>BPPlayerClass(TEXT("/Game/_Game/BP/BP_PlayerCharacter"));
	
	if (BPPlayerClass.Class)
	{
		//设置默认Pawn类为PlayerCharacter蓝图
		DefaultPawnClass = BPPlayerClass.Class;
	}
	//注册控制器
	PlayerControllerClass = ADarkSoulPlayerController::StaticClass();

	//注册HUD
	HUDClass = ADarkSoulHUD::StaticClass();
}
  • DarkSoulHUD类中添加我们的UI界面
  • 新建一个存储主界面UI的模版与指针,重写BeginPlay函数
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "DarkSoulHUD.generated.h"

/**
 * 
 */
UCLASS()
class DARKSOULSGAME_API ADarkSoulHUD : public AHUD
{
	GENERATED_BODY()
	
public:
	ADarkSoulHUD();
	//存储主界面UI的模版
	TSubclassOf<class UUI_FightMain> FightMainUIClass;
	//主界面UI的类指针
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "MainUI")
	class UUI_FightMain* FightMainUI;
protected:
	virtual void BeginPlay() override;
};

  • 添加UI在视口的逻辑
// Fill out your copyright notice in the Description page of Project Settings.


#include "DarkSoulHUD.h"
#include "UI/UI_FightMain.h"
ADarkSoulHUD::ADarkSoulHUD()
{
	static ConstructorHelpers::FClassFinder<UUI_FightMain> UI_FightMain(TEXT("/Game/_Game/BP/UI/BP_UI_FightMian"));
	FightMainUIClass = UI_FightMain.Class;
}


void ADarkSoulHUD::BeginPlay()
{
	//创建UI界面
	FightMainUI = CreateWidget<UUI_FightMain>(GetWorld(), FightMainUIClass);
	if (FightMainUI)
	{
		//添加到视口
		FightMainUI->AddToViewport(0);
	}
}

  • 然后去控件蓝图UI中ProgressBar组件绑定函数,编写显示逻辑
    在这里插入图片描述
    在这里插入图片描述

摄像机震动反馈

  • 我们在SoulBaseCharacter类中建立一个带BlueprintImplementableEvent宏的摄像机震动函数,我们在蓝图中去实现这个功能
	//摄像机震动反馈
	UFUNCTION(BlueprintImplementableEvent)
	void CameraShakeFeedBack();
  • 然后在PlayerCharacter类中攻击函数中调用这个方法
void APlayerCharacter::MeleeAttack()
{
	if (CanMeleeAttack())
	{
		UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
		if (CurAnimInstance)
		{
			//将状态改变为攻击状态
			PlayerBehavior = EPlayerBehavior::EPB_ATTACK;

			//将状态改变为备战状态
			MeleeState = EMeleeState::EMS_PREPARE;
			MeleeBehaviorStateWarToCommon = 10.f;//每次攻击后重新置为10秒计时

			//每次攻击都得减去体力值
			Stamina -= MeleeAttackSubStamina;

			//摄像机震动反馈
			CameraShakeFeedBack();

			//播放随机动画
			int32 AttackAnimIndex = UKismetMathLibrary::RandomIntegerInRange(0, MeleeAttackAnim.Num() - 1);
			if (AttackAnimIndex != LastMeleeIndex)
			{
				LastMeleeIndex = AttackAnimIndex;//更新上一个动作
				CurAnimInstance->Montage_Play(MeleeAttackAnim[AttackAnimIndex]);
			}
			else
			{
				if (AttackAnimIndex == 0)
				{
					int32 AddIndexNum = UKismetMathLibrary::RandomIntegerInRange(0, MeleeAttackAnim.Num() - 2);
					AttackAnimIndex += AddIndexNum;
					LastMeleeIndex = AttackAnimIndex;
					CurAnimInstance->Montage_Play(MeleeAttackAnim[AttackAnimIndex]);
				}
				else
				{
					AttackAnimIndex--;
					LastMeleeIndex = AttackAnimIndex;
					CurAnimInstance->Montage_Play(MeleeAttackAnim[AttackAnimIndex]);
				}
			}		
		}
	}
	
}
  • 新建一个LegacyCameraShake蓝图,用来设置自己需要的震动参数
    在这里插入图片描述
    在这里插入图片描述
  • 在角色蓝图中编写逻辑,使用Controller的Client Start Camera Shake节点播放震动效果
    在这里插入图片描述

体力值不足提示

  • 需求:当我们体力值不够时,将提示一秒钟的体力值不足文本
  • 逻辑:编写UI控件蓝图提示文本,然后在基类角色中获取Controller中的HUD中的我们FightMain主UI,在是否可以攻击函数里面设置隐藏与显示方法函数,隐藏一秒函数方法可以使用定时器
  • 编写UI控件蓝图提示文本与UI_FightMain中的提示文本的创建
UPROPERTY(BlueprintReadWrite, meta = (BindWidget))
UTextBlock* Text_Stamina_Exhausted;//体力提示文本

在这里插入图片描述

  • UI_FightMain中编写时间句柄和显示与隐藏函数逻辑
	FTimerHandle ShowStaminaTimerHandle;//时间句柄
	//函数方法
	void ShowStaminaText();//显示文本提示
	void HiddenStaminaText();//隐藏文本提示
  • 逻辑:使用UTextBlock组件库函数方法,要添加头文件#include "UMG/Public/Components/TextBlock.h"
// Fill out your copyright notice in the Description page of Project Settings.


#include "UI_FightMain.h"
#include "UMG/Public/Components/TextBlock.h"
void UUI_FightMain::ShowStaminaText()
{
	Text_Stamina_Exhausted->SetVisibility(ESlateVisibility::Visible);//显示控件文本
	//显示一秒钟后隐藏
	GetWorld()->GetTimerManager().SetTimer(ShowStaminaTimerHandle, this, &UUI_FightMain::HiddenStaminaText, 1.f, false, 1.f);
}

void UUI_FightMain::HiddenStaminaText()
{
	Text_Stamina_Exhausted->SetVisibility(ESlateVisibility::Hidden);//隐藏文本
	GetWorld()->GetTimerManager().ClearTimer(ShowStaminaTimerHandle);//删除时间句柄释放资源
}
  • DarkSoulHUD类中建一个可以获取到主界面UI的类指针函数方法,角色基类SoulBaseCharacter类中去获取获取Controller中的HUD中的我们FightMain主UI
  • DarkSoulHUD类中建一个可以获取到主界面UI的类指针函数方法
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/HUD.h"
#include "DarkSoulHUD.generated.h"

/**
 * 
 */
UCLASS()
class DARKSOULSGAME_API ADarkSoulHUD : public AHUD
{
	GENERATED_BODY()
	
public:
	ADarkSoulHUD();
	//存储主界面UI的模版
	TSubclassOf<class UUI_FightMain> FightMainUIClass;
	//主界面UI的类指针
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "MainUI")
	class UUI_FightMain* FightMainUI;
protected:
	virtual void BeginPlay() override;

public:
	//获取主界面UI的类指针函数方法
	inline UUI_FightMain* GetFightMainUI() { return FightMainUI; };
};
  • 角色基类SoulBaseCharacter类中去获取获取Controller中的HUD中的我们FightMain主UI,显示提示文本
	//获取UI并显示提示文本
	void ShowStaminaNotEnoughText();
void ASoulBaseCharacter::ShowStaminaNotEnoughText()
{
	//获取到Controller
	ADarkSoulPlayerController* PC = Cast<ADarkSoulPlayerController>(Controller);
	if (Controller)
	{
		//获取到HUD
		ADarkSoulHUD* HUD = Cast<ADarkSoulHUD>(PC->GetHUD());
		if (HUD)
		{
			//显示提示文本
			HUD->GetFightMainUI()->ShowStaminaText();
		}
	}
}
  • 最后在PlayerCharacter类中是否攻击函数中去调用ShowStaminaNotEnoughText()方法
bool APlayerCharacter::CanMeleeAttack()
{
	if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= MeleeAttackSubStamina)
	{
		return true;
	}
	else if (Stamina < MeleeAttackSubStamina)
	{
		ShowStaminaNotEnoughText();
	}
	return false;
}
  • 最后将文本开始设置为隐藏状态
    在这里插入图片描述

人物攻击转向手感调优

  • 思路:获取方向键的最后一个按键,获取它的轴值进行转向
  • SoulBaseCharacter类中新建一个FRotator变量记录攻击时转向的目标值
	//攻击时转向的目标值
	FRotator DesiredRotation;

DesiredRotation = FRotator(0., 0., 0.);
  • 然后新建一个计算按键的方向的函数方法
	FRotator CalculateRotation();
  • Tick中实时计算攻击转向的目标值
// Called every frame
void ASoulBaseCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	//实时计算攻击转向的目标值
	DesiredRotation = CalculateRotation();
}
  • 计算按键方向的函数方法逻辑
FRotator ASoulBaseCharacter::CalculateRotation()
{
	//获取最后一次输入的向量
	FVector LastVector = GetCharacterMovement()->GetLastInputVector();
	//判断最后一次输入向量是否为0,如果是就返回这个目标值,如果不为0就返回这个Vector转换为Rotator的目标值
	if (LastVector != FVector(0,0,0))
	{
		return UKismetMathLibrary::MakeRotFromX(LastVector);
	}
	else
	{
		return DesiredRotation;
	}
}
  • 然后新建一个线性插入旋转的函数方法,这个方法要在蓝图里面进行调用通知动画的
	//线性插入旋转值
	UFUNCTION(BlueprintCallable)
	void RInterpRotation();
  • 方法逻辑

void ASoulBaseCharacter::RInterpRotation()
{
	//实时进行转向插值
	FRotator RInterpRotation = UKismetMathLibrary::RInterpTo(GetActorRotation(), DesiredRotation, 
		GetWorld()->GetDeltaSeconds(), 5.f);
	SetActorRotation(FRotator(0., RInterpRotation.Yaw, 0.));
}
  • 在虚幻中建立一个Anim Notify State蓝图
    在这里插入图片描述
  • 实时计算转向目标值,进行转向
    在这里插入图片描述
  • 然后在蒙太奇中添加一个轨道,添加这个动画通知状态,这样就可以攻击时可以转向攻击
    在这里插入图片描述
  • 将Pawn Yaw旋转取掉,角色移动旋转勾上,也可以在代码中进行硬编码
    在这里插入图片描述
    在这里插入图片描述
// Sets default values
ASoulBaseCharacter::ASoulBaseCharacter()
{
 	// 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;
	
	SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
	SpringArm->SetupAttachment(GetRootComponent());
	SpringArm->TargetArmLength = 300.f;
	SpringArm->bUsePawnControlRotation = true;

	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
	FollowCamera->SetupAttachment(SpringArm);

	//默认是不奔跑的
	bIsRun = false;
	WeaponType = EWeaponType::EWT_MELEE;//拳击
	MeleeState = EMeleeState::EMS_COMMON;//普通状态
	PlayerBehavior = EPlayerBehavior::EPB_IDLE;//空闲状态

	MaxStamina = 100.f;
	Stamina = MaxStamina;
	MeleeAttackSubStamina = 10.f;
	IncreaseStamina = 1.f;

	//攻击转向默认值为0
	DesiredRotation = FRotator(0., 0., 0.);

	bUseControllerRotationPitch = false;
	bUseControllerRotationRoll = false;
	bUseControllerRotationYaw = false;

	GetCharacterMovement()->bOrientRotationToMovement = true;

}

设置相机弹簧臂滞后感

  • 自己设置参数即可
    在这里插入图片描述

人物近战翻滚逻辑

逻辑(一)

  • SoulBaseCharacter类中绑定翻滚的按键操作,定义翻滚虚函数方法方便继承,新建每次翻滚要减少的体力值变量赋初值
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	UInputAction* RollingAction;
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
	float MeleeRollingSubStamina;//翻滚消耗体力值


MeleeRollingSubStamina = 15.f;
	//攻击
	virtual void Attack();
	//翻滚
	virtual void Rolling();
  • PlayerCharacte类中去重写翻滚函数方法
	//重写Attack
	virtual void Attack() override;
	//重写Rolling
	virtual void Rolling() override;
  • 在新建两个函数方法来决定是什么翻滚,因为有持剑翻滚与普通翻滚,新建一个是否可以翻滚的状态函数方法处理
	void MeleeRolling();
	void SwordRolling();
	bool CanMeleeRolling();
  • PlayerCharacter中绑定翻滚函数操作
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent);
	if (EnhancedInputComponent)
	{
		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ASoulBaseCharacter::Move);
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ASoulBaseCharacter::Look);
		EnhancedInputComponent->BindAction(RunAction, ETriggerEvent::Triggered, this, &ASoulBaseCharacter::Run);
		EnhancedInputComponent->BindAction(RunAction, ETriggerEvent::Completed, this, &ASoulBaseCharacter::StopRun);
		EnhancedInputComponent->BindAction(AttackAction, ETriggerEvent::Started, this, &ASoulBaseCharacter::Attack);
		EnhancedInputComponent->BindAction(RollingAction, ETriggerEvent::Started, this, &ASoulBaseCharacter::Rolling);
	}
}
  • 实现是否可以翻滚的状态函数方法处理逻辑
bool APlayerCharacter::CanMeleeRolling()
{
	if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= MeleeRollingSubStamina)
	{
		return true;
	}
	else if (Stamina < MeleeRollingSubStamina)
	{
		ShowStaminaNotEnoughText();
	}
	return false;
}
  • 翻滚逻辑:判断是什么类型的翻滚,就执行那个类型的翻滚函数
void APlayerCharacter::Rolling()
{
	switch (WeaponType)
	{
	case EWeaponType::EWT_MELEE:
		MeleeRolling();
		break;
	case EWeaponType::EWT_SWORD:
		SwordRolling();
		break;
	}
}
  • 定义存储翻滚的蒙太奇数组
	//近战翻滚蒙太奇
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
	TArray<UAnimMontage*> MeleeRollingAnim;
  • 翻滚的逻辑需求:在SoulBaseCharacter类中新建两个变量用来获取用户输入的前后左右,因为翻滚是有方法的,并初始化
	//翻滚的朝向值(前后)
	int32 RollingForwardValue;
	//翻滚的朝向值(左右)
	int32 RollingRightValue;
	
//翻滚方向值默认值为0
RollingForwardValue = 0.0;
RollingRightValue = 0.0;

逻辑(二)

  • 我们在SoulBaseCharacter类的移动函数中获取到方向的输入按键,新建一个变量来控制到时候翻滚蒙太奇播放的速率
	//翻滚蒙太奇播放速率
	float RollingAnimPlayRate;
	
//翻滚动画播放速率	
RollingAnimPlayRate = 1.0;
void ASoulBaseCharacter::Move(const FInputActionValue& Value)
{
	FVector2D MoveVector = Value.Get<FVector2D>();
	//回获取翻滚的方向值
	RollingForwardValue = MoveVector.Y;
	RollingRightValue = MoveVector.X;

	if (Controller)
	{
		FRotator Rotation = Controller->GetControlRotation();
		FRotator YawRotation = FRotator(0.f, Rotation.Yaw, 0.f);

		FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
		FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

		AddMovementInput(ForwardDirection, MoveVector.Y);
		AddMovementInput(RightDirection, MoveVector.X);

	}
}
  • 然后去PlayerCharacter类中MeleeRolling方法中实现翻滚逻辑,
void APlayerCharacter::MeleeRolling()
{
	if (CanMeleeRolling())
	{
		UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
		if (CurAnimInstance)
		{
			//每次翻滚减去体力
			Stamina -= MeleeRollingSubStamina;
			PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
			if (RollingForwardValue == 1)
			{
				CurAnimInstance->Montage_Play(MeleeRollingAnim[0], RollingAnimPlayRate);
			}
			else if (RollingForwardValue == -1)
			{
				CurAnimInstance->Montage_Play(MeleeRollingAnim[1], RollingAnimPlayRate);
			}
			else if (RollingRightValue == 1)
			{
				CurAnimInstance->Montage_Play(MeleeRollingAnim[2], RollingAnimPlayRate);
			}
			else if (RollingRightValue == -1)
			{
				CurAnimInstance->Montage_Play(MeleeRollingAnim[3], RollingAnimPlayRate);
			}
			else
			{
				CurAnimInstance->Montage_Play(MeleeRollingAnim[0], RollingAnimPlayRate);
			}
		}
	}
}

在编辑器里面配置人物近战翻滚动画

  • 绑定输入操作到人物蓝图
    在这里插入图片描述
    在这里插入图片描述

  • 给翻滚动画全部创建蒙太奇,插槽换成之前的攻击插槽,然后添加动画通知,在动画蓝图中要重置状态的
    在这里插入图片描述

  • 然后将这些蒙太奇添加到人物蓝图的数组中
    在这里插入图片描述

  • 最后在动画蓝图事件中通知重置动画状态
    在这里插入图片描述

  • 把角色使用控制器所需旋转关掉,操作会比较好一点
    在这里插入图片描述

人物翻跟头行为配置

  • 首先在SoulBaseCharacte类中新增按键操作输入
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	UInputAction* SlideAction;
  • 定义一个方法,近战是翻跟头,持剑状态是翻滚滑行
	//攻击
	virtual void Attack();
	//翻滚
	virtual void Rolling();
	//滑行(近战:翻跟头/持剑:滑行翻滚)
	virtual void Slide();
  • 然后在PlayerCharacter类中重写滑行方法,然后添加函数分为持剑滑行与近战滑行,与判断是否近战滑行的函数方法
	//重写Slide
	virtual void Slide() override;

void MeleeSlide();
void SwordSlide();
bool CanMeleeSlide();
  • Slide滑行逻辑
void APlayerCharacter::Slide()
{
	switch (WeaponType)
	{
	case EWeaponType::EWT_MELEE:
		MeleeSlide();
		break;
	case EWeaponType::EWT_SWORD:
		SwordSlide();
		break;
	}
}
  • CanMeleeSlide方法逻辑
bool APlayerCharacter::CanMeleeSlide()
{
	if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= MeleeRollingSubStamina)
	{
		return true;
	}
	else if (Stamina < MeleeRollingSubStamina)
	{
		ShowStaminaNotEnoughText();
	}
	return false;
}
  • 新建一个存储蒙太奇的数组存放滑行蒙太奇
	//近战滑行蒙太奇
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
	TArray<UAnimMontage*> MeleeSlideAnim;
  • 近战滑行的逻辑与近战翻滚基本一样
void APlayerCharacter::MeleeSlide()
{
	if (CanMeleeSlide())
	{
		UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
		if (CurAnimInstance)
		{
			//每次翻滚减去体力
			Stamina -= MeleeRollingSubStamina;
			PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
			if (RollingForwardValue == 1)
			{
				CurAnimInstance->Montage_Play(MeleeSlideAnim[0], RollingAnimPlayRate);
			}
			else if (RollingForwardValue == -1)
			{
				CurAnimInstance->Montage_Play(MeleeSlideAnim[1], RollingAnimPlayRate);
			}
			else if (RollingRightValue == 1)
			{
				CurAnimInstance->Montage_Play(MeleeSlideAnim[2], RollingAnimPlayRate);
			}
			else if (RollingRightValue == -1)
			{
				CurAnimInstance->Montage_Play(MeleeSlideAnim[3], RollingAnimPlayRate);
			}
			else
			{
				CurAnimInstance->Montage_Play(MeleeSlideAnim[0], RollingAnimPlayRate);
			}
		}
	}
}
  • PlayerCharacter中绑定滑行函数操作,虚幻中新建输入操作绑定到映射,配置到角色蓝图中
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent);
	if (EnhancedInputComponent)
	{
		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ASoulBaseCharacter::Move);
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ASoulBaseCharacter::Look);
		EnhancedInputComponent->BindAction(RunAction, ETriggerEvent::Triggered, this, &ASoulBaseCharacter::Run);
		EnhancedInputComponent->BindAction(RunAction, ETriggerEvent::Completed, this, &ASoulBaseCharacter::StopRun);
		EnhancedInputComponent->BindAction(AttackAction, ETriggerEvent::Started, this, &ASoulBaseCharacter::Attack);
		EnhancedInputComponent->BindAction(RollingAction, ETriggerEvent::Started, this, &ASoulBaseCharacter::Rolling);
		EnhancedInputComponent->BindAction(SlideAction, ETriggerEvent::Started, this, &ASoulBaseCharacter::Slide);
	
	}
}

在这里插入图片描述
在这里插入图片描述

  • 然后配置滑行的蒙太奇,添加重置通知切换插槽
    在这里插入图片描述
  • 在角色蓝图上添加上蒙太奇动画,然后去动画蓝图中接收通知重置动画状态
    在这里插入图片描述
    在这里插入图片描述
  • 聚焦输入模式到游戏,这样就不用每次要点击游戏才能进行输入
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值