[UE4/UE5] GASでオンラインゲーム Part3 Attribute の Replicateについて

Post 2022年2月25日金曜日

C++ GameplayAbility GASでオンラインシリーズ UE4 UE5 Unreal Engine オンラインゲーム

Part 1の一部になります。

前回の続き

今回はGameplay Ability System (通称GAS)を使って雪玉とHPをAttributeで実装し、Gameplay Effectで影響を与えていきます。

お約束
この記事作成にあたって使用した主なUnreal Engine バージョンUE4.27.2 / UE5 Preview1

本日のゴール1

雪玉とHPをAttributeで実装。雪玉を投げた時に、雪玉を消費するようにしたい。


本日のゴール2

雪玉が当たった相手のHPを減らしたい。


参考文献

こちらの記事を参照しました。

https://github.com/sentyaanko/GASDocumentation/blob/lang-ja/README.jp.md#443-attributes-%E3%81%AE%E5%AE%9A%E7%BE%A9


実際の実装

まず前提として、おかわりさんのページの内容を実装していただくことが前提です。

こちらのおかわりさんのページを基本で組んでいただきます。

https://okawari-hakumai.hatenablog.com/entry/2018/08/03/082935


Attributeのhファイルの方に追加していきます。

初期設定用のコンストラクタの方に下記を追加。AttributeをReplicateするぞっっていう宣言みたいなもんです。

virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;


その下にこれを記述していきます。太字部分はご自分のUAttributeSet名称に変えていただきます。『名称』はご自分のAttributeSetの名称が入ります。

この赤字の部分ですね。(Uは含まない。)

『U名称』と書いてあったら、U〜〜〜になるわけです。※まあ正直Categoryの方はご自分のわかりやすいようにすればいいんですが…。

    // Yukidama

    UPROPERTY(BlueprintReadWrite, Category = "名称", ReplicatedUsing = OnRep_Yukidama)

        FGameplayAttributeData Yukidama;

    ATTRIBUTE_ACCESSORS(U名称, Yukidama);

    FGameplayAttribute YukidamaAttribute();

ついでにHealthも書き換えちゃいましょう。

    // Health

    UPROPERTY(BlueprintReadWrite, Category = "名称", ReplicatedUsing = OnRep_Health)

        FGameplayAttributeData Health;

    ATTRIBUTE_ACCESSORS(U名称, Health);

    FGameplayAttribute HealthAttribute();

ポイントはRelicatedUsing = OnRep_XXXですね。これで値が変更されたときにOnRep_XXXを呼び出します。続いて最後の};の前にこれを記述していきます。

protected:

    

    UFUNCTION()

    virtual void OnRep_Yukidama(const FGameplayAttributeData& OldYukidama);



    
UFUNCTION()

    virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);

上で記述したOnRep_XXXを記述したというわけです。


さて今度はAttributeのcppファイル、中身の方です。

U名称::U名称()

: Health(100.f)

, MaxSpeed(600.f)

, Damage(0.f)

        , Yukidama(5.f)

{

}

こんな感じで最初の方にYukidamaの初期値を指定しておきます。5個ですね。なんでintじゃなくてfloatなんだよと言われるかもしれませんが、基本的にAttributeではfloatで変数を扱います。

あとはおかわりさんの記事を元に、これをHealthの下とかに入れていきます。

FGameplayAttribute U名称::YukidamaAttribute()

{

    static FProperty* Property = FindFieldChecked<FProperty>(U名称::StaticClass(), GET_MEMBER_NAME_CHECKED(U名称, Yukidama));

    return FGameplayAttribute(Property);

}


FGameplayAttribute U名称::HealthAttribute()

{

    static FProperty* Property = FindFieldChecked<FProperty>(U名称::StaticClass(), GET_MEMBER_NAME_CHECKED(U名称, Health));

    return FGameplayAttribute(Property);

}

あとはcppファイルの最後に下記を追加すればOK。

void U名称::OnRep_Yukidama(const FGameplayAttributeData& OldYukidama)

{

    GAMEPLAYATTRIBUTE_REPNOTIFY(U名称, Yukidama, OldYukidama);

}


void U名称::OnRep_Heatlh(const FGameplayAttributeData& OldHealth)

{

    GAMEPLAYATTRIBUTE_REPNOTIFY(U名称, Health, OldHealth);

}



void U名称::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const

{

    Super::GetLifetimeReplicatedProps(OutLifetimeProps);


    DOREPLIFETIME_CONDITION_NOTIFY(U名称, Yukidama, COND_None, REPNOTIFY_Always);


    
DOREPLIFETIME_CONDITION_NOTIFY(U名称, Health, COND_None, REPNOTIFY_Always);

}

上では、OnRepつまりAttributeが書き換えられたときに値を上書きする処理だけです。

下のほうがReplicateの処理になります。どういう時にRepnotifyのイベントを出すかを設定するみたいですね。(調べてない笑)


次に実行ごとに雪玉が一つ減るというような単純なGameplay Effect(GE)を作ります。

GEファイルを作ります。GE_YukidamaCostとでもしましょう。

まずModifiersを「+」を押して追加して、内容を表示して下記のように設定していきます。

GE_YukidamaCostのプロパティ

これでOKです。単純に実行するごとに、YukidamaというAttributeから1、減らすという単純なものです。

作ったら、GA_ThrowにCostとして設定します。
GA_Throwにさっき作ったGEを設定

これでGA_Throwが実行されるごとに、GE_YukidamaCostが実行されます。
※正確に言うと、実行された瞬間に消費されるわけではなく、「Commit Ability」あるいは「Commit Cost」ノードが実行された瞬間にCostが消費されるので、このノードを後ろに配置することでコストの消費タイミングを操作することができます。便利やなぁ〜。
実際にこんな感じでどういうふうに変化するかを見ていくと、5からだんだん減っていってるのがわかります。

0になると、そもそもGA_Throwが実行されません。
もし実行されちゃうよ〜という人は、Commit Abilityのノードの場所を変えてみてください。それか変えたくないよーって人は、Check Ability Costのノードを最初の方に記載すれば大丈夫です。


ちなみに、同じ手順で、GA_Generateも作れます。GE_YukidamaGenerateとかを作って、先程GE_YukidamaCostで-1だったAddを1にして他の設定は同様にします。

あとはGA_Generateを下記のように作ります。


ゴール2:雪玉が当たった時に相手のHPを減らしたい

さてこっちは雪玉のActorのBPを弄ります。

シンプルに書けますね。雪玉側にはCastを使わずにかけるのがGASの非常にいいところです。

※注意点※
上記ノード画像に映し忘れましたが、AutonomousProxyの権限で、つまりClient側で、他のClientまたはServer側のGEを変更することはできません。
このため、GEの変更はかならずServerで実施する必要があります。Switch Has AuthorityでServer側から実行するか、Client側でRPCを使って実行する必要があります。


実際にアクションRPGを作ろうとか考えている人の場合は、ダメージ計算は1減らすではだめなので、ここのGEはかなり複雑になると思います。
相手の防御力は?シールドは使っている?クリティカルヒット?などの色々な情報を元にダメージ計算する形です。

ここはかなり難しい(らしい)のですが、私はやったことがありません。


おかずさんなど色々な方が色々な知見を持っているみたいなので、いずれはやりたいですね。

おわり