Skip to content
Published on

ゲーム開発完全ガイド2025: Unity vs Unreal vs Godot, ECS, マルチプレイヤー, 最適化

Authors

1. ゲームエンジン選択(せんたく)ガイド

2026年のゲーム開発はかつてないほど**選択肢(せんたくし)が豊富(ほうふ)**である。Unityはインディーモバイルと中級(ちゅうきゅう)PCで依然(いぜん)強(つよ)く、Unreal Engine 5はAAA級(きゅう)の視覚的(しかくてき)品質(ひんしつ)を全(すべ)てに提供(ていきょう)し、Godot 4はオープンソース陣営(じんえい)で急速(きゅうそく)に成長(せいちょう)している。

1.1 エンジン比較(ひかく)

項目(こうもく)UnityUnreal Engine 5Godot 4
ライセンス売上(うりあげ)ベース売上5%ロイヤリティ($1M以降)完全(かんぜん)無料(むりょう)
主要言語C#C++ / BlueprintGDScript / C#
学習曲線(きょくせん)中級急峻(きゅうしゅん)容易(ようい)
グラフィックURP/HDRPLumen, NaniteOpenGL/Vulkan
モバイル最適化非常(ひじょう)に良い良い良い
コンソール対応全コンソール全コンソール限定的
インディー推奨良い良い非常に良い
AAA推奨可能標準(ひょうじゅん)不向(ふむ)き

1.2 どのエンジンを選(えら)ぶか?

Unity: モバイルゲーム、2Dゲーム、迅速(じんそく)なプロトタイピング、大(おお)きなコミュニティ、AR/VRマルチプラットフォーム

Unreal: 視覚的忠実度(ちゅうじつど)が最優先(さいゆうせん)、オープンワールド、写実的(しゃじつてき)グラフィック、シネマティックカットシーン

Godot: インディープロジェクト、学習目的、ライセンス費用ゼロ、小規模(しょうきぼ)ゲーム、軽量(けいりょう)エンジン

2. Unity Deep Dive

2.1 MonoBehaviour vs DOTS/ECS

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    [SerializeField] private float speed = 5f;
    [SerializeField] private float jumpForce = 10f;

    private Rigidbody rb;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(horizontal, 0, vertical) * speed * Time.deltaTime;
        transform.Translate(movement);

        if (Input.GetKeyDown(KeyCode.Space) && IsGrounded())
        {
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }
    }

    bool IsGrounded()
    {
        return Physics.Raycast(transform.position, Vector3.down, 1.1f);
    }
}
using Unity.Entities;
using Unity.Mathematics;
using Unity.Burst;

public struct PlayerInput : IComponentData
{
    public float2 Move;
    public bool Jump;
}

public struct Velocity : IComponentData
{
    public float3 Value;
}

public struct PlayerSpeed : IComponentData
{
    public float Value;
}

[BurstCompile]
public partial struct PlayerMovementSystem : ISystem
{
    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        float deltaTime = SystemAPI.Time.DeltaTime;

        foreach (var (input, velocity, speed, transform)
            in SystemAPI.Query<RefRO<PlayerInput>, RefRW<Velocity>, RefRO<PlayerSpeed>, RefRW<LocalTransform>>())
        {
            float3 movement = new float3(input.ValueRO.Move.x, 0, input.ValueRO.Move.y) * speed.ValueRO.Value;
            velocity.ValueRW.Value = movement;
            transform.ValueRW.Position += movement * deltaTime;
        }
    }
}

2.2 Job SystemとBurst Compiler

using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;
using Unity.Mathematics;

[BurstCompile]
public struct ParallelMovementJob : IJobParallelFor
{
    public NativeArray<float3> Positions;
    [ReadOnly] public NativeArray<float3> Velocities;
    public float DeltaTime;

    public void Execute(int index)
    {
        Positions[index] += Velocities[index] * DeltaTime;
    }
}

public class JobScheduler : MonoBehaviour
{
    public void UpdatePositions()
    {
        var positions = new NativeArray<float3>(10000, Allocator.TempJob);
        var velocities = new NativeArray<float3>(10000, Allocator.TempJob);

        var job = new ParallelMovementJob
        {
            Positions = positions,
            Velocities = velocities,
            DeltaTime = Time.deltaTime
        };

        JobHandle handle = job.Schedule(positions.Length, 64);
        handle.Complete();

        positions.Dispose();
        velocities.Dispose();
    }
}

