GeWuYou 9edf64193f docs(architecture): 更新架构设计模式指南文档
- 将标题从"架构模式最佳实践"改为"架构设计模式指南"
- 添加全面的架构设计模式介绍和概述
- 新增MVC模式详细说明,包括概念、GFramework实现示例和最佳实践
- 新增MVVM模式详细说明,包括概念、GFramework实现示例和最佳实践
- 新增命令模式详细说明,包括概念、实现示例和撤销功能支持
- 新增查询模式详细说明,包括CQRS概念和复杂查询示例
- 新增事件驱动模式详细说明,包括事件定义和监听实现
- 新增依赖注入模式详细说明,包括构造函数注入示例
- 新增服务定位器模式详细说明,包括与依赖注入对比
- 新增对象池模式详细说明,包括通用对象池实现
- 新增状态模式详细说明,包括异步状态和状态机系统
- 补充模式选择与组合建议,针对小型、中型、大型项目提供不同方案
- 更新代码示例中的泛型语法格式,统一使用尖括号表示法
2026-03-07 17:32:26 +08:00

15 KiB

多人游戏架构指南

基于 GFramework 架构设计高性能、可扩展的多人游戏系统。

📋 目录

概述

多人游戏开发面临着单机游戏所没有的独特挑战:

主要挑战

  1. 网络延迟 - 玩家操作和服务器响应之间存在不可避免的延迟
  2. 状态同步 - 确保所有客户端看到一致的游戏状态
  3. 带宽限制 - 需要高效传输游戏数据,避免网络拥塞
  4. 作弊防护 - 防止客户端篡改游戏逻辑和数据
  5. 并发处理 - 同时处理多个玩家的输入和状态更新
  6. 断线重连 - 优雅处理网络中断和玩家重新连接

GFramework 的优势

GFramework 的架构设计天然适合多人游戏开发:

  • 分层架构 - 清晰分离客户端逻辑、网络层和服务器逻辑
  • 事件系统 - 松耦合的事件驱动架构便于状态同步
  • 命令模式 - 统一的输入处理和验证机制
  • Model-System 分离 - 数据和逻辑分离便于状态管理
  • 模块化设计 - 网络功能可以作为独立模块集成

核心概念

1. 客户端-服务器架构

// 服务器架构
public class ServerArchitecture : Architecture
{
    protected override void Init()
    {
        // 注册服务器专用的 Model
        RegisterModel(new ServerGameStateModel());
        RegisterModel(new PlayerConnectionModel());

        // 注册服务器专用的 System
        RegisterSystem(new ServerNetworkSystem());
        RegisterSystem(new AuthorityGameLogicSystem());
        RegisterSystem(new StateReplicationSystem());
        RegisterSystem(new AntiCheatSystem());

        // 注册工具
        RegisterUtility(new NetworkUtility());
        RegisterUtility(new ValidationUtility());
    }
}

// 客户端架构
public class ClientArchitecture : Architecture
{
    protected override void Init()
    {
        // 注册客户端专用的 Model
        RegisterModel(new ClientGameStateModel());
        RegisterModel(new PredictionModel());

        // 注册客户端专用的 System
        RegisterSystem(new ClientNetworkSystem());
        RegisterSystem(new PredictionSystem());
        RegisterSystem(new InterpolationSystem());
        RegisterSystem(new ClientInputSystem());

        // 注册工具
        RegisterUtility(new NetworkUtility());
    }
}

2. 状态同步策略

状态同步 (State Synchronization)

服务器定期向客户端发送完整的游戏状态。

// 游戏状态快照
public struct GameStateSnapshot
{
    public uint Tick { get; set; }
    public long Timestamp { get; set; }
    public PlayerState[] Players { get; set; }
    public EntityState[] Entities { get; set; }
}

public struct PlayerState
{
    public string PlayerId { get; set; }
    public Vector3 Position { get; set; }
    public Quaternion Rotation { get; set; }
    public int Health { get; set; }
    public PlayerAnimationState AnimationState { get; set; }
}

