mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
- 将标题从"架构模式最佳实践"改为"架构设计模式指南" - 添加全面的架构设计模式介绍和概述 - 新增MVC模式详细说明,包括概念、GFramework实现示例和最佳实践 - 新增MVVM模式详细说明,包括概念、GFramework实现示例和最佳实践 - 新增命令模式详细说明,包括概念、实现示例和撤销功能支持 - 新增查询模式详细说明,包括CQRS概念和复杂查询示例 - 新增事件驱动模式详细说明,包括事件定义和监听实现 - 新增依赖注入模式详细说明,包括构造函数注入示例 - 新增服务定位器模式详细说明,包括与依赖注入对比 - 新增对象池模式详细说明,包括通用对象池实现 - 新增状态模式详细说明,包括异步状态和状态机系统 - 补充模式选择与组合建议,针对小型、中型、大型项目提供不同方案 - 更新代码示例中的泛型语法格式,统一使用尖括号表示法
15 KiB
15 KiB
多人游戏架构指南
基于 GFramework 架构设计高性能、可扩展的多人游戏系统。
📋 目录
概述
多人游戏开发面临着单机游戏所没有的独特挑战:
主要挑战
- 网络延迟 - 玩家操作和服务器响应之间存在不可避免的延迟
- 状态同步 - 确保所有客户端看到一致的游戏状态
- 带宽限制 - 需要高效传输游戏数据,避免网络拥塞
- 作弊防护 - 防止客户端篡改游戏逻辑和数据
- 并发处理 - 同时处理多个玩家的输入和状态更新
- 断线重连 - 优雅处理网络中断和玩家重新连接
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);
}
}