2.3 Addressables - 非同期(ひどうき)アセット管理(かんり)

using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class AddressableLoader : MonoBehaviour
{
    [SerializeField] private AssetReferenceGameObject playerPrefab;

    async void Start()
    {
        AsyncOperationHandle<GameObject> handle = playerPrefab.InstantiateAsync();
        await handle.Task;

        if (handle.Status == AsyncOperationStatus.Succeeded)
        {
            GameObject player = handle.Result;
            Debug.Log($"Loaded: {player.name}");
        }
    }

    void OnDestroy()
    {
        Addressables.Release(playerPrefab);
    }
}

2.4 URP vs HDRP

特徴(とくちょう)URPHDRP
対象(たいしょう)モバイル、VR、コンソール高(こう)スペックPC、コンソール
品質良い映画(えいが)級
性能非常に良い重(おも)い
用途(ようと)インディー、モバイルAAA、シミュレータ

3. Unreal Engine 5 Deep Dive

3.1 Blueprint vs C++

Unrealは2つのワークフローを提供する。Blueprintは視覚的(しかくてき)ノードベースで迅速(じんそく)なプロトタイピングに適(てき)し、C++は性能と複雑(ふくざつ)なロジックに適する。

#include "MyCharacter.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"

AMyCharacter::AMyCharacter()
{
    PrimaryActorTick.bCanEverTick = true;

    SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->TargetArmLength = 400.0f;
    SpringArm->bUsePawnControlRotation = true;

    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
    Camera->SetupAttachment(SpringArm, USpringArmComponent::SocketName);

    GetCharacterMovement()->bOrientRotationToMovement = true;
    GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f);
    GetCharacterMovement()->JumpZVelocity = 600.0f;
    GetCharacterMovement()->AirControl = 0.2f;
}

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

    PlayerInputComponent->BindAxis("MoveForward", this, &AMyCharacter::MoveForward);
    PlayerInputComponent->BindAxis("MoveRight", this, &AMyCharacter::MoveRight);
    PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
}

void AMyCharacter::MoveForward(float Value)
{
    if (Value != 0.0f)
    {
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
        AddMovementInput(Direction, Value);
    }
}

3.2 Lumen - 動的(どうてき)グローバルイルミネーション

LumenはUnreal Engine 5の中核(ちゅうかく)機能の1つで、ベイクなしでリアルタイム間接照明と反射(はんしゃ)を処理する。

APostProcessVolume* PPV = ...;
PPV->Settings.bOverride_DynamicGlobalIlluminationMethod = true;
PPV->Settings.DynamicGlobalIlluminationMethod = EDynamicGlobalIlluminationMethod::Lumen;

3.3 Nanite - 仮想化ジオメトリ

Naniteは数十億ポリゴンメッシュを自動的(じどうてき)にLOD処理する。モデラーは手動(しゅどう)でLODを作(つく)る必要(ひつよう)がない。

UStaticMesh* Mesh = ...;
Mesh->NaniteSettings.bEnabled = true;
Mesh->NaniteSettings.PercentTriangles = 1.0f;

3.4 World Partition

巨大(きょだい)なオープンワールドを自動的にグリッドセルに分割(ぶんかつ)し、必要時のみロード。

4. Godot 4 Deep Dive

4.1 GDScript

extends CharacterBody3D

const SPEED = 5.0
const JUMP_VELOCITY = 4.5

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")

func _physics_process(delta):
    if not is_on_floor():
        velocity.y -= gravity * delta

    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    var input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
    var direction = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()

    if direction:
        velocity.x = direction.x * SPEED
        velocity.z = direction.z * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        velocity.z = move_toward(velocity.z, 0, SPEED)

    move_and_slide()

4.2 SceneシステムとSignal

extends Node

signal player_died(score)
signal score_changed(new_score)

var score = 0:
    set(value):
        score = value
        score_changed.emit(score)

func _ready():
    var player = $Player
    player.died.connect(_on_player_died)

func _on_player_died():
    player_died.emit(score)
    get_tree().reload_current_scene()

func add_score(amount: int):
    score += amount

