在前面,我們實現了敵人的AI系統,敵人可以根據自身的職業進行匹配對應的攻擊方式。比如近戰戰士會靠近目標后進行攻擊然后躲避目標的攻擊接著進行攻擊。我們實現了敵人的AI行為,但是現在還沒有實現需要釋放的技能,接下來,我們將實現敵人的使用的技能,剛好回顧一下之前的實現的功能。
首先基于之前的傷害技能基類,創建一個近戰技能基類
命名為近戰技能 RPGMeleeAbility
我們要實現技能的激活,需要使用到GameplayTag,所以我們增加一個新的技能標簽用于激活。
在RPGGameplayTags.h增加新的標簽
FGameplayTag Abilities_Attack; //攻擊技能激活標簽
然后將標簽添加到標簽管理器
/** Abilities*/GameplayTags.Abilities_Attack = UGameplayTagsManager::Get().AddNativeGameplayTag(FName("Abilities.Attack"),FString("攻擊技能標簽"));
接下來,我們要實現在角色數據表里面配置它的技能,打開Character Class Info.h增加對應的屬性設置
//對應每個職業的屬性和技能
USTRUCT()
struct FCharacterClassDefaultInfo
{GENERATED_BODY()UPROPERTY(EditDefaultsOnly, Category="Class Defaults")TSubclassOf<UGameplayEffect> PrimaryAttributes;UPROPERTY(EditDefaultsOnly, Category="Class Defaults")TArray<TSubclassOf<UGameplayAbility>> StartupAbilities;
};
后面在UE的藍圖里面設置以后,我們可以在結構體內獲取需要應用的技能。接下來,我們還需要回憶一下如何應用敵人的技能的,我們是在敵人的基類里面,去實現的技能的應用,
我們要擴展這個函數,增加傳入角色的類型,通過類型去獲取特定職業的技能。
//初始化角色的技能UFUNCTION(BlueprintCallable, Category="MyAbilitySystemLibrary|CharacterClassDefaults")static void GiveStartupAbilities(const UObject* WorldContextObject, UAbilitySystemComponent* ASC, ECharacterClass CharacterClass);
擴展函數,從數據結構中獲取到數據并應用到角色的ASC上
void URPGAbilitySystemBlueprintLibrary::GiveStartupAbilities(const UObject* WorldContextObject, UAbilitySystemComponent* ASC, ECharacterClass CharacterClass)
{//從實例獲取到關卡角色的配置UCharacterClassInfo* CharacterClassInfo = GetCharacterClassInfo(WorldContextObject);if(CharacterClassInfo == nullptr) return;//從戰斗接口獲取到角色的等級ICombatInterface* CombatInterface = Cast<ICombatInterface>(ASC->GetAvatarActor());int32 CharacterLevel = 1;if(CombatInterface){CharacterLevel = CombatInterface->GetPlayerLevel();}//應用角色擁有的技能數組for(const TSubclassOf<UGameplayAbility> AbilityClass : CharacterClassInfo->CommonAbilities){FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(AbilityClass, CharacterLevel); //創建技能實例ASC->GiveAbility(AbilitySpec); //只應用不激活}//獲取到默認的基礎角色數據const FCharacterClassDefaultInfo ClassDefaultInfo = CharacterClassInfo->GetClassDefaultInfo(CharacterClass);//應用職業技能數組for(const TSubclassOf<UGameplayAbility> AbilityClass : ClassDefaultInfo.StartupAbilities){FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(AbilityClass, CharacterLevel); //創建技能實例ASC->GiveAbility(AbilitySpec); //只應用不激活}
}
敵人的角色類型也是設置在敵人基類上的,所以我們可以直接調用時傳入
//初始化角色的技能if(HasAuthority()){URPGAbilitySystemBlueprintLibrary::GiveStartupAbilities(this, AbilitySystemComponent, CharacterClass);}
在UE中設置
接著我們可以編譯代碼打開UE,在UE里面創建一個對應的藍圖類
命名為GA_GoblinSpearAttack
首先設置技能標簽,我們就可以通過標簽激活技能。
在技能里實現測試代碼,在觸發技能時,繪制一個綠色的球
接著打開我們的數據表,現在每個職業多了一個需要設置的技能數組,將我們創建的技能添加給戰士
攻擊是由AI觸發的,所以,我們在AI的攻擊任務中,通過標簽激活技能即可。我們從控制的pawn中獲取到ASC,然后通過標簽激活技能
創建的變量我們可以將其設置外部可以設置
然后再行為樹中設置激活的技能標簽
接著運行測試效果,如果允許哥布林在攻擊時繪制綠色的球,證明我們技能激活成功。
設置技能攻擊蒙太奇
回憶一下之前我們實現的火球術,需要使用一個蒙太奇動畫,然后在蒙太奇動畫中觸發事件發射火球實現技能的釋放。接下來,我們使用同樣的思路實現近戰攻擊的技能。
找到敵人的攻擊動畫,創建一個蒙太奇
創建名稱AM_Attack_GoblinSpear,方便在蒙太奇下拉中選擇
在蒙太奇中,我們需要增加MotionWarping來控制朝向,注意,動畫必須要支持根動畫
設置角色在蓄力的時候轉向
設置觸發名稱,關閉移動,只留下轉向,切換轉向類型為朝向目標
我們有了蒙太奇,還需要實現在攻擊時朝向攻擊目標,按照之前的實現方式,我們通過MotionWarpping插件實現轉向。
添加轉向插件
我們需要在敵人基類身上增加MotionWarpping插件
實現調用,和主角的一樣即可,其實這里我們可以實現一個角色基類藍圖,在里面設置一些通用的函數,這里先不改。
要實現這個功能,我們需要讓敵人知道攻擊目標的位置,然后攻擊時朝向它,我們需要在AI控制器中獲取到目標。
我們在敵人接口里增加兩個函數,用于設置和獲取攻擊目標,這里設置了BlueprintNativeEvent,它默認給我們實現,并且可以在藍圖中實現此函數,如果在藍圖中實現了此函數,在藍圖中調用,將使用藍圖的版本。
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)void SetCombatTarget(AActor* InCombatTarget); //設置敵人的攻擊目標UFUNCTION(BlueprintCallable, BlueprintNativeEvent)AActor* GetCombatTarget() const; //獲取敵人的攻擊目標
接下來我們在敵人基類里面覆寫這兩個函數,并去實現,這樣寫法是因為我們是通過UE讓藍圖去實現的此函數,所以我們需要加上Vitrual和后面的_Implementation
/* IEnemyInterface敵人接口 */virtual void HighlightActor() override; //高亮virtual void UnHighlightActor() override; //取消高亮virtual AActor* GetCombatTarget_Implementation() const override;virtual void SetCombatTarget_Implementation(AActor* InCombatTarget) override;/* IEnemyInterface敵人接口 結束 */
在cpp中實現這兩個函數,我們也可以在藍圖中實現,這里看個人喜好
AActor* ARPGEnemy::GetCombatTarget_Implementation() const
{return CombatTarget;
}void ARPGEnemy::SetCombatTarget_Implementation(AActor* InCombatTarget)
{CombatTarget = InCombatTarget;
}
有了設置攻擊目標的函數,我們在AI攻擊任務里面就可以設置攻擊對象了。
在任務里增加一個黑板鍵選擇器,用于在AI行為樹中設置綁定的黑板鍵
在AI行為樹中設置攻擊目標為跟隨目標黑板鍵
這樣我們就可以在任務里面獲取攻擊目標,并設置給控制的Pawn身上
接下來,我們在技能里面,從角色身上獲取到攻擊目標,然后轉換為戰斗接口,調用偏轉朝向的函數,將朝向位置設置,然后再調用蒙太奇播放動畫
接著允許查看效果,是否真的能捅向目標
添加攻擊事件
接下來,我們需要在蒙太奇動畫里,攻擊的那一刻,觸發一個事件,來告訴技能當前可以計算攻擊。就像之前實現火球術時的一樣。
由于我們沒有設置對應的通知,我們去標簽管理器增加一個近戰攻擊的通知標簽。由于這個標簽不需要在c++內使用,所以我們可以直接通過UE添加。
設置屬于攻擊的近戰攻擊標簽
設置完成,打開蒙太奇,設置到通知上面,這樣就可以在技能里面去接收對應的標簽通知了。
然后我們在技能中設置等待游戲事件即可,Only Match Exact是標簽必須全部匹配才可以觸發事件
現在我們接收到事件了,還需要考慮如何觸發攻擊。在之前實現火球從法杖杖尖處釋放時,我們在戰斗接口設置了一個獲取技能釋放位置的函數,所以我們可以通過獲取到設置的位置,生成一個碰撞球體,判斷是否和目標產生重疊實現攻擊并應用GE。
但是當前函數無法在藍圖中調用,所以,我們需要修改它,將其變成可以在藍圖中調用的版本
//獲取技能釋放位置,通過在藍圖中設置獲取WeaponTipSocketName的位置UFUNCTION(BlueprintNativeEvent, BlueprintCallable)FVector GetCombatSocketLocation() const;
并將cpp的實現刪除。
在角色基類修改它的繼承,修改成GetCombatSocketLocation_Implementation
virtual FVector GetCombatSocketLocation_Implementation() const override;
并將實現代碼函數名稱也修改掉
FVector ARPGCharacter::GetCombatSocketLocation_Implementation() const
{return Weapon->GetSocketLocation(WeaponTipSocketName);
}
完成以后,就可以編譯打開UE,并設置它的WeaponTipSocketName
這里名稱,我們可以在武器骨骼上面增加插槽實現
接著在技能藍圖里,我們首先實現接收到事件后繪制一個調試球體,來表現攻擊范圍
運行查看釋放能夠正確繪制出調試球體
我們在點擊實現角色攻擊時,發現一個問題,有個地方報錯了,提示我們不要在接口中直接調用Event函數。而是調用Execute_GetCombatSocketLocation。
往前定位發現是在調用,直接調用獲取位置,由于前面我們將其修改成了藍圖編譯生成函數,所以在C++里面不能夠直接調用
所以我們修改此行代碼為使用Execute_GetCombatSocketLocation調用
const FVector SocketLocation = ICombatInterface::Execute_GetCombatSocketLocation(GetAvatarActorFromActorInfo());
接著運行測試就行了。