[UE5]GameplayAbilitySystemメモ3 -キャラクターへの実装-
前回の記事
更新情報
更新日 | 内容 |
---|
前置き
読み飽きた方は飛ばしてください
この記事ではC++を使用します。
解説を挟みますが、ある程度わかる人向けです
また、現在個人製作中のゲーム"KillGurality"のプロジェクトを用いて記事を書いているので、プロジェクト名を使用するプレフィックスなどには"KG"を使用しています。
ソースコードをコピペする場合は、環境に合わせて置き換えてください。
環境
バージョン:UE5.2
エディタ:VSCode
C++プロジェクトの作り方や勉強は、こちらの記事を参考にしてください
とてもわかりやすく、無料パートだけでも参考になるかと思います。おすすめです
VSCodeを使っている方は、この設定をしておくと便利です。
一旦前回までに作成したコンポーネントたちをキャラクタークラスに移植します。
このプロジェクトでは、CharacterBaseからPlayer,Enemyを継承し、PlayerはPlayerStateがGameplayAbilityComponentを所有している構図となります。詳しくは第1回の記事を参照してください。
CharacterBase
PlayerStateで書いたAbilitySystemInterface周りの処理をCharacterBaseにも移植します。
ソースコードは以下の通りです。
CharacterBase.h
#pragma once
#include "CoreMinimal.h"
+ #include "AbilitySystemInterface.h"
#include "GameFramework/Character.h"
#include "KGCharacterBase.generated.h"
+ class UAbilitySystemComponent;
+ class UAttributeSet;
UCLASS()
+ class KILLGURALITY_API AKGCharacterBase : public ACharacter, public IAbilitySystemInterface
- class KILLGURALITY_API AKGCharacterBase : public ACharacter
{
GENERATED_BODY()
public:
AKGCharacterBase();
+ virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
+ UAttributeSet* GetAttributeSet() const { return AttributeSet; }
protected:
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere, Category = "Combat");
TObjectPtr<USkeletalMeshComponent> Weapon;
+ UPROPERTY()
+ TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
+
+ UPROPERTY()
+ TObjectPtr<UAttributeSet> AttributeSet;
};
Character.cpp
#include "Character/KGCharacterBase.h"
AKGCharacterBase::AKGCharacterBase()
{
PrimaryActorTick.bCanEverTick = false;
Weapon = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Weapon"));
Weapon->SetupAttachment(GetMesh(), FName("WeaponSocket"));
Weapon->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
+UAbilitySystemComponent* AKGCharacterBase::GetAbilitySystemComponent() const
+{
+ return AbilitySystemComponent;
+}
void AKGCharacterBase::BeginPlay()
{
Super::BeginPlay();
}
Enemy
EnemyクラスはPlayerStateを持たないので(前回の記事参考)、ここでAbilitySystemComponentとAttrubuteSetの設定をします。
Enemy.h
#pragma once
#include "CoreMinimal.h"
#include "Character/KGCharacterBase.h"
#include "KGEnemy.generated.h"
UCLASS()
class KILLGURALITY_API AKGEnemy : public AKGCharacterBase
{
GENERATED_BODY()
public:
AKGEnemy();
};
Enemy.cpp
#include "Character/KGEnemy.h"
#include "AbilitySystem/KGAttributeSet.h"
#include "AbilitySystem/KGAbilitySystemComponent.h"
AKGEnemy::AKGEnemy()
{
AbilitySystemComponent = CreateDefaultSubobject<UKGAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
AttributeSet = CreateDefaultSubobject<UKGAttributeSet>(TEXT("AttributeSet"));
}
前の記事で作ったAttributeに以下の文を定義し、マクロを設定します。
ATTRIBUTE_ACCESSORS(ClassName, PropertyName);
全体のコードとしては以下になります。
AttributeSet.h
#pragma once
#include "CoreMinimal.h"
#include "AttributeSet.h"
+#include "AbilitySystemComponent.h"
#include "KGAttributeSet.generated.h"
+#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
+ GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
+ GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
+ GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
+ GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
UCLASS()
class KILLGURALITY_API UKGAttributeSet : public UAttributeSet
{
GENERATED_BODY()
public:
UKGAttributeSet();
UPROPERTY(BlueprintReadOnly, Category = "Attribute")
FGameplayAttributeData Health;
+ ATTRIBUTE_ACCESSORS(UKGAttributeSet, Health)
UPROPERTY(BlueprintReadOnly, Category = "Attribute")
FGameplayAttributeData MaxHealth;
+ ATTRIBUTE_ACCESSORS(UKGAttributeSet, MaxHealth)
};
Attribute.cpp
#include "AbilitySystem/KGAttributeSet.h"
UKGAttributeSet::UKGAttributeSet()
{
+ InitHealth(100.0f);
+ InitMaxHealth(100.0f);
}
Health, MaxHealthを100で初期化します。
GameplayAbilityComponentの初期化
GameplayAbilityComponentは、OwnerActorとAvatarActorを使用します。
簡単に言うと、誰が(OwnerActor)何に向けて(AvatarActor)どうするか(Ability)というところです。
ここでは、OwnerActorはAbilitySystemComponentを所有するクラスで、AvatarActorはAbilityを発動するクラスとなります。
ソースコードは以下の通りです。
Enamy.h
protected:
virtual void BeginPlay() override;
Enamy.cpp
void AAuraEnemy::BeginPlay()
{
Super::BeginPlay();
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}
Enemyクラスは、OwnerActorでもありAvatarActorでもあるとして初期化します
Player.h
public:
AKGPlayer();
virtual void PossessedBy(AController* NewController) override;
private:
void InitAbilityActorInfo();
前回忘れていたコンストラクタをしれっと追記しています。
ここでの初期化処理はマルチプレイなどに変更する場合多用しそうなので、InitAbilityActorInfoとして関数を作成しています。
Player.cpp
AKGPlayer::AKGPlayer()
{
}
void AKGPlayer::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
InitAbilityActorInfo();
}
void AKGPlayer::InitAbilityActorInfo()
{
AKGPlayerState* KGPlayerState = GetPlayerState<AKGPlayerState>();
check(KGPlayerState);
KGPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(KGPlayerState, this);
AbilitySystemComponent = KGPlayerState->GetAbilitySystemComponent();
AttributeSet = KGPlayerState->GetAttributeSet();
}
しれっと追記したコンストラクタですが、まだ何も書きません。期待させてしまってすみません
InitAbilityActorInfoでは、PlayerStateがOwnerActor、PlayerクラスがAvatarActorとなるように初期化しています。その前に、PlayerStateが取得できない場合はそのままゲームの進行をさせたくないので、ifではなくcheckでインスタンスがあるか確認します。
AbilitySystemComponentとAttributeSetを、PlayerStateから取得します。
ここまでで一度コンパイルしてみてください。
エラーが出たら適宜修正してください
エラー対応
余談ですが、InitAbilityActorInfoのCheckでエラーが出る場合は、GameModeのPlayerStateの設定を見直してみてください。自作したPlayerStateクラスに変更するとエラーは解消できるのではないでしょうか?
私は気づかず地味に時間を持ってかれました。悔しいので供養しておきます
デバッグで確認してみよう
エディタ画面に戻り、実行してみます。
そして、@
キーを押すとコマンドが撃てるので、showdebug abilitysystem
を入力します。
すると、このように表示されます。
PlayerがHealthとMaxHealthを持ち、100で初期化されていることが確認できました。
おわり
やっと最低限の準備編が終わりました!
次回は回復アイテムを使用し、Healthを外部のActorから変更するような記事を書きます
今まで影を潜めていたGameplayEffectの出番です。
週1ペースで書けたらいいなと思っております
よろしくお願いします
Discussion