4.3 C#サポート

using Godot;

public partial class PlayerController : CharacterBody3D
{
    public const float Speed = 5.0f;
    public const float JumpVelocity = 4.5f;

    public float Gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle();

    public override void _PhysicsProcess(double delta)
    {
        Vector3 velocity = Velocity;

        if (!IsOnFloor())
            velocity.Y -= Gravity * (float)delta;

        if (Input.IsActionJustPressed("jump") && IsOnFloor())
            velocity.Y = JumpVelocity;

        Vector2 inputDir = Input.GetVector("move_left", "move_right", "move_forward", "move_back");
        Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized();

        if (direction != Vector3.Zero)
        {
            velocity.X = direction.X * Speed;
            velocity.Z = direction.Z * Speed;
        }
        else
        {
            velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed);
            velocity.Z = Mathf.MoveToward(Velocity.Z, 0, Speed);
        }

        Velocity = velocity;
        MoveAndSlide();
    }
}

5. ゲームループの基礎(きそ)

while (gameRunning)
{
    ProcessInput();
    Update(deltaTime);
    Render();
    SyncFrameRate();
}

5.1 Fixed vs Variable Timestep

public class Player : MonoBehaviour
{
    void Update()
    {
        ProcessInput();
    }

    void FixedUpdate()
    {
        ApplyPhysics();
    }
}

6. 物理(ぶつり)エンジン

6.1 PhysX vs Chaos vs Bullet vs Box2D

エンジン使用先(しようさき)特徴
PhysXUnity, 旧Unreal安定、GPU加速
ChaosUnreal Engine 5+Epic自社製、破壊シミュレーション
BulletBlender, Godot一部オープンソース
Box2D2Dゲーム軽量、正確
public class BallController : MonoBehaviour
{
    private Rigidbody rb;
    public float bounciness = 0.8f;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
        var material = new PhysicMaterial
        {
            bounciness = bounciness,
            dynamicFriction = 0.6f,
            staticFriction = 0.6f
        };
        GetComponent<Collider>().material = material;
    }

    void OnCollisionEnter(Collision collision)
    {
        Debug.Log($"Hit: {collision.gameObject.name}");
    }
}

7. マルチプレイヤーアーキテクチャ

7.1 ネットワークモデル比較

モデル長所(ちょうしょ)短所(たんしょ)適(てき)するゲーム
P2Pサーバー費用ゼロNAT問題、チート協力(きょうりょく)インディー
Client-Server権威的、安定サーバー費用競争(きょうそう)マルチ
Dedicated Server最高の安定性最高(さいこう)に高価(こうか)AAA FPS、MOBA
Listen Serverホストがサーバーホスト退出(たいしゅつ)で終了カジュアル協力

7.2 Unity Netcode for GameObjects

using Unity.Netcode;
using UnityEngine;

public class NetworkPlayer : NetworkBehaviour
{
    [SerializeField] private float moveSpeed = 5f;

    private NetworkVariable<int> health = new NetworkVariable<int>(100,
        NetworkVariableReadPermission.Everyone,
        NetworkVariableWritePermission.Server);

    public override void OnNetworkSpawn()
    {
        if (IsOwner)
        {
            Camera.main.GetComponent<CameraFollow>().target = transform;
        }
        health.OnValueChanged += OnHealthChanged;
    }

    void Update()
    {
        if (!IsOwner) return;

        Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
        if (input.magnitude > 0.1f)
        {
            MoveServerRpc(input * moveSpeed * Time.deltaTime);
        }

        if (Input.GetKeyDown(KeyCode.Space))
        {
            ShootServerRpc();
        }
    }

    [ServerRpc]
    void MoveServerRpc(Vector3 movement)
    {
        transform.position += movement;
    }

    [ServerRpc]
    void ShootServerRpc()
    {
        var bullet = Instantiate(bulletPrefab, transform.position, transform.rotation);
        bullet.GetComponent<NetworkObject>().Spawn();
    }

    [ClientRpc]
    void TakeDamageClientRpc(int damage)
    {
        if (IsOwner)
        {
            ShowDamageUI(damage);
        }
    }

    void OnHealthChanged(int previous, int current)
    {
        UpdateHealthBar(current);
    }
}

