|
上一章我们初步完成了攻击功能的实现,但是会出现滑步现象,且攻击方式过于单一,这次我们来解决这个问题。
此时我们需要重写动画蓝图类。
创建一个类继承自AnimInstance,命名为HeroAnimInstance。
头文件如下
class DDEMO_API UHeroAnimInstance : public UAnimInstance
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly)
float Speed;
UPROPERTY(BlueprintReadOnly)
float Direction;
UPROPERTY(BlueprintReadOnly)
bool bInAir;
UPROPERTY(BlueprintReadOnly)
bool isAttack;
UPROPERTY(EditAnywhere,BlueprintReadOnly)
class AHeroDemo* Hero;
UFUNCTION(BlueprintCallable)
void UpdateAnimInfo(float dt);
};
cpp文件如下
void UHeroAnimInstance::UpdateAnimInfo(float dt)
{
APawn* Owner = TryGetPawnOwner();
ACharacter* Player = Cast<ACharacter>(Owner);
if(Owner!=nullptr)
{
Speed = Owner->GetVelocity().Size();
Direction = CalculateDirection(Owner->GetVelocity(), Owner->GetBaseAimRotation());
bInAir = Player->GetMovementComponent()->IsFalling();
}
}
完成后进行编译,并以此为父类创建动画蓝图。蓝图内的操作基本与之前一致。
现在修改HeroDemo的代码。
在头文件增加如下代码:
bool bAttacking;//为判断是否在攻击
UFUNCTION(BlueprintCallable)//判定攻击动画结束,需要在动画蓝图中调用
void AttackEnd();
将头文件中移动的函数做一些修改,使之在攻击时不能调用。如:
void AHeroDemo::MoveForward(float Axis)
{
if((Controller!=NULL)&(Axis!=0.0f)
#if 1
&&!bAttacking
#endif
//以上代码用来作一个判断
){
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Axis);
}
}
Attack()更改如下:
void AHeroDemo::Attack()
{
bAttacking = true;
UAnimInstance* AnimInstance = Cast<UAnimInstance>(GetMesh()->GetAnimInstance());
if (AnimInstance != nullptr)
{
if (!AnimInstance->Montage_IsPlaying(AttackMontage))
{
AnimInstance->Montage_Play(AttackMontage);
AnimInstance->Montage_JumpToSection(FName("Attack01"), AttackMontage);
}
}
}
AttackEnd()代码如下:
void AHeroDemo::AttackEnd()
{
bAttacking = false;
}
但是我们的代码中却没有调用AttackEnd函数,我们需要到动画蓝图中调用,通过获得蒙太奇动画的结束时间来判断何时调用。
来到AttackM动画蒙太奇,添加两段攻击动画,并将三段动画在分段中命名为Attack01,Attack02,Attack03,并进行分段,效果如下:

再在下方通知中创建新通知,命名为AttackEnd,将位置调整到第一段攻击动画结束处。再分别复制两次到另外两段攻击动画。 
这样,当蒙太奇动画播放完成时就会发出通知。
再来到动画蓝图的事件图表,调用并连接节点如下:
编译运行,发现在攻击时方向键已然失效。
现在我们来设置不同的攻击状态,期望的目标是连续点击左键有三种不同的攻击动画,如连续两次点击之间时间间隔超过5s则重新计算。
增加的代码如下。
.h
int AttackStatus = 1;
//用于判断攻击状态
FTimerHandle RestTimer;
//声明定时器
void ClearRestTime();
//清除之前的攻击状态
.cpp
void AHeroDemo::ClearRestTime()
{
AttackStatus = 1;
}
更改.cpp中Attack函数如下:
void AHeroDemo::Attack()
{
bAttacking = true;
UAnimInstance* AnimInstance = Cast<UAnimInstance>(GetMesh()->GetAnimInstance());
if (AnimInstance != nullptr)
{
if (!AnimInstance->Montage_IsPlaying(AttackMontage))
{
AnimInstance->Montage_Play(AttackMontage);
switch (AttackStatus)
{
case 1:AnimInstance->Montage_JumpToSection(FName("Attack01"), AttackMontage);
AttackStatus++;
GetWorldTimerManager().SetTimer(RestTimer, this, &AHeroDemo::ClearRestTime, 0.1f, false, 3.0f);
break;
case 2:AnimInstance->Montage_JumpToSection(FName("Attack02"), AttackMontage);
AttackStatus++;
GetWorldTimerManager().ClearTimer(RestTimer);
GetWorldTimerManager().SetTimer(RestTimer, this, &AHeroDemo::ClearRestTime, 0.1f, false, 3.0f);
break;
case 3:AnimInstance->Montage_JumpToSection(FName("Attack03"), AttackMontage);
GetWorldTimerManager().ClearTimer(RestTimer);
AttackStatus = 1;
break;
}
}
}
}
这里使用switch语句判断不同的攻击状态,分支中设置定时器运行时间,在下一个分支语句中清除之前设置的定时器,之后重新设置。
编译运行,已能完成既定目标。
|