#はじめに!
UE4の標準的なネットワークゲームではクライアント上ではGameModeはインスタンス化されず、アクセスできません!
※なぜクライアント上で作られないかは、クライアント上で作られるかどうかを示す NetLoadOnClient フラグが落ちているからです。
そのため、例えば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関数を読んでみます。
##BP_GameMode
1.c++で作ったGameModeを継承して、ブループリントを生成
2.ClassDefaults中のGameRuleパラメータをいじる
3.WorldSettingに作ったGameModeを設定する
#いざ実食
PIEでNumberOfPlayersを4に設定して・・・Play!
全てのサーバー/クライアント上でBP_GameModeに設定されているパラメータを取得できています!!!
加えて、アクセスしているGameModeは Default__BPGameMode_C という名前を持っていて、ClassDefaultObjectであることがわかります。
上手く取り出すことができました!
#ClassDefaultObjectについて補足
ClassDefaultObject(CDO)はアクターやUObjectのデフォルトの状態をメモリ上に再現したものです。
例えばBlueprintがuassetファイルから読みだされたとき、そのファイルに記載された情報からメモリ上に原本となる状態をメモリ上に構築します。
NewObjectやSpawnActorによってそれらがワールドに生まれる時、このCDOを 複製して ワールドに出現します。
次はその図解です。
CDOは誰からも不要になってメモリから削除されるまで常にメモリ上にあるため、
これにアクセスしてデフォルト値を取り出すことができます。
例としてキャラクター毎に固有のMagicPointパラメータをクラスのパラメータに持っておいて、エナジードリンクを飲んだらMagicPointが全快するという仕様があるとすると以下のように実装可能です。
どのプロパティを出力するかはノードの詳細パネルから変更可能です。
BeginPlayで Max Magic Point
プロパティに保存とかしなくても大丈夫ですよ!
#まとめ
GameModeはクライアント上で見えないように見えますが、ClassDefaultObjectだけはWorldSettingからアクセス可能です!
ClassDefaultObjectは一見、普通に生成されたActorと同じようにアクセス可能な型にみえますが、これは静的なデータで、
Worldにアクターとしてインスタンス化されているわけではありません。
「ショーケースに入った見本のようなもの」をイメージしてください。
くれぐれもCDOを直接書き換えたりしないようにご注意ください!