在上一篇文章中,我们实现了使用AI行为树控制敌人进行移动,它们可以一直跟随玩家,虽然现在还未实现攻击。但在移动过程中,我发现了有两个问题,第一个是敌人转向的时候很僵硬,可以说是瞬间转向的,这个原因是角色默认是使用控制器切换朝向造成的。第二个问题是敌人现在也可以拾取药瓶喝药,这个不能忍,造成这个原因是因为药瓶是碰撞触发的,没有对拾取的目标进行一个类型区分。
在这一篇中,我们主要将这两个问题解决一下,然后再继续进行敌人的AI行为树的制作。
处理敌人转向问题
敌人的转向问题很好解决,我们之前就处理过角色身上的转向问题,首先就是先将角色身上的使用控制器控制Yaw给关闭。
在蓝图中,我们可以将 使用控制器旋转Yaw 选项关闭
然后开启 将旋转朝向运动 (角色会朝向运动的方向)
我们将在c++里面设置,将这个配置写死,保证以后所有的敌人都使用这个配置。
打开敌人基类,在 构造函数中增加以下代码,会有同样的效果
bUseControllerRotationPitch = false; bUseControllerRotationRoll = false; bUseControllerRotationYaw = false; GetCharacterMovement()->bUseControllerDesiredRotation = true;
这样,就完成了对敌人转向问题的处理。
处理敌人可以拾取的问题
接下来我们处理敌人可以拾取药瓶的问题,比如普通药瓶敌人是无法拾取的,但是火堆的那种,可以实现对敌人的应用,我准备在基类上面增加属性去控制。
首先在基类上面增加属性,这里我额外增加了一个控制销毁的属性,我们之前是在蓝图中实现销毁的,现在,我们将此移动到代码中。
//Instant和Duration的GE在应用后,此物体是否需要被销毁 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") bool bDestroyOnEffectApplication = true; //敌人是否能够拾取此物体 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") bool bApplyEffectsToEnemies = false;
之前我们实现的时候,是在蓝图中实现的,调用代码实现的函数节点,我们也在蓝图中实现应用GE
接着,我们修改逻辑,实现在应用效果后删除的逻辑,我们将在应用效果后在非无限时间GE后面,判断是否需要在应用后销毁
然后就是处理是否敌人可拾取的权限,首先我们需要判断当前是否为敌人,然后再判断敌人是否有权限
如果不是敌人 &&后面的逻辑也不会执行,这个判断直接返回false,如果是敌人,并且敌人无法拾取,变量为false,后面也是true,那么将不会执行后面的逻辑。
if(TargetActor->ActorHasTag("Enemy") && !bApplyEffectsToEnemies) return;
我们需要在OnOverlap(触发开始重叠事件)时判断
void ARPGEffectActor::OnOverlap(AActor* TargetActor) { //如果触发角色类型为敌人,并且此拾取物设置无法被敌人拾取 if(TargetActor->ActorHasTag("Enemy") && !bApplyEffectsToEnemies) return;
针对于一些在OnEndOverlap(结束重叠事件)时也会触发,所以,我们也要在结束时判断
void ARPGEffectActor::OnEndOverlap(AActor* TargetActor) { //如果触发角色类型为敌人,并且此拾取物设置无法被敌人拾取 if(TargetActor->ActorHasTag("Enemy") && !bApplyEffectsToEnemies) return;
接着编译打开蓝图,我们这些配置项就可以有效的利用起来了
我们可以选择应用什么类型的GE,并在接触时触发还是结束重叠时触发,以及对于无限时间的GE,可以选择在离开时是否清楚GE的效果
OnOverlap和OnEndOverlap两个函数没有在函数中调用,我们可以按需调用,比如只需要调用OnOverlap函数,我们在蓝图中调用即可
如果是需要在触发结束重叠时触发,那么我们需要将OnEndOverlay调用,但是对于那种无限时间的GE,并且还需要在结束时清除掉,我们就需要两个方法都调用
源码
RPGEffectActor.h
// 版权归暮志未晚所有。 #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "GameplayEffectTypes.h" #include "RPGEffectActor.generated.h" struct FActiveGameplayEffectHandle; class UAbilitySystemComponent; class UGameplayEffect; //效果应用状态枚举 UENUM(BlueprintType) enum class EEffectApplicationPolicy { ApplyOnOverlap, ApplyOnEndOverlap, DoNotApply }; //效果移除的状态枚举 UENUM(BlueprintType) enum class EEffectRemovalPolicy { RemoveOnEndOverlap, DoNotRemove }; /** * 在场景中可放置的影响角色属性的物件基类 */ UCLASS() class AURA_API ARPGEffectActor : public AActor { GENERATED_BODY() public: ARPGEffectActor(); protected: // 游戏开始或生成对象时回调 virtual void BeginPlay() override; //给与目标添加GameplayEffect效果 UFUNCTION(BlueprintCallable) void ApplyEffectToTarget(AActor* TargetActor, TSubclassOfGameplayEffectClass); //在重叠开始时处理效果的添加删除逻辑 UFUNCTION(BlueprintCallable) void OnOverlap(AActor* TargetActor); //在重叠结束时处理效果的添加删除逻辑 UFUNCTION(BlueprintCallable) void OnEndOverlap(AActor* TargetActor); //Instant和Duration的GE在应用后,此物体是否需要被销毁 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") bool bDestroyOnEffectApplication = true; //敌人是否能够拾取此物体 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") bool bApplyEffectsToEnemies = false; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") TSubclassOf InstantGameplayEffectClass; //生成GameplayEffect的类 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") EEffectApplicationPolicy InstantEffectApplicationPolicy = EEffectApplicationPolicy::DoNotApply; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") TSubclassOf DurationGameplayEffectClass; //生成具有一定持续时间的GameplayEffect的类 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") EEffectApplicationPolicy DurationEffectApplicationPolicy = EEffectApplicationPolicy::DoNotApply; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") TSubclassOf InfinityGameplayEffectClass; //生成具有一定持续时间的GameplayEffect的类 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") EEffectApplicationPolicy InfinityEffectApplicationPolicy = EEffectApplicationPolicy::DoNotApply; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") EEffectRemovalPolicy InfinityEffectRemovalPolicy = EEffectRemovalPolicy::RemoveOnEndOverlap; //用于存储当前已经激活的GameplayEffect的句柄的map TMap ActiveEffectHandles; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Apply Effects") float ActorLevel = 1.f; };
RPGEffectActor.cpp
// 版权归暮志未晚所有。 #include "Actor/RPGEffectActor.h" #include "ActiveGameplayEffectHandle.h" #include "AbilitySystemBlueprintLibrary.h" #include "AbilitySystemComponent.h" ARPGEffectActor::ARPGEffectActor() { // 设置当前对象是否每帧调用Tick() PrimaryActorTick.bCanEverTick = false; SetRootComponent(CreateDefaultSubobject("SceneRoot")); } void ARPGEffectActor::BeginPlay() { Super::BeginPlay(); } void ARPGEffectActor::ApplyEffectToTarget(AActor* TargetActor, TSubclassOf GameplayEffectClass) { /** * 默认自己编写从actor身上获取ASC的方式 * IAbilitySystemInterface* ASCInterface = Cast (TargetActor); //判断当前actor是否有技能系统接口 if(ASCInterface) { UAbilitySystemComponent* TargetASC = ASCInterface->GetAbilitySystemComponent(); } */ //获取ASC UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor); if(TargetASC == nullptr) return; check(GameplayEffectClass); //创建Effect的句柄 包含了实例化Effect所需数据 FGameplayEffectContextHandle EffectContextHandle = TargetASC->MakeEffectContext(); //设置创建Effect的对象 EffectContextHandle.AddSourceObject(this); //Effect的实例化后的句柄,可以通过此来寻找调用 const FGameplayEffectSpecHandle EffectSpecHandle = TargetASC->MakeOutgoingSpec(GameplayEffectClass, ActorLevel, EffectContextHandle); //从句柄中获取到实例的地址,并被应用。 const FActiveGameplayEffectHandle ActiveGameplayEffectHandle = TargetASC->ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get()); //从句柄中获取到定义的对象,并判断设置的 const bool bIsInfinite = EffectSpecHandle.Data.Get()->Def.Get()->DurationPolicy == EGameplayEffectDurationType::Infinite; //在是无限时间效果和需要在结束时清除掉时,将效果句柄添加到map if(bIsInfinite && InfinityEffectRemovalPolicy == EEffectRemovalPolicy::RemoveOnEndOverlap) { ActiveEffectHandles.Add(ActiveGameplayEffectHandle, TargetASC); } else if(bDestroyOnEffectApplication) //如果设置了应用时删除,除了Infinite的都会自动删除 { Destroy(); } } void ARPGEffectActor::OnOverlap(AActor* TargetActor) { //如果触发角色类型为敌人,并且此拾取物设置无法被敌人拾取 if(TargetActor->ActorHasTag("Enemy") && !bApplyEffectsToEnemies) return; if(InstantEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap) { ApplyEffectToTarget(TargetActor, InstantGameplayEffectClass); } if(DurationEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap) { ApplyEffectToTarget(TargetActor, DurationGameplayEffectClass); } if(InfinityEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap) { ApplyEffectToTarget(TargetActor, InfinityGameplayEffectClass); } } void ARPGEffectActor::OnEndOverlap(AActor* TargetActor) { //如果触发角色类型为敌人,并且此拾取物设置无法被敌人拾取 if(TargetActor->ActorHasTag("Enemy") && !bApplyEffectsToEnemies) return; //添加效果 if(InstantEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap) { ApplyEffectToTarget(TargetActor, InstantGameplayEffectClass); } if(DurationEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap) { ApplyEffectToTarget(TargetActor, DurationGameplayEffectClass); } if(InfinityEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap) { ApplyEffectToTarget(TargetActor, InfinityGameplayEffectClass); } //删除效果 if(InfinityEffectRemovalPolicy == EEffectRemovalPolicy::RemoveOnEndOverlap) { UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor); if(!IsValid(TargetASC)) return; //创建存储需要移除的效果句柄存储Key,用于遍历完成后移除效果 TArray HandlesToRemove; //循环map内存的数据 for(TTuple HandlePair : ActiveEffectHandles) { //判断是否ASC相同 if(TargetASC == HandlePair.Value) { //通过句柄将效果移除,注意,有可能有多层效果,不能将其它层的效果也移除掉,所以只移除一层 TargetASC->RemoveActiveGameplayEffect(HandlePair.Key, 1); //添加到移除列表 HandlesToRemove.Add(HandlePair.Key); } } //遍历完成后,在Map中将移除效果的KeyValue删除 for(auto& Handle : HandlesToRemove) { ActiveEffectHandles.FindAndRemoveChecked(Handle); } } }
还没有评论,来说两句吧...