📝

[UE5]GameplayAbilitySystemメモ3 -キャラクターへの実装-

2024/06/01に公開

前回の記事

https://zenn.dev/sayaivy/articles/gameplayability_2

更新情報

更新日 内容

前置き

読み飽きた方は飛ばしてください

この記事ではC++を使用します。
解説を挟みますが、ある程度わかる人向けです

また、現在個人製作中のゲーム"KillGurality"のプロジェクトを用いて記事を書いているので、プロジェクト名を使用するプレフィックスなどには"KG"を使用しています。
ソースコードをコピペする場合は、環境に合わせて置き換えてください。

環境
バージョン:UE5.2
エディタ:VSCode


C++プロジェクトの作り方や勉強は、こちらの記事を参考にしてください
とてもわかりやすく、無料パートだけでも参考になるかと思います。おすすめです
https://zenn.dev/posita33/books/ue5_starter_cpp_and_bp_001/viewer/chap_00_about

VSCodeを使っている方は、この設定をしておくと便利です。
https://dev.epicgames.com/documentation/ja-jp/unreal-engine/setting-up-visual-studio-code-for-unreal-engine#vsコード向けのintellisenseを設定する


一旦前回までに作成したコンポーネントたちをキャラクタークラスに移植します。
このプロジェクトでは、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を発動するクラスとなります。

alt text

ソースコードは以下の通りです。

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クラスに変更するとエラーは解消できるのではないでしょうか?
私は気づかず地味に時間を持ってかれました。悔しいので供養しておきます
alt text

デバッグで確認してみよう

エディタ画面に戻り、実行してみます。
そして、@ キーを押すとコマンドが撃てるので、showdebug abilitysystemを入力します。

alt text

すると、このように表示されます。
alt text

PlayerがHealthとMaxHealthを持ち、100で初期化されていることが確認できました。

おわり

やっと最低限の準備編が終わりました!
次回は回復アイテムを使用し、Healthを外部のActorから変更するような記事を書きます
今まで影を潜めていたGameplayEffectの出番です。

週1ペースで書けたらいいなと思っております
よろしくお願いします

GitHubで編集を提案

Discussion