// 状态复制系统
public class StateReplicationSystem : AbstractSystem
{
    private ServerGameStateModel _gameState;
    private PlayerConnectionModel _connections;
    private uint _currentTick;

    protected override void OnInit()
    {
        _gameState = this.GetModel<ServerGameStateModel>();
        _connections = this.GetModel<PlayerConnectionModel>();

        // 每个 tick 复制状态
        this.RegisterEvent<ServerTickEvent>(OnServerTick);
    }

    private void OnServerTick(ServerTickEvent e)
    {
        _currentTick++;

        // 创建状态快照
        var snapshot = CreateSnapshot();

        // 发送给所有连接的客户端
        foreach (var connection in _connections.ActiveConnections)
        {
            SendSnapshotToClient(connection, snapshot);
        }
    }

    private GameStateSnapshot CreateSnapshot()
    {
        return new GameStateSnapshot
        {
            Tick = _currentTick,
            Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
            Players = _gameState.Players.Select(p => new PlayerState
            {
                PlayerId = p.Id,
                Position = p.Position,
                Rotation = p.Rotation,
                Health = p.Health,
                AnimationState = p.AnimationState
            }).ToArray(),
            Entities = _gameState.Entities.Select(e => CreateEntityState(e)).ToArray()
        };
    }
}

增量同步 (Delta Synchronization)

只发送状态变化,减少带宽消耗。

// 增量状态
public struct DeltaState
{
    public uint Tick { get; set; }
    public uint BaseTick { get; set; }
    public PlayerDelta[] PlayerDeltas { get; set; }
    public EntityDelta[] EntityDeltas { get; set; }
}

public struct PlayerDelta
{
    public string PlayerId { get; set; }
    public DeltaFlags Flags { get; set; }
    public Vector3? Position { get; set; }
    public Quaternion? Rotation { get; set; }
    public int? Health { get; set; }
}

[Flags]
public enum DeltaFlags
{
    None = 0,
    Position = 1 << 0,
    Rotation = 1 << 1,
    Health = 1 << 2,
    Animation = 1 << 3
}

// 增量复制系统
public class DeltaReplicationSystem : AbstractSystem
{
    private readonly Dictionary<uint, GameStateSnapshot> _snapshotHistory = new();
    private const int MaxHistorySize = 60; // 保留 60 个快照

    protected override void OnInit()
    {
        this.RegisterEvent<ServerTickEvent>(OnServerTick);
    }

    private void OnServerTick(ServerTickEvent e)
    {
        var currentSnapshot = CreateSnapshot();
        _snapshotHistory[e.Tick] = currentSnapshot;

        // 清理旧快照
        CleanupOldSnapshots(e.Tick);

        // 为每个客户端生成增量
        foreach (var connection in GetConnections())
        {
            var lastAckedTick = connection.LastAcknowledgedTick;

            if (_snapshotHistory.TryGetValue(lastAckedTick, out var baseSnapshot))
            {
                var delta = CreateDelta(baseSnapshot, currentSnapshot);
                SendDeltaToClient(connection, delta);
            }
            else
            {
                // 客户端太落后,发送完整快照
                SendSnapshotToClient(connection, currentSnapshot);
            }
        }
    }

    private DeltaState CreateDelta(GameStateSnapshot baseSnapshot, GameStateSnapshot currentSnapshot)
    {
        var delta = new DeltaState
        {
            Tick = currentSnapshot.Tick,
            BaseTick = baseSnapshot.Tick,
            PlayerDeltas = new List<PlayerDelta>()
        };

        // 比较玩家状态
        foreach (var currentPlayer in currentSnapshot.Players)
        {
            var basePlayer = baseSnapshot.Players.FirstOrDefault(p => p.PlayerId == currentPlayer.PlayerId);

            if (basePlayer.PlayerId == null)
            {
                // 新玩家,发送完整状态
                delta.PlayerDeltas.Add(CreateFullPlayerDelta(currentPlayer));
            }
            else
            {
                // 计算差异
                var playerDelta = CreatePlayerDelta(basePlayer, currentPlayer);
                if (playerDelta.Flags != DeltaFlags.None)
                {
                    delta.PlayerDeltas.Add(playerDelta);
                }
            }
        }

        return delta;
    }

