UI与角色行为制作(二)
将刀剑绑定到角色身上
- 在角色骨骼添加两个插槽用来放剑与剑鞘
- 在
PlayerCharacte
类中创建剑鞘的静态网格与剑的骨骼组件
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
class UStaticMeshComponent* ScabbardMesh;
UPROPERTY(EditDefaultSOnly, BlueprintReadOnly, Category = "Weapon")
class USkeletalMeshComponent* Sword;
- 将组件添加到插槽
APlayerCharacter::APlayerCharacter()
{
ScabbardMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ScabbardMesh"));
ScabbardMesh->SetupAttachment(GetMesh(), "Scabbard");
Sword = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Sword"));
Sword->SetupAttachment(GetMesh(), "Sword");
LastMeleeIndex = 0;
MeleeBehaviorStateWarToCommon = 10.;//默认十秒
}
-
剑鞘骨骼网格体导出静态网格体
-
然后在角色蓝图中进行添加剑与剑鞘模型
武器切换逻辑
- 调节好右手持剑的位置
- 在
SoulBaseCharacte
类中添加输入操作换武器,添加虚函数武器切换方法
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* ChangeWeaponAction;
//攻击
virtual void Attack();
//翻滚
virtual void Rolling();
//滑行(近战:翻跟头/持剑:滑行翻滚)
virtual void Slide();
//切换武器
virtual void ChangeWeapon();
- 在子类
PlayerCharacte
类中重写切换武器方法
//重写Attack
virtual void Attack() override;
//重写Rolling
virtual void Rolling() override;
//重写Slide
virtual void Slide() override;
//重写ChangeWeapon
virtual void ChangeWeapon() override;
- 绑定输入操作映射
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, &APlayerCharacter::Attack);
EnhancedInputComponent->BindAction(RollingAction, ETriggerEvent::Started, this, &APlayerCharacter::Rolling);
EnhancedInputComponent->BindAction(SlideAction, ETriggerEvent::Started, this, &APlayerCharacter::Slide);
EnhancedInputComponent->BindAction(ChangeWeaponAction, ETriggerEvent::Started, this, &APlayerCharacter::ChangeWeapon);
}
}
- 添加逻辑bool函数,当前是否能切换武器
bool CanChangeWeapon();
- 是否能切换武器逻辑
bool APlayerCharacter::CanChangeWeapon()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE)
{
return true;
}
return false;
}
- 然后我们需要标记装备武器的状态,在
SoulBaseCharacter
类中添加一个bool变量来表示是否当前状态有装备武器, 默认为false
//是否装备武器
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attack")
bool bEquipWeapon;
bEquipWeapon = false;
- 重写切换武器函数逻辑
void APlayerCharacter::ChangeWeapon()
{
if (CanChangeWeapon())
{
if (WeaponType == EWeaponType::EWT_MELEE)
{
//改为持剑状态
WeaponType = EWeaponType::EWT_SWORD;
//将武器添加到右手
Sword->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, "WEAPON_R");
bEquipWeapon = true;
}
else if (WeaponType == EWeaponType::EWT_SWORD)
{
//改为普通状态
WeaponType = EWeaponType::EWT_MELEE;
//将武器放回剑鞘
Sword->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, "Sword");
bEquipWeapon = false;
}
}
}
- 虚幻中添加输入操作并且绑定到映射,添加到角色蓝图上
配置人物持剑动画混合空间
- 配置持剑动画资产,创建动画混合空间
- 然后在动画蓝图开始配置逻辑
- 获取到是否准备武器状态bool变量,到时候切换状态的条件值
- 新建持剑的动画状态机
- 切换逻辑就是那个bool变量值
- 添加动画混合空间
- 战备状态的动画也可以像持剑动画转换,转换条件是是否持剑
- 微调持剑状态穿模,也可以设置一下平滑速度
人物持剑攻击逻辑
- 人物持剑攻击逻辑与普通拳法攻击逻辑是差不多的
- 在
PlayerCharacte
类中定义判断是否能持剑攻击的函数方法
//是否可以持剑攻击
bool CanSwodAttack();
- 在
SoulBaseCharacte
类中新建持剑攻击衰减值变量并初始化
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
float SwordAttackSubStamina;//持剑攻击消耗体力值
SwordAttackSubStamina= 12.f;
PlayerCharacter
类中CanSwordAttack方法逻辑
bool APlayerCharacter::CanSwodAttack()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= SwordAttackSubStamina)
{
return true;
}
else if (Stamina < SwordAttackSubStamina)
{
ShowStaminaNotEnoughText();
}
return false;
}
- 添加持剑攻击蒙太奇数组,用来存储持剑攻击的蒙太奇
//近战攻击蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> MeleeAttackAnim;
//近战翻滚蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> MeleeRollingAnim;
//近战滑行蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> MeleeSlideAnim;
//持剑攻击蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> SwordAttackAnim;
- SwordAttack方法逻辑
void APlayerCharacter::SwordAttack()
{
if (CanSwodAttack())
{
UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
if (CurAnimInstance)
{
//将状态改变为攻击状态
PlayerBehavior = EPlayerBehavior::EPB_ATTACK;
Stamina -= SwordAttackSubStamina;
//播放随机动画
int32 AttackAnimIndex = UKismetMathLibrary::RandomIntegerInRange(0, SwordAttackAnim.Num() - 1);
if (AttackAnimIndex != LastMeleeIndex)
{
LastMeleeIndex = AttackAnimIndex;
CurAnimInstance->Montage_Play(SwordAttackAnim[AttackAnimIndex]);
}
else
{
if (AttackAnimIndex == 0)
{
int32 AddIndexNum = UKismetMathLibrary::RandomIntegerInRange(0, SwordAttackAnim.Num() - 2);
AttackAnimIndex += AddIndexNum;
LastMeleeIndex = AttackAnimIndex;
CurAnimInstance->Montage_Play(SwordAttackAnim[AttackAnimIndex]);
}
else
{
AttackAnimIndex--;
LastMeleeIndex = AttackAnimIndex;
CurAnimInstance->Montage_Play(SwordAttackAnim[AttackAnimIndex]);
}
}
}
}
}
优化人物持剑逻辑
- 人物持剑与拳法随机攻击是一样的,可以在
SoulBaseCharacte
类中建立一个函数将公共逻辑包含进去,这样只调用函数即可,优化代码 - 新建一个随机攻击函数方法,然后将子类中的
LastAttackAnimIndex
拿到基类SoulBaseCharacte
中来,并且新增一个记录上一个持剑攻击动作的标志变量
//记录上一个拳击攻击动作的标志变量
int32 LastMeleeIndex;
//记录上一个持剑攻击动作的标志变量
int32 LastSwordIndex;
//记录上一个攻击动作的标志变量
LastMeleeIndex = 0;
LastSwordIndex = 0;
//-------------------------------------------------
//随机攻击算法
void RandomAttack(TArray<UAnimMontage*> NeedAnim, int32& LastAttackAnimIndex);
- 随机攻击算法逻辑
void ASoulBaseCharacter::RandomAttack(TArray<UAnimMontage*> NeedAnim, int32& LastAttackAnimIndex)
{
UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
if (CurAnimInstance)
{
//播放随机动画
int32 AttackAnimIndex = UKismetMathLibrary::RandomIntegerInRange(0, NeedAnim.Num() - 1);
if (AttackAnimIndex != LastAttackAnimIndex)
{
LastAttackAnimIndex = AttackAnimIndex;
CurAnimInstance->Montage_Play(NeedAnim[AttackAnimIndex]);
}
else
{
if (AttackAnimIndex == 0)
{
int32 AddIndexNum = UKismetMathLibrary::RandomIntegerInRange(0, NeedAnim.Num() - 2);
AttackAnimIndex += AddIndexNum;
LastAttackAnimIndex = AttackAnimIndex;
CurAnimInstance->Montage_Play(NeedAnim[AttackAnimIndex]);
}
else
{
AttackAnimIndex--;
LastAttackAnimIndex = AttackAnimIndex;
CurAnimInstance->Montage_Play(NeedAnim[AttackAnimIndex]);
}
}
}
}
PlayerCharacter
类中的MeleeAttack方法就该这样写了
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]);
// }
// }
//}
//将状态改变为攻击状态
PlayerBehavior = EPlayerBehavior::EPB_ATTACK;
//每次攻击都得减去体力值
Stamina -= MeleeAttackSubStamina;
//将状态改变为备战状态
MeleeState = EMeleeState::EMS_PREPARE;
MeleeBehaviorStateWarToCommon = 10.f;//每次攻击后重新置为10秒计时
//摄像机震动反馈
CameraShakeFeedBack();
RandomAttack(MeleeAttackAnim, LastMeleeIndex);
}
}
- SwordAttack函数方法逻辑
void APlayerCharacter::SwordAttack()
{
if (CanSwodAttack())
{
//UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
//if (CurAnimInstance)
//{
// //将状态改变为攻击状态
// PlayerBehavior = EPlayerBehavior::EPB_ATTACK;
// Stamina -= SwordAttackSubStamina;
// //播放随机动画
// int32 AttackAnimIndex = UKismetMathLibrary::RandomIntegerInRange(0, SwordAttackAnim.Num() - 1);
// if (AttackAnimIndex != LastMeleeIndex)
// {
// LastMeleeIndex = AttackAnimIndex;
// CurAnimInstance->Montage_Play(SwordAttackAnim[AttackAnimIndex]);
// }
// else
// {
// if (AttackAnimIndex == 0)
// {
// int32 AddIndexNum = UKismetMathLibrary::RandomIntegerInRange(0, SwordAttackAnim.Num() - 2);
// AttackAnimIndex += AddIndexNum;
// LastMeleeIndex = AttackAnimIndex;
// CurAnimInstance->Montage_Play(SwordAttackAnim[AttackAnimIndex]);
// }
// else
// {
// AttackAnimIndex--;
// LastMeleeIndex = AttackAnimIndex;
// CurAnimInstance->Montage_Play(SwordAttackAnim[AttackAnimIndex]);
// }
// }
//}
//将状态改变为攻击状态
PlayerBehavior = EPlayerBehavior::EPB_ATTACK;
//每次攻击都得减去体力值
Stamina -= SwordAttackSubStamina;
//摄像机震动反馈
CameraShakeFeedBack();
RandomAttack(SwordAttackAnim, LastSwordIndex);
}
}
配置人物持剑攻击动画蒙太奇
-
重定向资产,创建蒙太奇
-
将持剑攻击蒙太奇添加到角色蓝图
-
在持剑攻击蒙太奇上添加重置动作通知,并更换插槽,添加转向算法通知
-
动画蓝图事件表中,通知重置角色状态
配置刀风效果
- 添加Niagara粒子模块
- 在
PlayerCharacter
类中添加Niagara组件
//剑风
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
class UNiagaraComponent* SwordNiagara;
- 创建这个组件,需要头文件
#include "NiagaraComponent.h"
APlayerCharacter::APlayerCharacter()
{
ScabbardMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ScabbardMesh"));
ScabbardMesh->SetupAttachment(GetMesh(), "Scabbard");
Sword = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Sword"));
Sword->SetupAttachment(GetMesh(), "Sword");
SwordNiagara = CreateDefaultSubobject<UNiagaraComponent>(TEXT("SwordNiagara"));
SwordNiagara->SetupAttachment(Sword);
MeleeBehaviorStateWarToCommon = 10.;//默认十秒
}
- 将Niagara特效添加到角色的Niagara组件上并且调好位置
- 默认Niagara特效是不激活的,只有在挥刀的那个瞬间激活,逻辑:使用动画通知状态,开始挥刀的那个时间激活, 结束挥刀不激活
- 默认不激活
- 新建一个动画通知状态蓝图编写逻辑
- 然后将这个动画通知状态添加到持剑攻击蒙太奇上
制作持剑翻滚
- 重定向资源
- 添加持剑翻滚蒙太奇动画重置通知,设置插槽,角色动画蓝图中重置角色状态
- 持剑翻滚与普通状态的翻滚是一样的逻辑,可以优化一下代码,把播放翻滚的逻辑改为一个函数方法
- 在
SoulBaseCharacte
类中新建一个函数方法专门用来播放翻滚以及滑行的逻辑,然后新建一个持剑翻滚消耗体力值的变量
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
float SwordRollingSubStamina;//持剑翻滚消耗体力值
SwordRollingSubStamina = 12.f;
//翻滚算法
void PlayerRollingOrSlideAnim(TArray<UAnimMontage*> RollingOrSlideAnim);
- 翻滚算法逻辑
void ASoulBaseCharacter::PlayerRollingOrSlideAnim(TArray<UAnimMontage*> RollingOrSlideAnim)
{
UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
if (CurAnimInstance)
{
if (RollingForwardValue == 1)
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[0], RollingAnimPlayRate);
}
else if (RollingForwardValue == -1)
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[1], RollingAnimPlayRate);
}
else if (RollingRightValue == 1)
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[2], RollingAnimPlayRate);
}
else if (RollingRightValue == -1)
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[3], RollingAnimPlayRate);
}
else
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[0], RollingAnimPlayRate);
}
}
}
- 在
PlayerCharacte
类中之前写的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);
// }
//}
//每次翻滚减去体力
Stamina -= MeleeRollingSubStamina;
PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
PlayerRollingOrSlideAnim(MeleeRollingAnim);
}
}
- 新建是否可以持剑翻滚的方法函数和存放持剑翻滚的蒙太奇数组
//是否可以持剑翻滚
bool CanSwordRolling();
//持剑翻滚蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> SwordRollingAnim;
- 是否可以持剑翻滚逻辑
bool APlayerCharacter::CanSwordRolling()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= SwordRollingSubStamina)
{
return true;
}
else if (Stamina < SwordRollingSubStamina)
{
ShowStaminaNotEnoughText();
}
return false;
}
- 持剑攻击函数逻辑
void APlayerCharacter::SwordRolling()
{
if (CanSwordRolling())
{
//每次翻滚减去体力
Stamina -= SwordRollingSubStamina;
PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
PlayerRollingOrSlideAnim(SwordRollingAnim);
}
}
- 将持剑蒙太奇添加到角色蓝图上
持剑滑行逻辑
-
重定向资源
-
添加持剑滑行蒙太奇动画重置通知,设置插槽,角色动画蓝图中重置角色状态
-
PlayerCharacte
类中之前的MeleeSlide方法也可以改写成这样
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);
// }
//}
//每次翻滚减去体力
Stamina -= MeleeRollingSubStamina;
PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
PlayerRollingOrSlideAnim(MeleeSlideAnim);
}
}
- 新建是否可以持剑滑行的方法函数和存放持剑滑行的蒙太奇数组
//是否可以切换武器
bool CanChangeWeapon();
//是否可以持剑攻击
bool CanSwodAttack();
//是否可以持剑翻滚
bool CanSwordRolling();
//是否可以持剑滑行
bool CanSwordSlide();
//近战攻击蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> MeleeAttackAnim;
//近战翻滚蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> MeleeRollingAnim;
//近战滑行蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> MeleeSlideAnim;
//持剑攻击蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> SwordAttackAnim;
//持剑翻滚蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> SwordRollingAnim;
//持剑滑行蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> SwordSlideAnim;
- CanSwordSlide函数逻辑
bool APlayerCharacter::CanSwordSlide()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= SwordRollingSubStamina)
{
return true;
}
else if (Stamina < SwordRollingSubStamina)
{
ShowStaminaNotEnoughText();
}
return false;
}
- 持剑滑行逻辑
void APlayerCharacter::SwordSlide()
{
if (CanSwordSlide())
{
Stamina -= SwordRollingSubStamina;
PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
PlayerRollingOrSlideAnim(SwordSlideAnim);
}
}
- 添加蒙太奇动画到角色蓝图上
PlayerCharacte.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "DarkSoulsGame/Characters/SoulBaseCharacter.h"
#include "PlayerCharacter.generated.h"
/**
*
*/
UCLASS()
class DARKSOULSGAME_API APlayerCharacter : public ASoulBaseCharacter
{
GENERATED_BODY()
public:
APlayerCharacter();
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
class UStaticMeshComponent* ScabbardMesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
class USkeletalMeshComponent* Sword;
//剑风
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
class UNiagaraComponent* SwordNiagara;
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual void Tick(float DeltaTime) override;
//重写Attack
virtual void Attack() override;
//重写Rolling
virtual void Rolling() override;
//重写Slide
virtual void Slide() override;
//重写ChangeWeapon
virtual void ChangeWeapon() override;
void MeleeAttack();
void SwordAttack();
bool CanMeleeAttack();
void MeleeRolling();
void SwordRolling();
bool CanMeleeRolling();
void MeleeSlide();
void SwordSlide();
bool CanMeleeSlide();
//是否可以切换武器
bool CanChangeWeapon();
//是否可以持剑攻击
bool CanSwodAttack();
//是否可以持剑翻滚
bool CanSwordRolling();
//是否可以持剑滑行
bool CanSwordSlide();
//记录从备战状态到普通状态的变量时间
float MeleeBehaviorStateWarToCommon;
protected:
private:
//近战攻击蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> MeleeAttackAnim;
//近战翻滚蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> MeleeRollingAnim;
//近战滑行蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> MeleeSlideAnim;
//持剑攻击蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> SwordAttackAnim;
//持剑翻滚蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> SwordRollingAnim;
//持剑滑行蒙太奇
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "AnimMontage", meta = (AllowPrivateAccess = "true"))
TArray<UAnimMontage*> SwordSlideAnim;
};
PlayerCharacte.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "PlayerCharacter.h"
#include "DarkSoulsGame/DarkSoulPlayerController.h"
#include "Kismet/KismetMathLibrary.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/SkeletalMeshSocket.h"
#include "NiagaraComponent.h"
APlayerCharacter::APlayerCharacter()
{
ScabbardMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ScabbardMesh"));
ScabbardMesh->SetupAttachment(GetMesh(), "Scabbard");
Sword = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Sword"));
Sword->SetupAttachment(GetMesh(), "Sword");
SwordNiagara = CreateDefaultSubobject<UNiagaraComponent>(TEXT("SwordNiagara"));
SwordNiagara->SetupAttachment(Sword);
MeleeBehaviorStateWarToCommon = 10.;//默认十秒
}
void APlayerCharacter::BeginPlay()
{
Super::BeginPlay();
ADarkSoulPlayerController* PlayerController = Cast<ADarkSoulPlayerController>(Controller);
if (PlayerController)
{
UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer());
if (Subsystem)
{
Subsystem->AddMappingContext(PlayerMappingContext, 0);
}
}
}
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, &APlayerCharacter::Attack);
EnhancedInputComponent->BindAction(RollingAction, ETriggerEvent::Started, this, &APlayerCharacter::Rolling);
EnhancedInputComponent->BindAction(SlideAction, ETriggerEvent::Started, this, &APlayerCharacter::Slide);
EnhancedInputComponent->BindAction(ChangeWeaponAction, ETriggerEvent::Started, this, &APlayerCharacter::ChangeWeapon);
}
}
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;
}
}
}
void APlayerCharacter::Attack()
{
switch (WeaponType)
{
case EWeaponType::EWT_NONE:
break;
case EWeaponType::EWT_MELEE:
MeleeAttack();
break;
case EWeaponType::EWT_SWORD:
SwordAttack();
break;
default:
break;
}
}
void APlayerCharacter::Rolling()
{
switch (WeaponType)
{
case EWeaponType::EWT_MELEE:
MeleeRolling();
break;
case EWeaponType::EWT_SWORD:
SwordRolling();
break;
}
}
void APlayerCharacter::Slide()
{
switch (WeaponType)
{
case EWeaponType::EWT_MELEE:
MeleeSlide();
break;
case EWeaponType::EWT_SWORD:
SwordSlide();
break;
}
}
void APlayerCharacter::ChangeWeapon()
{
if (CanChangeWeapon())
{
if (WeaponType == EWeaponType::EWT_MELEE)
{
//改为持剑状态
WeaponType = EWeaponType::EWT_SWORD;
//将武器添加到右手
Sword->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, "WEAPON_R");
bEquipWeapon = true;
}
else if (WeaponType == EWeaponType::EWT_SWORD)
{
//改为普通状态
WeaponType = EWeaponType::EWT_MELEE;
//将武器放回剑鞘
Sword->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, "Sword");
bEquipWeapon = 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;
// //摄像机震动反馈
// 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]);
// }
// }
//}
//将状态改变为攻击状态
PlayerBehavior = EPlayerBehavior::EPB_ATTACK;
//每次攻击都得减去体力值
Stamina -= MeleeAttackSubStamina;
//将状态改变为备战状态
MeleeState = EMeleeState::EMS_PREPARE;
MeleeBehaviorStateWarToCommon = 10.f;//每次攻击后重新置为10秒计时
//摄像机震动反馈
CameraShakeFeedBack();
RandomAttack(MeleeAttackAnim, LastMeleeIndex);
}
}
void APlayerCharacter::SwordAttack()
{
if (CanSwodAttack())
{
//UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
//if (CurAnimInstance)
//{
// //将状态改变为攻击状态
// PlayerBehavior = EPlayerBehavior::EPB_ATTACK;
// Stamina -= SwordAttackSubStamina;
// //播放随机动画
// int32 AttackAnimIndex = UKismetMathLibrary::RandomIntegerInRange(0, SwordAttackAnim.Num() - 1);
// if (AttackAnimIndex != LastMeleeIndex)
// {
// LastMeleeIndex = AttackAnimIndex;
// CurAnimInstance->Montage_Play(SwordAttackAnim[AttackAnimIndex]);
// }
// else
// {
// if (AttackAnimIndex == 0)
// {
// int32 AddIndexNum = UKismetMathLibrary::RandomIntegerInRange(0, SwordAttackAnim.Num() - 2);
// AttackAnimIndex += AddIndexNum;
// LastMeleeIndex = AttackAnimIndex;
// CurAnimInstance->Montage_Play(SwordAttackAnim[AttackAnimIndex]);
// }
// else
// {
// AttackAnimIndex--;
// LastMeleeIndex = AttackAnimIndex;
// CurAnimInstance->Montage_Play(SwordAttackAnim[AttackAnimIndex]);
// }
// }
//}
//将状态改变为攻击状态
PlayerBehavior = EPlayerBehavior::EPB_ATTACK;
//每次攻击都得减去体力值
Stamina -= SwordAttackSubStamina;
//摄像机震动反馈
CameraShakeFeedBack();
RandomAttack(SwordAttackAnim, LastSwordIndex);
}
}
bool APlayerCharacter::CanMeleeAttack()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= MeleeAttackSubStamina)
{
return true;
}
else if (Stamina < MeleeAttackSubStamina)
{
ShowStaminaNotEnoughText();
}
return false;
}
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);
// }
//}
//每次翻滚减去体力
Stamina -= MeleeRollingSubStamina;
PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
PlayerRollingOrSlideAnim(MeleeRollingAnim);
}
}
void APlayerCharacter::SwordRolling()
{
if (CanSwordRolling())
{
//每次翻滚减去体力
Stamina -= SwordRollingSubStamina;
PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
PlayerRollingOrSlideAnim(SwordRollingAnim);
}
}
bool APlayerCharacter::CanMeleeRolling()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= MeleeRollingSubStamina)
{
return true;
}
else if (Stamina < MeleeRollingSubStamina)
{
ShowStaminaNotEnoughText();
}
return false;
}
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);
// }
//}
//每次翻滚减去体力
Stamina -= MeleeRollingSubStamina;
PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
PlayerRollingOrSlideAnim(MeleeSlideAnim);
}
}
void APlayerCharacter::SwordSlide()
{
if (CanSwordSlide())
{
Stamina -= SwordRollingSubStamina;
PlayerBehavior = EPlayerBehavior::EPB_ROLLING;
PlayerRollingOrSlideAnim(SwordSlideAnim);
}
}
bool APlayerCharacter::CanMeleeSlide()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= MeleeRollingSubStamina)
{
return true;
}
else if (Stamina < MeleeRollingSubStamina)
{
ShowStaminaNotEnoughText();
}
return false;
}
bool APlayerCharacter::CanChangeWeapon()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE)
{
return true;
}
return false;
}
bool APlayerCharacter::CanSwodAttack()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= SwordAttackSubStamina)
{
return true;
}
else if (Stamina < SwordAttackSubStamina)
{
ShowStaminaNotEnoughText();
}
return false;
}
bool APlayerCharacter::CanSwordRolling()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= SwordRollingSubStamina)
{
return true;
}
else if (Stamina < SwordRollingSubStamina)
{
ShowStaminaNotEnoughText();
}
return false;
}
bool APlayerCharacter::CanSwordSlide()
{
if (PlayerBehavior == EPlayerBehavior::EPB_IDLE && Stamina >= SwordRollingSubStamina)
{
return true;
}
else if (Stamina < SwordRollingSubStamina)
{
ShowStaminaNotEnoughText();
}
return false;
}
SoulBaseCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "../SoulEnumType.h"
#include "SoulBaseCharacter.generated.h"
UCLASS()
class DARKSOULSGAME_API ASoulBaseCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
ASoulBaseCharacter();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
class UInputMappingContext* PlayerMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
class UInputAction* MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
class UInputAction* LookAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
class UInputAction* RunAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
class UInputAction* AttackAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* RollingAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* SlideAction;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
UInputAction* ChangeWeaponAction;
//角色状态
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character Properties")
bool bIsRun;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character Attribute")
EWeaponType WeaponType;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character Attribute")
EPlayerBehavior PlayerBehavior;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Character Attribute")
EMeleeState MeleeState;
//角色机制
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 MeleeRollingSubStamina;//翻滚消耗体力值
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
float SwordRollingSubStamina;//持剑翻滚消耗体力值
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
float SwordAttackSubStamina;//持剑攻击消耗体力值
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Attribute")
float IncreaseStamina;//增长体力值
//攻击时转向的目标值
FRotator DesiredRotation;
//翻滚的朝向值(前后)
int32 RollingForwardValue;
//翻滚的朝向值(左右)
int32 RollingRightValue;
//翻滚蒙太奇播放速率
float RollingAnimPlayRate;
//是否装备武器
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Attack")
bool bEquipWeapon;
//记录上一个拳击攻击动作的标志变量
int32 LastMeleeIndex;
//记录上一个持剑攻击动作的标志变量
int32 LastSwordIndex;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
void Run();
void StopRun();
//攻击
virtual void Attack();
//翻滚
virtual void Rolling();
//滑行(近战:翻跟头/持剑:滑行翻滚)
virtual void Slide();
//切换武器
virtual void ChangeWeapon();
UFUNCTION(BlueprintCallable,BlueprintPure)
float GetCurStamina();//获取体力
UFUNCTION(BlueprintCallable, BlueprintPure)
float GetMaxStamina();//获取最大体力
//摄像机震动反馈
UFUNCTION(BlueprintImplementableEvent)
void CameraShakeFeedBack();
//获取UI并显示提示文本
void ShowStaminaNotEnoughText();
//计算按键的方向
FRotator CalculateRotation();
//线性插入旋转值
UFUNCTION(BlueprintCallable)
void RInterpRotation();
//随机攻击算法
void RandomAttack(TArray<UAnimMontage*> NeedAnim, int32& LastAttackAnimIndex);
//翻滚算法
void PlayerRollingOrSlideAnim(TArray<UAnimMontage*> RollingOrSlideAnim);
private:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera", meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* SpringArm;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera", meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FollowCamera;
};
SoulBaseCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SoulBaseCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Engine/Engine.h"
#include <DarkSoulsGame/DarkSoulPlayerController.h>
#include <DarkSoulsGame/DarkSoulHUD.h>
#include <DarkSoulsGame/UI/UI_FightMain.h>
#include "Kismet/KismetMathLibrary.h"
// 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;//空闲状态
//默认武器装备为false
bEquipWeapon = false;
MaxStamina = 100.f;
Stamina = MaxStamina;
MeleeAttackSubStamina = 10.f;
MeleeRollingSubStamina = 15.f;
SwordAttackSubStamina = 12.f;
SwordRollingSubStamina = 12.f;
IncreaseStamina = 1.f;
//攻击转向默认值为0
DesiredRotation = FRotator(0., 0., 0.);
//翻滚方向值默认值为0
RollingForwardValue = 0.0;
RollingRightValue = 0.0;
//翻滚动画播放速率
RollingAnimPlayRate = 1.0;
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;
bUseControllerRotationYaw = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
//记录上一个攻击动作的标志变量
LastMeleeIndex = 0;
LastSwordIndex = 0;
}
FRotator ASoulBaseCharacter::CalculateRotation()
{
//获取最后一次输入的向量
FVector LastVector = GetCharacterMovement()->GetLastInputVector();
//判断最后一次输入向量是否为0,如果是就返回这个目标值,如果不为0就返回这个Vector转换为Rotator的目标值
if (LastVector != FVector(0,0,0))
{
return UKismetMathLibrary::MakeRotFromX(LastVector);
}
else
{
return DesiredRotation;
}
}
void ASoulBaseCharacter::RInterpRotation()
{
//实时进行转向插值
FRotator RInterpRotation = UKismetMathLibrary::RInterpTo(GetActorRotation(), DesiredRotation,
GetWorld()->GetDeltaSeconds(), 5.f);
SetActorRotation(FRotator(0., RInterpRotation.Yaw, 0.));
}
void ASoulBaseCharacter::RandomAttack(TArray<UAnimMontage*> NeedAnim, int32& LastAttackAnimIndex)
{
UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
if (CurAnimInstance)
{
//播放随机动画
int32 AttackAnimIndex = UKismetMathLibrary::RandomIntegerInRange(0, NeedAnim.Num() - 1);
if (AttackAnimIndex != LastAttackAnimIndex)
{
LastAttackAnimIndex = AttackAnimIndex;
CurAnimInstance->Montage_Play(NeedAnim[AttackAnimIndex]);
}
else
{
if (AttackAnimIndex == 0)
{
int32 AddIndexNum = UKismetMathLibrary::RandomIntegerInRange(0, NeedAnim.Num() - 2);
AttackAnimIndex += AddIndexNum;
LastAttackAnimIndex = AttackAnimIndex;
CurAnimInstance->Montage_Play(NeedAnim[AttackAnimIndex]);
}
else
{
AttackAnimIndex--;
LastAttackAnimIndex = AttackAnimIndex;
CurAnimInstance->Montage_Play(NeedAnim[AttackAnimIndex]);
}
}
}
}
void ASoulBaseCharacter::PlayerRollingOrSlideAnim(TArray<UAnimMontage*> RollingOrSlideAnim)
{
UAnimInstance* CurAnimInstance = GetMesh()->GetAnimInstance();
if (CurAnimInstance)
{
if (RollingForwardValue == 1)
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[0], RollingAnimPlayRate);
}
else if (RollingForwardValue == -1)
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[1], RollingAnimPlayRate);
}
else if (RollingRightValue == 1)
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[2], RollingAnimPlayRate);
}
else if (RollingRightValue == -1)
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[3], RollingAnimPlayRate);
}
else
{
CurAnimInstance->Montage_Play(RollingOrSlideAnim[0], RollingAnimPlayRate);
}
}
}
// Called when the game starts or when spawned
void ASoulBaseCharacter::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ASoulBaseCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
//实时计算攻击转向的目标值
DesiredRotation = CalculateRotation();
}
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);
}
}
void ASoulBaseCharacter::Look(const FInputActionValue& Value)
{
FVector2D LookAxisVector = Value.Get<FVector2D>();
if (Controller)
{
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
}
void ASoulBaseCharacter::Run()
{
bIsRun = true;
GetCharacterMovement()->MaxWalkSpeed = 600.f;
//bIsRun = !bIsRun;
//StopRun();
}
void ASoulBaseCharacter::StopRun()
{
bIsRun = false;
GetCharacterMovement()->MaxWalkSpeed = 200.f;
//float WalkSpeed = 200.0f; // 步行速度
//float RunSpeed = 600.0f; // 奔跑速度
//float ActualSpeed;
//if (bIsRun)
//{
// ActualSpeed = RunSpeed;
//}
//else
//{
// ActualSpeed = WalkSpeed;
//}
//GetCharacterMovement()->MaxWalkSpeed = ActualSpeed;
}
void ASoulBaseCharacter::Attack()
{
}
void ASoulBaseCharacter::Rolling()
{
}
void ASoulBaseCharacter::Slide()
{
}
void ASoulBaseCharacter::ChangeWeapon()
{
}
float ASoulBaseCharacter::GetCurStamina()
{
return Stamina;
}
float ASoulBaseCharacter::GetMaxStamina()
{
return MaxStamina;
}
void ASoulBaseCharacter::ShowStaminaNotEnoughText()
{
//获取到Controller
ADarkSoulPlayerController* PC = Cast<ADarkSoulPlayerController>(Controller);
if (Controller)
{
//获取到HUD
ADarkSoulHUD* HUD = Cast<ADarkSoulHUD>(PC->GetHUD());
if (HUD)
{
//显示提示文本
HUD->GetFightMainUI()->ShowStaminaText();
}
}
}