前面,我们实现了近战攻击技能,敌人通过AI靠近玩家,并且通过AI还能够触发近战攻击的释放。现在我们思考一个问题,如果敌人没有武器,攻击的手段是用的双手,我们该如何去获取它的攻击范围。
现在实现的一套是获取武器的攻击范围,我们还没有实现在没有武器的情况下,还需要区分左右手,这样该如何去实现。
我们准备增加对应的标签,然后新增一个结构体,用于动画和蒙太奇的映射,最好能把需要获取的骨骼位置也写入到其中,这样如果角色有多个近战攻击动画,我们可以随机一个使用,然后从内部获取所需的数据,进行后续范围判断。
逻辑是这样的,开干。
兼容更多的攻击方式
现在我们只支持使用武器攻击,我们需要再增加比如左右手的攻击,后续甚至有可能增加嘴的攻击,比如龙喷火。
首先,我们在标签文件RPGGameplayTags.h中增加一些标签,使用标签去区分不同的攻击方式。
FGameplayTag Montage_Attack_Weapon; //使用武器攻击蒙太奇标签
FGameplayTag Montage_Attack_RightHand; //右手攻击蒙太奇标签
FGameplayTag Montage_Attack_LeftHand; //左手攻击蒙太奇标签
接着在cpp文件中,将标签注册到标签管理器中
/*
* Montage
*/
GameplayTags.Montage_Attack_Weapon = UGameplayTagsManager::Get()
.AddNativeGameplayTag(
FName("Montage.Attack.Weapon"),
FString("使用武器攻击蒙太奇标签")
);
GameplayTags.Montage_Attack_LeftHand = UGameplayTagsManager::Get()
.AddNativeGameplayTag(
FName("Montage.Attack.LeftHand"),
FString("左手攻击蒙太奇标签")
);
GameplayTags.Montage_Attack_RightHand = UGameplayTagsManager::Get()
.AddNativeGameplayTag(
FName("Montage.Attack.RightHand"),
FString("右手攻击蒙太奇标签")
);
然后编译打开标签管理器,查看标签有没有被正确注册
有了标签,我们需要一个可以设置和标签对应蒙太奇动画的结构体,这样,敌人的普通攻击可以设置多个,在技能里面可以兼容多种攻击方式,我们没必须为每一种攻击方式创建一个攻击技能。
在CombatInterface.h文件中,我们新增一个结构体,用于存储对应的蒙太奇,标签和骨骼插槽。蒙太奇是攻击使用的动画,标签是在动画播放时触发的标签,这样可以在接收到标签的事件通知时,触发攻击范围检测,而骨骼插槽的设置,是为了设置攻击的范围的位置,
//蒙太奇动画和标签以及骨骼位置的映射,用于攻击技能获取和设置攻击范围
USTRUCT(BlueprintType)
struct FTaggedMontage
{
GENERATED_BODY()
//使用的蒙太奇
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
UAnimMontage* Montage = nullptr;
//对应的标签
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FGameplayTag MontageTag;
//攻击时的触发伤害的骨骼插槽
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FName CombatTipSocketName; //设置技能释放的位置
};
有了结构体以后,我们需要设置它,在RPGCharacter.h中,我们在角色基类身上设置结构体数组,这样我们可以设置多个基础攻击动画。
UPROPERTY(EditAnywhere, Category="Combat")
TArray<FTaggedMontage> AttackMontage;
有了属性配置,我们需要一个函数去获取,所以接着在CombatInterface.h文件的战斗接口中添加一个函数用于获取。
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
TArray<FTaggedMontage> GetAttackMontages();
接着我们在角色基类里面实现它
virtual TArray<FTaggedMontage> GetAttackMontages_Implementation() override;
实现也很简单,直接返回即可
TArray<FTaggedMontage> ARPGCharacter::GetAttackMontages_Implementation()
{
return AttackMontage;
}
我们还需要一个获取骨骼插槽位置的函数,这个函数可以通过结构体去判断获取武器上的插槽还是角色模型身上的武器插槽。
所以我们在RPGCharacter.h中,接着创建一个虚函数,用于通过数据获取位置
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
FVector GetCombatSocketLocationByStruct(const FTaggedMontage TaggedMontage) const;
在角色基类中覆写
virtual FVector GetCombatSocketLocationByStruct_Implementation(const FTaggedMontage TaggedMontage) const override;
在实现这里,我们通过结构体判断当前是否使用武器攻击,如果使用武器攻击,则从武器上面获取插槽位置,反之,则从角色模型身上获取插槽位置。
FVector ARPGCharacter::GetCombatSocketLocationByStruct_Implementation(const FTaggedMontage TaggedMontage) const
{
if(TaggedMontage.MontageTag.MatchesTagExact(FRPGGameplayTags::Get().Montage_Attack_Weapon))
{
return Weapon->GetSocketLocation(TaggedMontage.CombatTipSocketName);
}
else
{
return GetMesh()->GetSocketLocation(TaggedMontage.CombatTipSocketName);
}
}
修改蓝图适配新的逻辑
上面,我们修改相关逻辑,编译后打开UE,我们也需要将蓝图修改以后,适配新的逻辑。
首先打开普通攻击技能蓝图,在设置完朝向后,从创建的数组中获取一条结构体使用,为了方便后面使用,我们将其保存为变量,如果直接链接到后面,随机的逻辑将会重新随机,所以我们保存为变量,并通过变量结构体获取需要使用的蒙太奇。
接着修改播放完成蒙太奇后的逻辑,事件修改为结构体内的标签,获取位置方法,改为通过结构体获取
如果蒙太奇被打断了,技能会无法结束,所以,我们还需要设置在动画完成或中断时,结束技能
打开战士的蓝图,配置结构体数据
最重要的一步,就是要修改蒙太奇里面的通知,这样才可以触发事件
修改完成以后查看效果是否正常。