Unreal Engine ネットワーク機能&FPS実装 完全ガイド

📚 第1章: ネットワーキングの基礎知識

ネットワークゲームの基本的な仕組み

マルチプレイヤーゲームでは、以下の要素が重要です:

1.1 Unreal Engineのネットワーク機能

主要な3つの機能

  1. レプリケーション (Replication)
    • ゲームの状態を自動的に同期する機能
    • 例:プレイヤーの位置、体力、所持アイテムなど
  2. RPC (Remote Procedure Calls)
    • サーバーとクライアント間で関数を呼び出す機能
    • 例:攻撃、アイテム使用、チャットメッセージなど
  3. アクターのレプリケーション
    • ゲーム内のオブジェクトを同期する機能
    • 例:武器、弾丸、エフェクトなど

1.2 基本的な設定方法

プロジェクト設定

  1. Edit → Project Settings を開く
  2. Maps & Modes セクションで:
    • Default GameMode を設定
    • Game Default Map を選択
  3. プレイヤー人数の設定:
    • Number of Players を2以上に設定
    • Net Mode を選択(Listen Server推奨)

🎯 第2章: FPSゲームの実装

FPSゲームの基本要素

2.1 プレイヤーキャラクターの設定

C++での実装

// MyFPSCharacter.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MyFPSCharacter.generated.h"

UCLASS()
class MYFPSGAME_API AMyFPSCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    AMyFPSCharacter();

    // 体力システム
    UPROPERTY(Replicated, BlueprintReadWrite, Category = "Character")
    float Health;

    // 現在の武器
    UPROPERTY(ReplicatedUsing = OnRep_CurrentWeapon)
    class AWeapon* CurrentWeapon;

    // 武器変更通知
    UFUNCTION()
    void OnRep_CurrentWeapon();

    // 発射処理(サーバー)
    UFUNCTION(Server, Reliable)
    void ServerFire();

    // 発射エフェクト(全クライアント)
    UFUNCTION(NetMulticast, Reliable)
    void MulticastOnFire();

protected:
    virtual void BeginPlay() override;
    virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override;
};

// MyFPSCharacter.cpp
#include "MyFPSCharacter.h"
#include "Net/UnrealNetwork.h"
#include "Engine/Engine.h"

AMyFPSCharacter::AMyFPSCharacter()
{
    PrimaryActorTick.bCanEverTick = true;
    
    // ネットワーク機能を有効化
    bReplicates = true;
    
    // デフォルト値の設定
    Health = 100.0f;
}

void AMyFPSCharacter::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    
    // 変数のレプリケーション設定
    DOREPLIFETIME(AMyFPSCharacter, Health);
    DOREPLIFETIME(AMyFPSCharacter, CurrentWeapon);
}

void AMyFPSCharacter::OnRep_CurrentWeapon()
{
    // 武器が変更された時の処理
    if (CurrentWeapon)
    {
        // 武器の表示更新など
    }
}

void AMyFPSCharacter::ServerFire_Implementation()
{
    // サーバーでの発射処理
    if (HasAuthority())
    {
        // ヒット判定
        FHitResult HitResult;
        FVector Start = GetActorLocation();
        FVector End = Start + GetActorForwardVector() * 1000.0f;
        
        if (GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Visibility))
        {
            // ヒット時の処理
            if (AActor* HitActor = HitResult.GetActor())
            {
                // ダメージ適用
                FDamageEvent DamageEvent;
                HitActor->TakeDamage(20.0f, DamageEvent, GetController(), this);
            }
        }
        
        // 発射エフェクトを全クライアントに通知
        MulticastOnFire();
    }
}

void AMyFPSCharacter::MulticastOnFire_Implementation()
{
    // 発射エフェクトの再生
    // サウンド、パーティクル、アニメーションなど
}

ブループリントでの実装

// ブループリントでの設定手順

1. プレイヤーキャラクターBPを作成
   - Content Browser → 右クリック → Blueprint Class → Character

2. 変数の設定
   - Variables タブで新しい変数を追加
   - Replication セクションで「Replicated」を選択

3. 関数の設定
   - Functions タブで新しい関数を追加
   - Details パネルで:
     - Replicates: True
     - Run on Server/Client を選択

2.2 武器システムの実装

// Weapon.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Weapon.generated.h"

UCLASS()
class MYFPSGAME_API AWeapon : public AActor
{
    GENERATED_BODY()

public:
    AWeapon();

    // 武器の基本情報
    UPROPERTY(Replicated, BlueprintReadWrite, Category = "Weapon")
    int32 Ammo;

    UPROPERTY(Replicated, BlueprintReadWrite, Category = "Weapon")
    float Damage;

    // 発射処理
    UFUNCTION(Server, Reliable)
    void ServerFire();

    // リロード処理
    UFUNCTION(Server, Reliable)
    void ServerReload();

    // エフェクト
    UFUNCTION(NetMulticast, Reliable)
    void MulticastPlayFireEffect();

protected:
    virtual void BeginPlay() override;
    virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override;

    // コンポーネント
    UPROPERTY(VisibleAnywhere)
    USkeletalMeshComponent* WeaponMesh;
};

// Weapon.cpp
#include "Weapon.h"
#include "Net/UnrealNetwork.h"

AWeapon::AWeapon()
{
    PrimaryActorTick.bCanEverTick = true;
    bReplicates = true;

    // メッシュコンポーネントの設定
    WeaponMesh = CreateDefaultSubobject(TEXT("WeaponMesh"));
    RootComponent = WeaponMesh;

    // デフォルト値
    Ammo = 30;
    Damage = 20.0f;
}