7.3 Mirror Networking

using Mirror;
using UnityEngine;

public class MirrorPlayer : NetworkBehaviour
{
    [SyncVar(hook = nameof(OnHealthChanged))]
    public int health = 100;

    void OnHealthChanged(int oldHealth, int newHealth)
    {
        Debug.Log($"Health: {oldHealth} to {newHealth}");
    }

    void Update()
    {
        if (!isLocalPlayer) return;

        if (Input.GetKeyDown(KeyCode.Space))
        {
            CmdShoot();
        }
    }

    [Command]
    void CmdShoot()
    {
        RpcPlayShootEffect();
    }

    [ClientRpc]
    void RpcPlayShootEffect()
    {
        Instantiate(muzzleFlash, transform.position, transform.rotation);
    }
}

7.4 Photon vs Steamworks

  • Photon: クラウドベース、使いやすい、部分(ぶぶん)無料
  • Steamworks: Steam P2P、フレンド統合、Steamゲームの標準

8. 性能最適化

8.1 Draw Call最適化

public class StaticOptimizer : MonoBehaviour
{
    void Start()
    {
        StaticBatchingUtility.Combine(gameObject);
    }
}

public class InstancedRenderer : MonoBehaviour
{
    public Mesh mesh;
    public Material material;
    public int count = 10000;

    private Matrix4x4[] matrices;

    void Start()
    {
        matrices = new Matrix4x4[count];
        for (int i = 0; i < count; i++)
        {
            matrices[i] = Matrix4x4.TRS(
                Random.insideUnitSphere * 100,
                Quaternion.identity,
                Vector3.one
            );
        }
    }

    void Update()
    {
        Graphics.DrawMeshInstanced(mesh, 0, material, matrices, count);
    }
}

8.2 LOD

public class LODSetup : MonoBehaviour
{
    void Start()
    {
        LODGroup group = gameObject.AddComponent<LODGroup>();
        LOD[] lods = new LOD[3];

        lods[0] = new LOD(0.6f, GetRenderers(0));
        lods[1] = new LOD(0.3f, GetRenderers(1));
        lods[2] = new LOD(0.1f, GetRenderers(2));

        group.SetLODs(lods);
        group.RecalculateBounds();
    }

    Renderer[] GetRenderers(int level)
    {
        return new Renderer[] { transform.GetChild(level).GetComponent<Renderer>() };
    }
}

8.3 Occlusion Culling

壁(かべ)の後(うし)ろの見(み)えないオブジェクトを描画(びょうが)しない。Unityは自動ベイク、UnrealはVisibility Systemを使用。

8.4 テクスチャ最適化

形式(けいしき)モバイルPCコンソール
ASTCiOS、最新Android部分部分
ETC2Android非対応非対応
BC7非対応PC標準PS5、Xbox

9. アセットパイプライン

9.1 モデルインポートのベストプラクティス

  • ポリゴン数: キャラクター5k-50k、環境1k-10k
  • テクスチャ: 2のべき乗(512、1024、2048)
  • UV: 単一(たんいつ)チャネル、0-1範囲(はんい)内
  • ボーン: キャラクター50個以下(モバイル30個以下)

9.2 アニメーション

  • Unity: Animator State Machine、Animation Rigging
  • Unreal: Animation Blueprint、Control Rig、Live Link
  • Godot: AnimationPlayer、AnimationTree

10. オーディオ: FMOD vs Wwise

項目FMODWwise
価格インディー無料($150k以下)インディー無料($200k以下)
統合(とうごう)Unity、Unreal対応Unity、Unreal対応
UIシンプル複雑(ふくざつ)、強力
用途インディー、中規模AAA

11. プラットフォーム配信(はいしん)

11.1 モバイル (iOS / Android)

  • iOS: Xcodeビルド、App Store Connect、IDFA準拠(じゅんきょ)
  • Android: Google Play Console、App Bundle (.aab) 必須

11.2 PC (Steam / Epic / GOG)

  • Steam: Steamworks SDK、Steam Pipe、Workshop
  • Epic: Epic Online Services、Epic Games Store

11.3 コンソール (PS5 / Xbox / Switch)

  • 開発者(かいはつしゃ)登録(とうろく)必須
  • TRC/XR/Lotcheck認証(にんしょう)通過必要
  • ビルドにはdev kitが必要