    private PlayerDelta CreatePlayerDelta(PlayerState baseState, PlayerState currentState)
    {
        var delta = new PlayerDelta { PlayerId = currentState.PlayerId };

        if (Vector3.Distance(baseState.Position, currentState.Position) > 0.01f)
        {
            delta.Flags |= DeltaFlags.Position;
            delta.Position = currentState.Position;
        }

        if (Quaternion.Angle(baseState.Rotation, currentState.Rotation) > 0.1f)
        {
            delta.Flags |= DeltaFlags.Rotation;
            delta.Rotation = currentState.Rotation;
        }

        if (baseState.Health != currentState.Health)
        {
            delta.Flags |= DeltaFlags.Health;
            delta.Health = currentState.Health;
        }

        return delta;
    }
}

3. 客户端预测与回滚

客户端立即响应玩家输入,然后在收到服务器确认后进行校正。

// 输入命令
public struct PlayerInputCommand
{
    public uint Tick { get; set; }
    public long Timestamp { get; set; }
    public Vector2 MoveDirection { get; set; }
    public Vector2 LookDirection { get; set; }
    public InputFlags Flags { get; set; }
}

[Flags]
public enum InputFlags
{
    None = 0,
    Jump = 1 << 0,
    Attack = 1 << 1,
    Interact = 1 << 2,
    Reload = 1 << 3
}

// 客户端预测系统
public class ClientPredictionSystem : AbstractSystem
{
    private PredictionModel _prediction;
    private ClientGameStateModel _gameState;
    private readonly Queue<PlayerInputCommand> _pendingInputs = new();
    private uint _lastProcessedServerTick;

    protected override void OnInit()
    {
        _prediction = this.GetModel<PredictionModel>();
        _gameState = this.GetModel<ClientGameStateModel>();

        this.RegisterEvent<PlayerInputEvent>(OnPlayerInput);
        this.RegisterEvent<ServerStateReceivedEvent>(OnServerStateReceived);
    }

    private void OnPlayerInput(PlayerInputEvent e)
    {
        var input = new PlayerInputCommand
        {
            Tick = _prediction.CurrentTick,
            Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
            MoveDirection = e.MoveDirection,
            LookDirection = e.LookDirection,
            Flags = e.Flags
        };

        // 保存输入用于重放
        _pendingInputs.Enqueue(input);

        // 立即应用预测
        ApplyInput(input);

        // 发送到服务器
        SendInputToServer(input);
    }

    private void OnServerStateReceived(ServerStateReceivedEvent e)
    {
        _lastProcessedServerTick = e.Snapshot.Tick;

        // 应用服务器状态
        ApplyServerState(e.Snapshot);

        // 移除已确认的输入
        while (_pendingInputs.Count > 0 && _pendingInputs.Peek().Tick <= e.Snapshot.Tick)
        {
            _pendingInputs.Dequeue();
        }

        // 重放未确认的输入
        ReplayPendingInputs();
    }

    private void ApplyInput(PlayerInputCommand input)
    {
        var player = _gameState.LocalPlayer;

        // 应用移动
        var movement = input.MoveDirection * player.Speed * Time.DeltaTime;
        player.Position += new Vector3(movement.X, 0, movement.Y);

        // 应用旋转
        if (input.LookDirection != Vector2.Zero)
        {
            player.Rotation = Quaternion.LookRotation(
                new Vector3(input.LookDirection.X, 0, input.LookDirection.Y)
            );
        }

        // 应用动作
        if ((input.Flags & InputFlags.Jump) != 0 && player.IsGrounded)
        {
            player.Velocity = new Vector3(player.Velocity.X, player.JumpForce, player.Velocity.Z);
        }
    }