void AWeapon::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    
    DOREPLIFETIME(AWeapon, Ammo);
    DOREPLIFETIME(AWeapon, Damage);
}

void AWeapon::ServerFire_Implementation()
{
    if (HasAuthority() && Ammo > 0)
    {
        Ammo--;
        
        // ヒット判定とダメージ処理
        // ...

        // エフェクト再生
        MulticastPlayFireEffect();
    }
}

void AWeapon::ServerReload_Implementation()
{
    if (HasAuthority())
    {
        Ammo = 30; // リロード
    }
}

void AWeapon::MulticastPlayFireEffect_Implementation()
{
    // 発射エフェクトの再生
    // パーティクル、サウンドなど
}

2.3 GameMode の設定

// MyFPSGameMode.h
#pragma once

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

UCLASS()
class MYFPSGAME_API AMyFPSGameMode : public AGameModeBase
{
    GENERATED_BODY()

public:
    AMyFPSGameMode();

    virtual void PostLogin(APlayerController* NewPlayer) override;
    virtual void Logout(AController* Exiting) override;

    // マッチ管理
    UFUNCTION(BlueprintCallable, Category = "Game")
    void StartMatch();

    UFUNCTION(BlueprintCallable, Category = "Game")
    void EndMatch();

protected:
    virtual void BeginPlay() override;

private:
    void HandleMatchStart();
    void HandleMatchEnd();
};

// MyFPSGameMode.cpp
#include "MyFPSGameMode.h"
#include "GameFramework/PlayerController.h"
#include "MyFPSCharacter.h"

AMyFPSGameMode::AMyFPSGameMode()
{
    // デフォルトのプレイヤ
    DefaultPawnClass = AMyFPSCharacter::StaticClass();
}

void AMyFPSGameMode::PostLogin(APlayerController* NewPlayer)
{
    Super::PostLogin(NewPlayer);

    if (NewPlayer)
    {
        // プレイヤーのスポーン位置を設定
        if (AActor* StartSpot = FindPlayerStart(NewPlayer))
        {
            FRotator StartRotation(0.0f, 0.0f, 0.0f);
            if (APawn* NewPawn = GetWorld()->SpawnActor(DefaultPawnClass, StartSpot->GetActorLocation(), StartRotation))
            {
                NewPlayer->Possess(NewPawn);
            }
        }
    }
}

void AMyFPSGameMode::Logout(AController* Exiting)
{
    Super::Logout(Exiting);

    // プレイヤー退出時の処理
    if (Exiting)
    {
        if (APawn* ExitingPawn = Exiting->GetPawn())
        {
            ExitingPawn->Destroy();
        }
    }
}

void AMyFPSGameMode::StartMatch()
{
    if (HasAuthority())
    {
        HandleMatchStart();
    }
}

void AMyFPSGameMode::EndMatch()
{
    if (HasAuthority())
    {
        HandleMatchEnd();
    }
}

void AMyFPSGameMode::HandleMatchStart()
{
    // マッチ開始時の処理
    // プレイヤーのリスポーン
    // スコアのリセット等
}

void AMyFPSGameMode::HandleMatchEnd()
{
    // マッチ終了時の処理
    // 結果の表示
    // 次のマッチの準備等
}

2.4 プロジェクトでの実装手順

1. 新規プロジェクトの作成

  1. First Person テンプレートを選択
  2. 必要なC++クラスを追加
  3. 基本設定を行う

2.5 マルチプレイヤーテスト設定

// ゲームインスタンスの設定
// MyGameInstance.h
UCLASS()
class MYFPSGAME_API UMyGameInstance : public UGameInstance
{
    GENERATED_BODY()

public:
    // セッション作成
    UFUNCTION(BlueprintCallable, Category = "Network")
    void CreateGameSession();

    // セッション参加
    UFUNCTION(BlueprintCallable, Category = "Network")
    void JoinGameSession();

protected:
    virtual void Init() override;
    
private:
    void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);
    void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);
};

// MyGameInstance.cpp
void UMyGameInstance::CreateGameSession()
{
    auto Sessions = IOnlineSubsystem::Get()->GetSessionInterface();
    if (Sessions.IsValid())
    {
        FOnlineSessionSettings SessionSettings;
        SessionSettings.bIsLANMatch = true;
        SessionSettings.NumPublicConnections = 4;
        SessionSettings.bShouldAdvertise = true;
        SessionSettings.bUsesPresence = true;
        
        Sessions->CreateSession(0, NAME_GameSession, SessionSettings);
    }
}

void UMyGameInstance::JoinGameSession()
{
    auto Sessions = IOnlineSubsystem::Get()->GetSessionInterface();
    if (Sessions.IsValid())
    {
        Sessions->FindSessions(0, SessionSearch);
    }
}

2.6 デバッグとテスト

テスト手順

  1. PIEでのマルチプレイヤーテスト:
    // エディタ設定
    Number of Players: 2
    Net Mode: Play As Listen Server
    
    // デバッグコマンド
    stat net           // ネットワーク統計
    ShowDebug net      // デバッグ情報表示
    net PktLag=100     // 遅延シミュレーション
  2. スタンドアロンでのテスト:
    // コマンドライン引数
    -game -server      // サーバーとして起動
    -game              // クライアントとして起動

2.7 最適化とベストプラクティス

// プレイヤーキャラクターの最適化例
void AMyFPSCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    // クライアントサイド予測
    if (IsLocallyControlled())
    {
        PredictMovement(DeltaTime);
    }
}

void AMyFPSCharacter::PredictMovement(float DeltaTime)
{
    // 移動予測の実装
    FVector PredictedLocation = GetActorLocation() + GetVelocity() * DeltaTime;
    // 補間処理など
}