LoginSignup
10
6

More than 3 years have passed since last update.

[UE4]マルチプレイクライアントからGameModeにアクセスする(&CDOを簡単に紹介)

Last updated at Posted at 2019-12-21

はじめに!

UE4の標準的なネットワークゲームではクライアント上ではGameModeはインスタンス化されず、アクセスできません!

※なぜクライアント上で作られないかは、クライアント上で作られるかどうかを示す NetLoadOnClient フラグが落ちているからです。
image.png

そのため、例えばGameModeにフラグなどを設定してあった場合、
GameStateなどにコピーしてレプリケート・・・なんてことをしてみたりすることもあると思います。

実はこれはちょっともったいないです。なぜなら クライアントからGameModeにアクセスする事 は可能だからです。
(ただし完全なサーバー上にあるGameModeが通信されて複製されているものではありません→後述)

まずソース

ゲームモードのヘッダファイル

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "TPSrc424GameModeGameMode.generated.h"

UENUM(BlueprintType)
enum class EGameRuleType : uint8
{
    Default,
    CaptureTheFlag,
    TeamDeathmatch,
    PvE
};

USTRUCT(BlueprintType)
struct FGameRule
{
    GENERATED_USTRUCT_BODY();

    UPROPERTY( EditDefaultsOnly, BlueprintReadOnly, Category="GameRule")
    EGameRuleType   RuleType;

    UPROPERTY( EditDefaultsOnly, BlueprintReadOnly, Category="GameRule")
    bool    bAllowOpeningStorage;

    UPROPERTY( EditDefaultsOnly, BlueprintReadOnly, Category="GameRule")
    bool    bDisableToObtainExp;
};


UCLASS(minimalapi)
class ATPSrc424GameModeGameMode : public AGameModeBase
{
    GENERATED_BODY()

public:
    ATPSrc424GameModeGameMode();

    static const ATPSrc424GameModeGameMode* GetGameModeCDO( const UObject* WorldContextObject );

    UFUNCTION( BlueprintCallable, Category="GameRule", meta = (WorldContext = "WorldContextObject"))
    static const FGameRule& GetGameRule( const UObject* WorldContextObject );
private:
    UPROPERTY( EditDefaultsOnly, Category="GameRule")
    FGameRule   Rule;
};

ゲームモードのソースファイル

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

#include "TPSrc424GameModeGameMode.h"
#include "TPSrc424GameModeCharacter.h"
#include "UObject/ConstructorHelpers.h"

DEFINE_LOG_CATEGORY_STATIC(LogMyGameMode, Log, All);

ATPSrc424GameModeGameMode::ATPSrc424GameModeGameMode()
{
    // set default pawn class to our Blueprinted character
    static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPersonCPP/Blueprints/ThirdPersonCharacter"));
    if (PlayerPawnBPClass.Class != NULL)
    {
        DefaultPawnClass = PlayerPawnBPClass.Class;
    }
}

const ATPSrc424GameModeGameMode* ATPSrc424GameModeGameMode::GetGameModeCDO( const UObject* WorldContextObject )
{
    const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::Assert );
    const AWorldSettings* WorldSetting = World->GetWorldSettings(true, true);
    const AGameModeBase* GameModeBase = WorldSetting->DefaultGameMode.GetDefaultObject();
    const ATPSrc424GameModeGameMode* MyGameMode = Cast<const ATPSrc424GameModeGameMode>( GameModeBase );
    if (MyGameMode)
    {
        UE_LOG(LogMyGameMode, Log, TEXT("Name:%s"), *MyGameMode->GetName() );
    }
    return MyGameMode;
}
const FGameRule& ATPSrc424GameModeGameMode::GetGameRule( const UObject* WorldContextObject )
{
    const ATPSrc424GameModeGameMode* GameMode = GetGameModeCDO( WorldContextObject );
    if( GameMode == nullptr )
    {
        static FGameRule Dummy;
        return Dummy;
    }
    return GameMode->Rule;
}

ここまで。

重要なのはGetGameModeCDOです。
名前が示す通り、この関数でワールドに設定されたWorldSettingを経由して、GameModeクラスのClassDefaultObjectを獲得しています。
CDOはアセットから取り出した素の状態を表していて、 サーバー上にあるGameModeが通信されて複製されたものではありません。

ClassDefaultObject(CDO)とは↓
ClassDefaultObject@docs.unrealengine.com
この記事の後ろで少しCDOについて補足しています。

使ってみよう!

ThirdPersonCharacterのBlueprint

こんな感じで、おもむろにGetGameRule関数を読んでみます。

image.png

BP_GameMode

1.c++で作ったGameModeを継承して、ブループリントを生成
2.ClassDefaults中のGameRuleパラメータをいじる
3.WorldSettingに作ったGameModeを設定する
image.png

いざ実食

PIEでNumberOfPlayersを4に設定して・・・Play!
全てのサーバー/クライアント上でBP_GameModeに設定されているパラメータを取得できています!!!
加えて、アクセスしているGameModeは Default__BPGameMode_C という名前を持っていて、ClassDefaultObjectであることがわかります。
image.png

上手く取り出すことができました!

ClassDefaultObjectについて補足

ClassDefaultObject(CDO)はアクターやUObjectのデフォルトの状態をメモリ上に再現したものです。
例えばBlueprintがuassetファイルから読みだされたとき、そのファイルに記載された情報からメモリ上に原本となる状態をメモリ上に構築します。
NewObjectやSpawnActorによってそれらがワールドに生まれる時、このCDOを 複製して ワールドに出現します。
次はその図解です。

image.png

CDOは誰からも不要になってメモリから削除されるまで常にメモリ上にあるため、
これにアクセスしてデフォルト値を取り出すことができます。
例としてキャラクター毎に固有のMagicPointパラメータをクラスのパラメータに持っておいて、エナジードリンクを飲んだらMagicPointが全快するという仕様があるとすると以下のように実装可能です。

image.png

どのプロパティを出力するかはノードの詳細パネルから変更可能です。

image.png

BeginPlayで Max Magic Point プロパティに保存とかしなくても大丈夫ですよ!

まとめ

GameModeはクライアント上で見えないように見えますが、ClassDefaultObjectだけはWorldSettingからアクセス可能です!
ClassDefaultObjectは一見、普通に生成されたActorと同じようにアクセス可能な型にみえますが、これは静的なデータで、
Worldにアクターとしてインスタンス化されているわけではありません。
「ショーケースに入った見本のようなもの」をイメージしてください。
くれぐれもCDOを直接書き換えたりしないようにご注意ください!

10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6