    private void ReplayPendingInputs()
    {
        // 从服务器状态开始重放所有未确认的输入
        var savedState = SavePlayerState();

        foreach (var input in _pendingInputs)
        {
            ApplyInput(input);
        }
    }
}

架构设计

1. 分离逻辑层

// 共享游戏逻辑 (客户端和服务器都使用)
public class SharedGameLogic
{
    public static void ProcessMovement(PlayerState player, Vector2 moveDirection, float deltaTime)
    {
        var movement = moveDirection.Normalized() * player.Speed * deltaTime;
        player.Position += new Vector3(movement.X, 0, movement.Y);
    }

    public static bool CanJump(PlayerState player)
    {
        return player.IsGrounded && !player.IsStunned;
    }

    public static int CalculateDamage(int attackPower, int defense, float criticalChance)
    {
        var baseDamage = Math.Max(1, attackPower - defense);
        var isCritical = Random.Shared.NextDouble() < criticalChance;
        return isCritical ? baseDamage * 2 : baseDamage;
    }
}

// 服务器权威逻辑
public class ServerGameLogicSystem : AbstractSystem
{
    private ServerGameStateModel _gameState;

    protected override void OnInit()
    {
        _gameState = this.GetModel<ServerGameStateModel>();

        this.RegisterEvent<PlayerInputReceivedEvent>(OnPlayerInputReceived);
        this.RegisterEvent<AttackRequestEvent>(OnAttackRequest);
    }

    private void OnPlayerInputReceived(PlayerInputReceivedEvent e)
    {
        var player = _gameState.GetPlayer(e.PlayerId);

        // 验证输入
        if (!ValidateInput(e.Input))
        {
            SendInputRejection(e.PlayerId, "Invalid input");
            return;
        }

        // 应用共享逻辑
        SharedGameLogic.ProcessMovement(player, e.Input.MoveDirection, Time.DeltaTime);

        // 服务器端验证
        if ((e.Input.Flags & InputFlags.Jump) != 0)
        {
            if (SharedGameLogic.CanJump(player))
            {
                player.Velocity = new Vector3(player.Velocity.X, player.JumpForce, player.Velocity.Z);
            }
        }
    }

    private void OnAttackRequest(AttackRequestEvent e)
    {
        var attacker = _gameState.GetPlayer(e.AttackerId);
        var target = _gameState.GetPlayer(e.TargetId);

        // 服务器端验证
        if (!CanAttack(attacker, target))
        {
            return;
        }

        // 计算伤害
        var damage = SharedGameLogic.CalculateDamage(
            attacker.AttackPower,
            target.Defense,
            attacker.CriticalChance
        );

        // 应用伤害
        target.Health = Math.Max(0, target.Health - damage);

        // 广播事件
        this.SendEvent(new PlayerDamagedEvent
        {
            AttackerId = e.AttackerId,
            TargetId = e.TargetId,
            Damage = damage,
            RemainingHealth = target.Health
        });

        if (target.Health == 0)
        {
            this.SendEvent(new PlayerDiedEvent
            {
                PlayerId = e.TargetId,
                KillerId = e.AttackerId
            });
        }
    }
}

// 客户端表现逻辑
public class ClientPresentationSystem : AbstractSystem
{
    protected override void OnInit()
    {
        this.RegisterEvent<PlayerDamagedEvent>(OnPlayerDamaged);
        this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
    }

    private void OnPlayerDamaged(PlayerDamagedEvent e)
    {
        // 播放受击特效
        PlayDamageEffect(e.TargetId, e.Damage);

        // 播放受击音效
        PlayDamageSound(e.TargetId);

        // 更新 UI
        UpdateHealthBar(e.TargetId, e.RemainingHealth);
    }

    private void OnPlayerDied(PlayerDiedEvent e)
    {
        // 播放死亡动画
        PlayDeathAnimation(e.PlayerId);

        // 播放死亡音效
        PlayDeathSound(e.PlayerId);

        // 显示击杀提示
        ShowKillFeed(e.KillerId, e.PlayerId);
    }
}