ue-gameplay-framework について
UE 5.8 プロジェクト向けの開発スキルです。Gameplay Framework のクラスと検証済み API の使い方を対応付け、サーバールール、複製状態、プレイヤー制御、スポーン、トラベルを扱います。
主な機能
- 01Gameplay Framework のクラス責務マップ
- 02プレイヤー参加と Pawn 生成の流れ
- 03マッチ状態と複製状態の管理
- 04PlayerController、PlayerState、HUD の利用法
- 05セッションとレベルトラベルのリファレンス
活用シーン
- 01ゲームデータを保持するクラスの選定
- 02プレイヤー参加、生成、リスポーンの実装
- 03クライアント向け複製マッチ状態の設計
- 04コントローラー入力とレベル遷移の処理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ quodsoler/ unreal-engine-skills --skill ue-gameplay-frameworkインストール後、Claude Code で/ue-gameplay-frameworkを入力して読み込まれたか確認します。
同梱ファイルreferences/framework-class-map.md
詳細
UE ゲームプレイフレームワーク
対象エンジン:UE 5.8。以下の API は 5.8 のヘッダーで検証済みです。古い形式は「非推奨 — 使用しないでください」に記載されています。
ゲームプレイフレームワークは、エンジンが自動的にスポーンするクラス群です。AGameModeBase、AGameStateBase、APlayerController、APlayerState、APawn、ACharacter、AHUD、UGameInstanceに加え、それらを連携させる参加、スポーン、マッチステート、トラベル、ダメージの各フローで構成されます。これらはすべてEngineモジュール(Classes/GameFramework、Classes/Engine、Classes/Kismet)に含まれているため、Build.cs には"Core"、"CoreUObject"、"Engine"が必要です。入力をバインドする場合は"EnhancedInput"を追加し、セッションを扱う場合に限り"OnlineSubsystem"と"OnlineSubsystemUtils"(または"OnlineServicesInterface")を追加してください。
コンテキスト
.agents/ue-project-context.mdが存在する場合は読み込んでください(モジュール名、規約、有効なプラグイン、GAS/ネットワーク設定)。ファイルがなくても作業を続けてください。
リクエストとコードベースから対象領域を特定してください。異なるコードにつながる妥当な解釈が二つある場合に限り、質問してください。
| リクエストの内容… | 参照先 |
|---|---|
| 各データを保持するクラス、サーバーとクライアントでの存在状況 | クラス責務マップ |
| 参加フロー、プレイヤースタート、ポーンのスポーン、マッチステート | GameMode |
| レプリケートされるスコア、タイマー、接続中プレイヤー一覧 | GameState |
| プレイヤーごとの入力、RPC ブリッジ、所有権取得、入力モード | PlayerController |
| プレイヤーごとにレプリケートされるステータス、チーム、名前 | PlayerState |
所有可能な本体、PossessedBy、キャラクターのセットアップ | Pawn と Character |
Canvas オーバーレイ、ヒットボックス、showdebug | HUD |
| Gameplay Abilities を使わないヘルスとダメージ | GAS を使わないダメージ |
| レベル読み込みをまたいで維持されるデータ、サブシステム、アクセサー | GameInstance |
| オンラインセッションのホスティング、検索、参加 | セッション |
ServerTravel、ClientTravel、シームレストラベル | トラベル |
存在状況/権限マトリックス、これらのスニペットの元となる完全なクラス宣言、ライフサイクルの順序、継承プロパティの表については、references/framework-class-map.mdを参照してください。
クラス責務マップ
| クラス | 存在場所 | 所有するもの | ここに配置しないもの |
|---|---|---|---|
AGameModeBase / AGameMode | サーバーとスタンドアロンのみ。クライアントではnullptr | ルール、参加の承認、スポーン先の選択、マッチステート | クライアントが読み取る必要のあるもの |
AGameStateBase / AGameState | 全環境。レプリケートされる | グローバルなレプリケート状態、PlayerArray、サーバークロック | サーバー専用の判断 |
APlayerController | サーバーはすべてのコントローラーを保持。各クライアントは自身のもののみ保持 | 入力、RPC ブリッジ、所有権取得、入力モード、ビューターゲット | 他のクライアントが必要とするデータ |
APlayerState | 全環境(bAlwaysRelevant = true) | プレイヤーごとにレプリケートされるステータス、チーム、名前、Ping | 入力処理、UI 状態 |
APawn / ACharacter | サーバーとすべてのクライアント | 本体:コリジョン、メッシュ、移動 | ルール、スコア付け |
AHUD | ローカルプレイヤーのみ。専用サーバーには存在しない | Canvas オーバーレイ、ヒットボックス、デバッグ表示 | レプリケートされるデータ |
UGameInstance | プロセスごとに一つ。すべてのレベル読み込みをまたいで存続 | セッションハンドル、レベルをまたぐ状態、サブシステム | マッチごとの状態 |
APlayerStateはコンストラクターでbReplicates = trueとbAlwaysRelevant = trueを設定します。そのため、スコアボードはどの距離でも機能します。
GameMode(サーバー専用)
AGameModeBaseは最小限のベースクラスです。AGameModeはマッチステートマシンを追加します。MatchState::EnteringMap → WaitingToStart → InProgress → WaitingPostMatch → LeavingMap(失敗時はAbortedも含む)が必要な場合に使用してください。
参加シーケンス(サーバーのみ)
| 順序 | オーバーライドするシグネチャ | 備考 |
|---|---|---|
| 1 | virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) | プレイヤーが参加する前に実行されます。また、virtual void InitGameState() もあります |
| 2 | virtual void PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) | 拒否するには、ErrorMessage を空でない値に設定します。ブロックせずに認証情報を確認する場合は、代わりに PreLoginAsync(..., const FOnPreLoginCompleteDelegate& OnComplete)(GameModeBase.h:303)をオーバーライドします。この場合、必ず OnComplete を呼び出してください(空の文字列は許可を意味します)。呼び出さないと接続がハングし、さらに PreLogin のチェックも実行する必要があります |
| 3 | virtual APlayerController* Login(UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) | APlayerController とその APlayerState を生成します |
| 4 | virtual void PostLogin(APlayerController* NewPlayer) | サーバーからクライアントへの RPC が安全に実行できる最初の時点です |
| 5 | virtual void OnPostLogin(AController* NewPlayer) | 保護されています。PostLogin の最後に実行され、ポーンの生成前に呼び出される最後のフックです |
| 6 | virtual void HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer) | BlueprintNativeEvent です。プレイヤーが観戦する必要がない限り、RestartPlayer を呼び出します |
| 7 | virtual void RestartPlayer(AController* NewPlayer) | FindPlayerStart → SpawnDefaultPawnFor → Possess |
| — | virtual void Logout(AController* Exiting) | PlayerState を持つコントローラーが退出したときに実行されます |
// MyGameMode.cpp - include the header of every class named below
#include "MyGameMode.h"
AMyGameMode::AMyGameMode()
{
DefaultPawnClass = AMyCharacter::StaticClass();
PlayerControllerClass = AMyPlayerController::StaticClass();
GameStateClass = AMyGameState::StaticClass();
PlayerStateClass = AMyPlayerState::StaticClass();
HUDClass = AMyHUD::StaticClass();
bUseSeamlessTravel = true;
}
bool AMyGameMode::ReadyToStartMatch_Implementation()
{
return GetNumPlayers() >= MinPlayersToStart;
}
ChoosePlayerStart、FindPlayerStart、SpawnDefaultPawnFor、PlayerCanRestart、MustSpectate、ReadyToStartMatch、ReadyToEndMatch は BlueprintNativeEvent です。生成された _Implementation をオーバーライドし、名前だけの関数はオーバーライドしないでください。シグネチャの原文表記:virtual AActor* ChoosePlayerStart_Implementation(AController* Player)、virtual AActor* FindPlayerStart_Implementation(AController* Player, const FString& IncomingName)、virtual APawn* SpawnDefaultPawnFor_Implementation(AController* NewPlayer, AActor* StartSpot)。同じパイプライン内の通常の virtual 関数:virtual void RestartPlayerAtPlayerStart(AController* NewPlayer, AActor* StartSpot)、virtual void RestartPlayerAtTransform(AController* NewPlayer, const FTransform& SpawnTransform)、virtual bool ShouldSpawnAtStartSpot(AController* Player)。スポーンポイントは APlayerStart アクターで、FName PlayerStartTag によって選択されます。
マッチ状態(AGameMode のみ)
パブリック:FName GetMatchState() const、virtual bool IsMatchInProgress() const、virtual void StartMatch()、virtual void EndMatch()、virtual void AbortMatch()、virtual void RestartGame()。
プロテクトされたフック:virtual void SetMatchState(FName NewState)、virtual void OnMatchStateSet()、virtual void HandleMatchIsWaitingToStart()、virtual void HandleMatchHasStarted()、virtual void HandleMatchHasEnded()、virtual void HandleLeavingMap()、virtual void HandleMatchAborted()。
状態を遷移させる正規の方法は SetMatchState のみです。新しい状態は AGameState::MatchState に設定され、クライアントにレプリケートされます。MatchState を直接書き換えないでください。
GameState(全クライアント)
クライアントからは GameMode が見えないため、クライアントが必要とする情報はすべてここに格納します。
// MyGameState.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "MyGameState.generated.h"
UCLASS()
class MYGAME_API AMyGameState : public AGameStateBase
{
GENERATED_BODY()
public:
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Match")
int32 TeamAScore = 0;
UPROPERTY(ReplicatedUsing = OnRep_MatchTimeRemaining, BlueprintReadOnly, Category = "Match")
float MatchTimeRemaining = 0.f;
protected:
UFUNCTION()
void OnRep_MatchTimeRemaining();
};
// MyGameState.cpp - also include Net/UnrealNetwork.h
void AMyGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyGameState, TeamAScore);
DOREPLIFETIME(AMyGameState, MatchTimeRemaining);
}
void AMyGameState::OnRep_MatchTimeRemaining()
{
// Client-side reaction: refresh the timer widget.
}
すべての ReplicatedUsing = X には、対応する UFUNCTION() void X(); の宣言が必要です。また、すべての Replicated/ReplicatedUsing プロパティには、GetLifetimeReplicatedProps 内に DOREPLIFETIME の行が必要です。条件、COND_*、プッシュモデル、RPC のルールについては ue-networking-replication を参照してください。
継承メンバー:TArray<TObjectPtr<APlayerState>> PlayerArray(それ自体はレプリケートされません。各 APlayerState が AddPlayerState を通じてすべてのマシンで自身を追加します。PlayerState は常に関連性があるため、これは機能します)。継承されたレプリケート対象メンバー:TSubclassOf<AGameModeBase> GameModeClass(ReplicatedUsing = OnRep_GameModeClass)、TSubclassOf<ASpectatorPawn> SpectatorClass、bReplicatedHasBegunPlay、ReplicatedWorldTimeSecondsDouble。アクセサー:virtual double GetServerWorldTimeSeconds() const、virtual bool HasBegunPlay() const、virtual bool HasMatchStarted() const、virtual bool HasMatchEnded() const、virtual void AddPlayerState(APlayerState* PlayerState)、virtual void RemovePlayerState(APlayerState* PlayerState)。AGameState は FName MatchState(ReplicatedUsing = OnRep_MatchState)と int32 ElapsedTime(replicatedUsing = OnRep_ElapsedTime)を追加します。
PlayerController
// MyPlayerController.cpp
#include "MyPlayerController.h"
#include "EnhancedInputSubsystems.h"
#include "Engine/LocalPlayer.h"
#include "InputMappingContext.h"
void AMyPlayerController::BeginPlay()
{
Super::BeginPlay();
if (IsLocalController())
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
SetInputMode(FInputModeGameOnly());
}
}
void AMyPlayerController::ServerRequestRespawn_Implementation()
{
// Runs on the server.
}
bool AMyPlayerController::ServerRequestRespawn_Validate()
{
return true;
}
上記の宣言(クラス本体全体はリファレンスを参照):virtual void BeginPlay() override;、virtual void SetupInputComponent() override;、virtual void OnPossess(APawn* aPawn) override;、virtual void OnUnPossess() override;、および UFUNCTION(Server, Reliable, WithValidation) void ServerRequestRespawn();。
AController::Possess と AController::UnPossess は virtual final です。代わりに OnPossess/OnUnPossess をオーバーライドしてください。APlayerController::SetupInputComponent() は、APlayerController 自体で宣言されている protected virtual 関数です。所有状態が変わっても有効な入力(メニュー、観戦者用キー、グローバルショートカット)にはこれを使用してください。Pawn 固有のバインドは APawn::SetupPlayerInputComponent に記述し、マッピングコンテキスト、アクション、モディファイア、トリガーについては ue-input-system を参照してください。
注目すべきメンバー:TObjectPtr<APawn> AcknowledgedPawn(サーバーで確認済みの所有状態)、TObjectPtr<AHUD> MyHUD、TObjectPtr<APlayerCameraManager> PlayerCameraManager、TObjectPtr<UPlayerInput> PlayerInput(ローカルのみ)、TSubclassOf<UCheatManager> CheatClass、uint32 bShowMouseCursor:1、uint32 bEnableClickEvents:1、uint32 bEnableStreamingSource:1(このビューポートの World Partition 読み込みを制御します)、uint16 SeamlessTravelCount。入力モードの FInputModeGameOnly、FInputModeUIOnly、FInputModeGameAndUI は、virtual void SetInputMode(const FInputModeDataBase& InData) で適用します。
| カメラ関連の処理 | 担当 |
|---|---|
PlayerCameraManager、SetViewTarget、SetViewTargetWithBlend、カメラモディファイア、カメラシェイク、Gameplay Cameras(5.8 では実験的機能) | ue-gameplay-cameras |
**リッスンサーバーの二重の役割:**ホストの PlayerController は ROLE_Authority とローカルプレイヤーの両方を持ちます。HasAuthority() とともに IsLocalController() でも判定してください。専用サーバー向けに記述されたコードは、リッスンサーバーのホストで二度実行されることがよくあります。
PlayerState
// MyPlayerState.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "MyPlayerState.generated.h"
UCLASS()
class MYGAME_API AMyPlayerState : public APlayerState
{
GENERATED_BODY()
public:
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
virtual void CopyProperties(APlayerState* PlayerState) override;
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Score")
int32 Kills = 0;
UPROPERTY(ReplicatedUsing = OnRep_TeamIndex, BlueprintReadOnly, Category = "Team")
uint8 TeamIndex = 0;
protected:
UFUNCTION()
void OnRep_TeamIndex();
};
// MyPlayerState.cpp - also include Net/UnrealNetwork.h
void AMyPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyPlayerState, Kills);
DOREPLIFETIME(AMyPlayerState, TeamIndex);
}
void AMyPlayerState::OnRep_TeamIndex()
{
// Client-side reaction: recolour the team badge.
}
void AMyPlayerState::CopyProperties(APlayerState* PlayerState)
{
Super::CopyProperties(PlayerState);
if (AMyPlayerState* Target = Cast<AMyPlayerState>(PlayerState))
{
Target->Kills = Kills;
Target->TeamIndex = TeamIndex;
}
}
UFUNCTION() の OnRep 宣言と DOREPLIFETIME エントリーの両方がないと、プロパティはコンパイルされて実行されますが、レプリケーションされず、何も通知されません。レプリケーション条件とプッシュモデルについては、ue-networking-replication を参照してください。
シームレストラベル用フック:virtual void CopyProperties(APlayerState* PlayerState)(protected。古い PlayerState に対して、引数として引き継ぎ先の PlayerState を渡して呼び出されます。また、非アクティブな PlayerState が切断時に保存される際にも呼び出されます)、virtual void OverrideWith(APlayerState* PlayerState)、virtual void SeamlessTravelTo(class APlayerState* NewPlayerState)。組み込み機能:FString GetPlayerName() const、float GetScore() const / void SetScore(const float NewScore)、int32 GetPlayerId() const、APawn* GetPawn() const、class APlayerController* GetPlayerController() const、virtual void ClientInitialize(class AController* C)。
MyPawn->GetPlayerState() または MyController->PlayerState で 1 つにアクセスします。GameState の PlayerArray からすべてにアクセスできます。
if (const AGameStateBase* GS = GetWorld()->GetGameState<AGameStateBase>())
{
for (APlayerState* PS : GS->PlayerArray) { /* every connected player */ }
}
Pawn と Character
APawn は、最小構成の Possess 可能なアクターです。メッシュ、コリジョンコンポーネント、移動コンポーネントはありません。ACharacter はカプセルルート、スケルタルメッシュ、ネットワーク予測機能を備えた UCharacterMovementComponent を追加します。ADefaultPawn はエンジンの飛行用プレースホルダー(GetCollisionComponent()、GetMeshComponent()、bAddDefaultMovementBindings)です。ASpectatorPawn はこれを継承します。
// Pawn-side overrides
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
virtual void PossessedBy(AController* NewController) override;
virtual void UnPossessed() override;
virtual void OnRep_Controller() override;
virtual void NotifyControllerChanged() override;
PossessedBy はサーバー上でのみ実行されます。OnRep_Controller は、レプリケートされた Controller ポインターに対応するクライアント側の処理です。NotifyControllerChanged はサーバーと所有クライアントで実行されます(シミュレートされたプロキシでは実行されません。Pawn.h:396)。そのため、入力マッピングコンテキストの更新など、所有者側のロジックを記述するのに適しています。
Character のコンストラクターでは、GetCapsuleComponent()->SetCapsuleSize(42.f, 96.f) でカプセルのサイズを設定し、GetMesh()->SetRelativeLocation(...) / SetRelativeRotation(...) でメッシュの位置を設定します。そのために #include "Components/CapsuleComponent.h" と #include "Components/SkeletalMeshComponent.h" が必要です。
| 領域 | 担当 |
|---|---|
UCharacterMovementComponent の調整、EMovementMode、PhysCustom(最初に Super::PhysCustom(DeltaTime, Iterations) を呼び出します)、ルートモーション、予測、FMovementBaseInterfaceData、Mover(5.8 では Experimental) | ue-character-movement |
Character で直接使う API:virtual void Jump()、virtual void StopJumping()、bool CanJump() const、virtual void LaunchCharacter(FVector LaunchVelocity, bool bXYOverride, bool bZOverride)、virtual void Crouch(bool bClientSimulation = false)、virtual void UnCrouch(bool bClientSimulation = false)、virtual void OnRep_IsCrouched()。レプリケートされる状態:float JumpMaxHoldTime、int32 JumpMaxCount、uint8 bIsCrouched:1。コンポーネントのゲッター:GetCapsuleComponent()、GetMesh()、GetCharacterMovement()、GetArrowComponent()(#if WITH_EDITORONLY_DATA の下でのみ宣言されています。呼び出す場合はガードしてください)。
HUD
AHUDは、AGameModeBase::HUDClassから生成される、ローカルプレイヤー1人用の即時モードキャンバスオーバーレイです。専用サーバー上には存在しません。保持型UI(メニュー、体力バー、レイアウト)にはUMGを使用してください。詳細はue-ui-umg-slateを参照してください。
// MyHUD.cpp — declares: virtual void DrawHUD() override; virtual void NotifyHitBoxClick(FName BoxName) override;
#include "Engine/Canvas.h" // plus MyHUD.h and GameFramework/PlayerController.h
static const FName MenuBoxName(TEXT("MenuButton"));
void AMyHUD::DrawHUD()
{
Super::DrawHUD();
if (Canvas == nullptr)
{
return;
}
const float CentreX = Canvas->SizeX * 0.5f;
const float CentreY = Canvas->SizeY * 0.5f;
DrawRect(FLinearColor::White, CentreX - 2.f, CentreY - 2.f, 4.f, 4.f);
DrawText(TEXT("Menu"), FLinearColor::White, 40.f, 40.f);
AddHitBox(FVector2D(40.f, 40.f), FVector2D(120.f, 24.f), MenuBoxName, true, 0);
}
void AMyHUD::NotifyHitBoxClick(FName BoxName)
{
Super::NotifyHitBoxClick(BoxName);
if (BoxName == MenuBoxName)
{
if (APlayerController* PC = GetOwningPlayerController())
{
PC->SetInputMode(FInputModeUIOnly());
}
}
}
TObjectPtr<UCanvas> Canvasが有効なのはDrawHUD内だけです。キャッシュしないでください。描画ヘルパー:DrawText、DrawLine、DrawRect、DrawTexture、DrawTextureSimple。void AddHitBox(FVector2D Position, FVector2D Size, FName InName, bool bConsumesInput, int32 Priority = 0)で追加したヒットボックスは、ネイティブ関数virtual void NotifyHitBoxClick(FName BoxName)、NotifyHitBoxRelease、NotifyHitBoxBeginCursorOver、NotifyHitBoxEndCursorOverと、ReceiveHitBoxClick/ReceiveHitBoxRelease/ReceiveHitBoxBeginCursorOverのBlueprintイベントを発生させます。所有者へのアクセス:TObjectPtr<APlayerController> PlayerOwner、APlayerController* GetOwningPlayerController() const、APawn* GetOwningPawn() const。
デバッグ表示:uint8 bShowHUD:1はすべての描画を制御し、ShowHUD execによって切り替わります。virtual void ShowDebug(FName DebugType = NAME_None)は、showdebugコンソールコマンドを処理します(エンジンがサポートする値にはAI、physics、net、camera、collisionがあります)。独自の行を追加するには、virtual void ShowDebugInfo(float& YL, float& YPos)をオーバーライドしてください。
GASを使わないダメージ処理
エンジンのダメージ処理は、1つの仮想関数と4つのUGameplayStaticsエントリーポイントで構成されます。GASを使用しない場合に使ってください。属性、実行計算、キューについてはue-gameplay-abilitiesを参照してください。
// Dealing damage - server authority only. Needs Kismet/GameplayStatics.h.
// Target, Hit, ShotDirection, InstigatorController and IgnoreActors are locals of the caller.
UGameplayStatics::ApplyDamage(Target, 25.f, InstigatorController, this, UMyDamageType::StaticClass());
UGameplayStatics::ApplyPointDamage(Target, 40.f, ShotDirection, Hit,
InstigatorController, this, UMyDamageType::StaticClass());
UGameplayStatics::ApplyRadialDamage(this, 100.f, Hit.ImpactPoint, 500.f,
UMyDamageType::StaticClass(), IgnoreActors, this, InstigatorController, false, ECC_Visibility);
UGameplayStatics::ApplyRadialDamageWithFalloff(this, 100.f, 10.f, Hit.ImpactPoint, 200.f, 600.f, 1.f,
UMyDamageType::StaticClass(), IgnoreActors, this, InstigatorController, ECC_Visibility);
// Receiving damage. AMyCharacter declares UPROPERTY(Replicated) float Health = 100.f; and FName LastHitBone;
// Declaration:
// virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser) override;
#include "Engine/DamageEvents.h"
float AMyCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
const float Applied = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
if (!HasAuthority() || Applied <= 0.f)
{
return 0.f;
}
if (DamageEvent.IsOfType(FPointDamageEvent::ClassID))
{
const FPointDamageEvent& Point = static_cast<const FPointDamageEvent&>(DamageEvent);
LastHitBone = Point.HitInfo.BoneName;
}
Health = FMath::Max(0.f, Health - Applied);
return Applied;
}
FDamageEventはTSubclassOf<UDamageType> DamageTypeClassを保持し、virtual bool IsOfType(int32 InID) constを通じてディスパッチします。FPointDamageEventはfloat Damage、FVector_NetQuantizeNormal ShotDirection、FHitResult HitInfoを追加します。FRadialDamageEventはFRadialDamageParams Params、FVector Origin、TArray<FHitResult> ComponentHitsを追加します。UDamageTypeはクラスとして使われるUObjectで、ヒットごとにインスタンス化されることはありません。サブクラス化して、float DamageImpulse、float DestructibleImpulse、float DamageFalloff、uint32 bCausedByWorld:1、uint32 bScaleMomentumByMass:1を持たせます。
TakeDamageのオーバーライドに代わる動的マルチキャスト。すべてAActorにあります。
| デリゲート | ハンドラーのシグネチャ |
|---|---|
OnTakeAnyDamage | (AActor* DamagedActor, float Damage, const UDamageType* DamageType, AController* InstigatedBy, AActor* DamageCauser) |
OnTakePointDamage | (AActor* DamagedActor, float Damage, AController* InstigatedBy, FVector HitLocation, UPrimitiveComponent* FHitComponent, FName BoneName, FVector ShotFromDirection, const UDamageType* DamageType, AActor* DamageCauser) |
OnTakeRadialDamage | (AActor* DamagedActor, float Damage, const UDamageType* DamageType, FVector Origin, const FHitResult& HitInfo, AController* InstigatedBy, AActor* DamageCauser) |
OnTakeAnyDamage.AddDynamic(this, &AMyCharacter::HandleAnyDamage);でバインドし、ハンドラーにUFUNCTION()を付けてください。void SetCanBeDamaged(bool bInCanBeDamaged)/bool CanBeDamaged() constでダメージを受けるかどうかを設定します。AWorldSettings::KillZを下回ると、AWorldSettings::KillZDamageTypeを使ってvirtual void FellOutOfWorld(const class UDamageType& dmgType)が呼び出されます。
ゲームインスタンス
プロセスごとに1つ存在し、シームレスなトラベルを含むすべてのレベル読み込みを通じて存続します。virtual void Init()(起動時に1回。Super::Init() はゲームインスタンスのサブシステムを最後に初期化するため、その呼び出しの後でのみ使用してください)、保護された virtual void OnStart()(インスタンスの準備完了時に1回)、および virtual void Shutdown()(終了時)をオーバーライドします。UGameInstance::GetWorld() は final です。オーバーライドしないでください。ゲームインスタンスのサブシステム(UGameInstanceSubsystem)は、レベルをまたいで利用するサービスの適切な置き場所です。サブシステムの一覧表は ue-cpp-foundations にあります。
| 必要なもの | 呼び出し |
|---|---|
| アクターからゲームインスタンスを取得する | AActor::GetGameInstance<UMyGameInstance>() または UWorld::GetGameInstance<UMyGameInstance>() |
| ワールドを持つ任意の UObject からゲームインスタンスを取得する | UGameplayStatics::GetGameInstance(const UObject* WorldContextObject) |
| ゲームインスタンスのサブシステム | UGameInstance::GetSubsystem<UMyGameInstanceSubsystem>() |
| 最初のローカルプレイヤーコントローラー | UWorld::GetFirstPlayerController() または UGameInstance::GetFirstLocalPlayerController(const UWorld* World = nullptr) |
| 特定のプレイヤーコントローラー | UGameplayStatics::GetPlayerController(const UObject* WorldContextObject, int32 PlayerIndex) |
| ゲームモード(サーバーのみ) | UWorld::GetAuthGameMode<AMyGameMode>() または UGameplayStatics::GetGameMode(const UObject* WorldContextObject) |
| ゲームステート | UWorld::GetGameState<AMyGameState>() または UGameplayStatics::GetGameState(const UObject* WorldContextObject) |
セッション
5.8 には2つのスタックが含まれています。レガシーの OnlineSubsystem(モジュール "OnlineSubsystem"、"OnlineSubsystemUtils"。バックエンドは Null、Steam、EOS)は、既存のほとんどのプロジェクトで使われています。Online Services v2(Plugins/Online/OnlineServices、モジュール "OnlineServicesInterface")は新しいコード向けの最新の方式であり、OnlineServicesOSSAdapter が両者をつなぎます。セッションのコードはレベルより長く存続する必要があるため、UGameInstance またはゲームインスタンスのサブシステムに配置します。
// Legacy OnlineSubsystem - host. Needs OnlineSubsystem.h, OnlineSessionSettings.h and
// Interfaces/OnlineSessionInterface.h; CreateSessionHandle is an FDelegateHandle member.
IOnlineSubsystem* OSS = IOnlineSubsystem::Get();
IOnlineSessionPtr Sessions = OSS ? OSS->GetSessionInterface() : nullptr;
if (Sessions.IsValid())
{
FOnlineSessionSettings Settings;
Settings.NumPublicConnections = 4;
Settings.bShouldAdvertise = true;
Settings.bIsLANMatch = false;
Settings.bUsesPresence = true;
CreateSessionHandle = Sessions->AddOnCreateSessionCompleteDelegate_Handle(
FOnCreateSessionCompleteDelegate::CreateUObject(this, &UMyGameInstance::HandleCreateSessionComplete));
Sessions->CreateSession(0, NAME_GameSession, Settings);
}
FOnlineSessionSearch と Sessions->FindSessions(0, SessionSearch.ToSharedRef()) で検索し、Sessions->JoinSession(0, NAME_GameSession, SessionSearch->SearchResults[Index]) で参加します。その後、Sessions->GetResolvedConnectString(NAME_GameSession, ConnectInfo) でアドレスを解決し、PlayerController->ClientTravel(ConnectInfo, TRAVEL_Absolute) を呼び出します。ハンドラーは void HandleCreateSessionComplete(FName SessionName, bool bWasSuccessful)、void HandleFindSessionsComplete(bool bWasSuccessful)、void HandleJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result) です。保存した FDelegateHandle はすべて、対応する Clear...Delegate_Handle で解放してください。検索と参加のコード全体は、references/framework-class-map.md を参照してください。
// Online Services (v2). Needs Online/OnlineServices.h and Online/Sessions.h.
using namespace UE::Online;
TSharedPtr<IOnlineServices> Services = GetServices(EOnlineServices::Default);
ISessionsPtr SessionsV2 = Services.IsValid() ? Services->GetSessionsInterface() : nullptr;
v2 の呼び出しはデリゲートハンドルではなく TOnlineAsyncOpHandle/TOnlineResult を返します。ISessions に並ぶ関連インターフェースは IAuth、ILobbies、IAchievements、IPresence、ILeaderboards です。
トラベル
| パターン | 呼び出し | クライアント | GameMode / GameState |
|---|---|---|---|
| サーバートラベル、シームレスでない場合 | UWorld::ServerTravel(const FString& InURL, bool bAbsolute = false, bool bShouldSkipGameNotify = false) | 切断して再接続 | 再作成 |
| サーバートラベル、シームレスな場合 | AGameModeBase::bUseSeamlessTravel = true を設定した同じ呼び出し | 接続を維持 | 再作成 |
| 1 つのクライアントを別のサーバーに移動 | APlayerController::ClientTravel(const FString& URL, ETravelType TravelType, bool bSeamless = false, FGuid MapPackageGuid = FGuid()) | そのクライアントのみ | 該当なし |
| マップをローカルにロード | UGameplayStatics::OpenLevel(const UObject* WorldContextObject, FName LevelName, bool bAbsolute = true, FString Options = FString(TEXT(""))) | 該当なし | 再作成 |
GameMode からリッスンサーバーのマップを変更するには、GetWorld()->ServerTravel(TEXT("/Game/Maps/NewMap?listen")) を使用します。
シームレストラベルは 2 段階(現在のマップ → トランジションマップ → 移動先)で実行され、virtual void GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList) は両方の段階で呼び出されます。UGameInstance、APlayerController、APlayerState は引き継がれますが、AGameModeBase、AGameStateBase、ポーン、レベルアクターは引き継がれません。AGameModeBase::ProcessServerTravel(const FString& URL, bool bAbsolute = false) は、URL の処理をカスタマイズするためのオーバーライドポイントです。レベルストリーミング、World Partition、Data Layers については ue-world-level-streaming を参照してください。
非推奨 — 使用しないでください
| 使用しないもの | 5.8 で使用するもの | 出典 |
|---|---|---|
DispatchPostLogin(NewPlayer) | OnPostLogin(AController* NewPlayer) をオーバーライドします。これは PostLogin の最後に実行されます | GameFramework/GameModeBase.h:329 の UE_DEPRECATED(5.6) |
virtual void Possess(APawn*) override / virtual void UnPossess() override | OnPossess(APawn* aPawn) / OnUnPossess() | GameFramework/Controller.h:284,288 の virtual final |
Pawn->IsControlled() | IsPawnControlled() または IsPlayerControlled() | GameFramework/Pawn.h:254 の UE_DEPRECATED(4.24) |
Pawn->GetMovementBase() | GetMovementBaseObject() または GetMovementBaseInterfaceData() | GameFramework/Pawn.h:59、GameFramework/Character.h:797 の UE_DEPRECATED(5.8) |
Character->SetBase(UPrimitiveComponent*, BoneName, bNotifyActor) | SetBase(FMovementBaseInterfaceData*, const FName, bool) | GameFramework/Character.h:515 の UE_DEPRECATED(5.8) |
Pawn->RemoteViewPitch | GetRemoteViewPitch() / RemoteViewPitch16 | GameFramework/Pawn.h:134 の UE_DEPRECATED(5.6) |
Actor->NetUpdateFrequency = 10.f | SetNetUpdateFrequency(10.f) / GetNetUpdateFrequency() | GameFramework/Actor.h:903 の UE_DEPRECATED(5.5) |
Actor->MinNetUpdateFrequency / Actor->NetCullDistanceSquared への直接アクセス | Set/GetMinNetUpdateFrequency()、Set/GetNetCullDistanceSquared() | GameFramework/Actor.h:898,908 の UE_DEPRECATED(5.5) |
PlayerController::InputKey(...) の旧オーバーロード | FInputKeyEventArgs を受け取るオーバーロード | GameFramework/PlayerController.h:1785 の UE_DEPRECATED(5.6) |
PlayerController::InputTouch(...) の旧オーバーロード | FTouchId を受け取るオーバーロード | GameFramework/PlayerController.h:1794 の UE_DEPRECATED(5.8) |
PlayerController::ProcessTouchHitResult(FInputDeviceId, uint32, ...) | FTouchId を受け取るオーバーロード | GameFramework/PlayerController.h:2005 の UE_DEPRECATED(5.8) |
SetDeprecatedInputYawScale() および Pitch/Roll の同種の関数 | Enhanced Input の UInputModifierScalar | GameFramework/PlayerController.h:511 の UE_DEPRECATED(5.0) |
UCheatManager::DebugCameraControllerRef | FDebugCameraManager | GameFramework/CheatManager.h:107 の UE_DEPRECATED(5.8) |
よくある間違い
クライアントでのGameModeの参照解除: GetAuthGameMode() はサーバー以外では常に nullptr を返すため、GetWorld()->GetAuthGameMode<AMyGameMode>()->EndMatch() を実行するとクラッシュします。代わりに、if (AMyGameMode* GM = GetWorld()->GetAuthGameMode<AMyGameMode>()) { GM->EndMatch(); } と記述してください。
OnRepの宣言がないReplicatedUsing: UHTでビルドに失敗します。ReplicatedUsing = OnRep_X を指定する場合は、同じクラス本体に必ず UFUNCTION() void OnRep_X(); を記述してください。
DOREPLIFETIME がないレプリケート対象プロパティ: コンパイルされ、実行もされますが、レプリケートされないままになります。まず Super:: を呼び出し、その後、プロパティごとに1つずつ DOREPLIFETIME を指定した GetLifetimeReplicatedProps を追加してください。
Possess/UnPossess のオーバーライド: これらはどちらも AController で virtual final です。代わりに OnPossess(APawn* aPawn) と OnUnPossess() をオーバーライドしてください。
BlueprintNativeEvent を名前そのままでオーバーライドする: ChoosePlayerStart、FindPlayerStart、SpawnDefaultPawnFor、HandleStartingNewPlayer、ReadyToStartMatch、ReadyToEndMatch、PlayerCanRestart、MustSpectate は、いずれも _Implementation のサフィックスが必要です。また、呼び出しをチェーンする場合は、対応する Super:: の呼び出しにも同じサフィックスが必要です。
専用サーバーでのHUD、カメラ、オーディオ処理: これらのオブジェクトは専用サーバー上には存在しません。プレゼンテーション関連のコードは if (GetNetMode() != NM_DedicatedServer) でガードしてください。
データに対してクラスが間違っている: すべてのクライアントから見えるスコアは APlayerController ではなく APlayerState に持たせます。クライアントが参照するマッチタイマーは AGameModeBase ではなく、レプリケートされる AGameStateBase のプロパティに持たせます。レベルをまたいで保持するデータは AGameStateBase ではなく UGameInstance に持たせます。ポーンの入力バインドはCharacterコンストラクターではなく、APawn::SetupPlayerInputComponent に記述します。
サーバーが所持を確認する前の GetPawn() 呼び出し: サーバーが確認したポーンが必要な場合は、AcknowledgedPawn を使用してください。
複数プレイヤーでのPIE: 各プレイヤーにそれぞれのPlayerControllerが割り当てられますが、GameModeインスタンスは共有されます。マルチプレイヤー関連の処理を信頼する前に、PIEの「Number of Players」を2以上に設定してください。
関連スキル
ue-gameplay-abilities— GASの属性、ゲームプレイエフェクト、アビリティ駆動型のダメージ(以下の代替手段)ue-networking-replication—DOREPLIFETIMEの条件、COND_*、RPCのルール、プッシュモデル、Iris(5.8ではベータ版)ue-character-movement—UCharacterMovementComponent、移動モード、PhysCustom、予測、Moverue-gameplay-cameras—APlayerCameraManager、ビューフォーカスターゲットとブレンド、カメラモディファイア、Gameplayue-input-system— Enhanced Inputのマッピングコンテキスト、入力アクション、モディファイア、トリガーue-actor-component-architecture— アクターのライフサイクル、コンポーネント、アタッチメント、ティックグループue-cpp-foundations—UCLASS/UPROPERTY/UFUNCTIONの指定子、TObjectPtr、サブシステムの一覧ue-ui-umg-slate— UMGウィジェットと保持型UI、キャンバスHUD描画の一般的な代替手段ue-serialization-savegames—USaveGame、スロット、UGameInstanceをまたいで保持するデータue-world-level-streaming— レベルストリーミング、World Partition、Data Layers、ストリーミングソースue-ai-navigation— AIコントローラー、ビヘイビアツリー、知覚、EQS、ナビメッシュクエリue-audio-system— UAudioComponent、MetaSounds、サブミックス、減衰、同時実行- その他の関連項目:
ue-blueprint-cpp-interop、ue-game-features、ue-mover、ue-physics-collision、ue-state-trees、ue-testing-debugging