12. インディー vs AAAワークフロー

項目インディーAAA
エンジンGodot, Unity, UnrealUnreal、自社製
ワークフロー多才(たさい)、迅速(じんそく)反復(はんぷく)分業(ぶんぎょう)、専門化(せんもんか)
アセットAsset Store、外注(がいちゅう)社内(しゃない)制作
マーケティングSNS、インフルエンサーTV、トレーラー、イベント

13. クイズ

Q1. ECSの核心(かくしん)的な利点(りてん)は?

A1. データ指向設計でキャッシュ効率を最大化(さいだいか)。コンポーネントがメモリに連続(れんぞく)配置(はいち)され、SIMDとマルチスレッドが効果的(こうかてき)。数万から数十万のエンティティを60fpsで処理可能。

Q2. Unreal Engine 5のLumenとNaniteの違い(ちがい)は?

A2. Lumenは動的グローバルイルミネーションシステムでリアルタイム間接照明と反射を処理。Naniteは仮想化ジオメトリシステムで数十億ポリゴンメッシュを自動LOD処理。Lumenは光、Naniteはメッシュ。

Q3. Draw Callが多(おお)いとなぜ性能が低下(ていか)するか?

A3. 各draw callはCPUからGPUへの命令(めいれい)送信(そうしん)オーバーヘッドがある。多すぎるとCPUバウンドになりGPUが遊(あそ)ぶ。バッチング、インスタンシング、テクスチャアトラスで削減(さくげん)する必要がある。

Q4. Server-authoritativeモデルの利点は?

A4. サーバーが全権威(ぜんけんい)を持(も)つため、クライアントのメモリ改変(かいへん)がゲーム状態に影響(えいきょう)を与(あた)えない。チート防止(ぼうし)に強いが遅延(ちえん)が増加(ぞうか)するためclient-side predictionが必要。

Q5. Static batchingとdynamic batchingの違いは?

A5. Static batchingはビルド時または開始時に動かないオブジェクトを単一(たんいつ)メッシュに統合(とうごう)。メモリは多く使うが実行(じっこう)時コストはゼロ。Dynamic batchingは毎フレーム小さな動的オブジェクトを統合する仕組(しく)みでCPUコストがある。

14. 参考資料

  1. Unity Documentation: https://docs.unity3d.com
  2. Unity DOTS: https://unity.com/dots
  3. Unity Learn: https://learn.unity.com
  4. Unreal Engine 5 Documentation: https://docs.unrealengine.com
  5. Unreal Engine - Lumen Technical Details: https://docs.unrealengine.com/5.3/en-US/lumen-global-illumination-and-reflections-in-unreal-engine
  6. Unreal Engine - Nanite Virtualized Geometry: https://docs.unrealengine.com/5.3/en-US/nanite-virtualized-geometry-in-unreal-engine
  7. Godot Documentation: https://docs.godotengine.org
  8. Godot 4 Tutorials: https://godotengine.org/learn
  9. Mirror Networking: https://mirror-networking.com
  10. Photon Engine: https://www.photonengine.com
  11. Game Programming Patterns: https://gameprogrammingpatterns.com
  12. GDC Vault: https://www.gdcvault.com
  13. Brackeys YouTube: https://www.youtube.com/@Brackeys
  14. Sebastian Lague YouTube: https://www.youtube.com/@SebastianLague

15. おわりに

ゲーム開発は技術と芸術(げいじゅつ)が出会(であ)う領域(りょういき)である。どのエンジンを選んでも、核心は**「プレイヤーが楽(たの)しい体験(たいけん)」**を作ることにある。Unityは多才(たさい)さと大きなコミュニティで依然インディーの標準であり、Unreal Engine 5は視覚的忠実度の新しい基準(きじゅん)を確立(かくりつ)し、Godot 4はオープンソース精神(せいしん)と軽量さで急速に成長している。

エンジンは道具(どうぐ)に過ぎない。ゲームデザイン、ゲームプレイループ、プレイヤー心理(しんり)の理解(りかい)がより重要である。小さなプロジェクトから始(はじ)めて、リリースまで完成(かんせい)する経験(けいけん)を積(つ)むことが最大の資産(しさん)である。