游戏循环及实时模拟
游戏循环是游戏程序的核心,它负责持续更新游戏状态并将结果渲染到屏幕上。本章将详细介绍游戏循环的设计与实现,以及在实时游戏中的各种应用技术。
7.1 渲染循环
渲染循环是游戏引擎的心脏,负责将游戏世界的状态转换为玩家可见的画面。一个高效的渲染循环对于提供流畅的游戏体验至关重要。
基本渲染循环结构
csharp
public class GameLoop : MonoBehaviour
{
private float targetFrameRate = 60.0f;
private float targetFrameTime;
void Start()
{
// 设置目标帧率
targetFrameTime = 1.0f / targetFrameRate;
Application.targetFrameRate = (int)targetFrameRate;
}
void Update()
{
// 游戏逻辑更新
UpdateGameLogic(Time.deltaTime);
// 输入处理
ProcessInput();
// 物理模拟(如果不使用FixedUpdate)
UpdatePhysicsSimulation(Time.deltaTime);
}
void LateUpdate()
{
// 相机更新
UpdateCameras();
// UI更新
UpdateUI();
}
void OnRenderObject()
{
// 自定义渲染
CustomRender();
}
}
高级渲染循环实现
csharp
public class AdvancedRenderLoop : MonoBehaviour
{
// 渲染统计
private class RenderStats
{
public int frameCount;
public float totalFrameTime;
public float minFrameTime = float.MaxValue;
public float maxFrameTime = float.MinValue;
public float averageFPS;
public void Update(float frameTime)
{
frameCount++;
totalFrameTime += frameTime;
minFrameTime = Mathf.Min(minFrameTime, frameTime);
maxFrameTime = Mathf.Max(maxFrameTime, frameTime);
averageFPS = frameCount / totalFrameTime;
}
}
private RenderStats stats = new RenderStats();
private float lastFrameTime;
// 渲染队列
private Queue<RenderCommand> renderQueue = new Queue<RenderCommand>();
// 渲染命令基类
public abstract class RenderCommand
{
public abstract void Execute();
}
// 具体渲染命令示例
public class DrawMeshCommand : RenderCommand
{
public Mesh mesh;
public Material material;
public Matrix4x4 matrix;
public override void Execute()
{
Graphics.DrawMesh(mesh, matrix, material, 0);
}
}
void Update()
{
float currentTime = Time.realtimeSinceStartup;
float frameTime = currentTime - lastFrameTime;
lastFrameTime = currentTime;
// 更新统计信息
stats.Update(frameTime);
// 性能监控
if (frameTime > targetFrameTime * 1.5f)
{
Debug.LogWarning($"Frame time spike: {frameTime * 1000}ms");
}
// 处理渲染队列
ProcessRenderQueue();
}
private void ProcessRenderQueue()
{
int commandCount = renderQueue.Count;
for (int i = 0; i < commandCount; i++)
{
RenderCommand command = renderQueue.Dequeue();
command.Execute();
}
}
public void EnqueueRenderCommand(RenderCommand command)
{
renderQueue.Enqueue(command);
}
}
多线程渲染优化
csharp
public class MultiThreadedRenderer : MonoBehaviour
{
private readonly object renderLock = new object();
private List<RenderBatch> renderBatches = new List<RenderBatch>();
private Thread renderThread;
private bool isRunning = true;
public class RenderBatch
{
public List<Matrix4x4> matrices = new List<Matrix4x4>();
public Mesh mesh;
public Material material;
public void Render()
{
if (matrices.Count > 0)
{
Graphics.DrawMeshInstanced(mesh, 0, material, matrices);
}
}
}
void Start()
{
// 启动渲染线程
renderThread = new Thread(RenderThreadFunction);
renderThread.Start();
}
void RenderThreadFunction()
{
while (isRunning)
{
List<RenderBatch> batchesToRender = null;
lock (renderLock)
{
if (renderBatches.Count > 0)
{
batchesToRender = new List<RenderBatch>(renderBatches);
renderBatches.Clear();
}
}
if (batchesToRender != null)
{
foreach (var batch in batchesToRender)
{
batch.Render();
}
}
Thread.Sleep(1); // 避免CPU占用过高
}
}
public void AddRenderBatch(RenderBatch batch)
{
lock (renderLock)
{
renderBatches.Add(batch);
}
}
void OnDestroy()
{
isRunning = false;
renderThread?.Join();
}
}
渲染优化技术
csharp
public class RenderOptimizer : MonoBehaviour
{
[System.Serializable]
public class LODSettings
{
public float[] distances = { 10f, 25f, 50f, 100f };
public int[] qualityLevels = { 3, 2, 1, 0 };
}
public LODSettings lodSettings;
private Camera mainCamera;
private Dictionary<Renderer, int> currentLODLevels = new Dictionary<Renderer, int>();
// 视锥体剔除
public class FrustumCuller
{
private Plane[] frustumPlanes;
public void UpdateFrustumPlanes(Camera camera)
{
frustumPlanes = GeometryUtility.CalculateFrustumPlanes(camera);
}
public bool IsVisible(Bounds bounds)
{
return GeometryUtility.TestPlanesAABB(frustumPlanes, bounds);
}
}
private FrustumCuller frustumCuller = new FrustumCuller();
// 遮挡剔除
public class OcclusionCuller
{
private RenderTexture depthTexture;
private ComputeShader occlusionShader;
public void Initialize(int width, int height)
{
depthTexture = new RenderTexture(width, height, 24,
RenderTextureFormat.Depth);
// 加载遮挡剔除计算着色器
occlusionShader = Resources.Load<ComputeShader>("OcclusionCulling");
}
public bool IsOccluded(Bounds bounds)
{
// 实现遮挡测试逻辑
// 这里简化处理,实际需要复杂的GPU计算
return false;
}
}
void Start()
{
mainCamera = Camera.main;
}
void Update()
{
// 更新视锥体平面
frustumCuller.UpdateFrustumPlanes(mainCamera);
// 处理场景中的所有渲染器
Renderer[] renderers = FindObjectsOfType<Renderer>();
foreach (var renderer in renderers)
{
// 视锥体剔除
if (!frustumCuller.IsVisible(renderer.bounds))
{
renderer.enabled = false;
continue;
}
renderer.enabled = true;
// LOD处理
UpdateLOD(renderer);
// 批处理优化
OptimizeBatching(renderer);
}
}
private void UpdateLOD(Renderer renderer)
{
float distance = Vector3.Distance(mainCamera.transform.position,
renderer.transform.position);
int lodLevel = 0;
for (int i = 0; i < lodSettings.distances.Length; i++)
{
if (distance > lodSettings.distances[i])
{
lodLevel = i;
}
}
// 应用LOD级别
if (currentLODLevels.ContainsKey(renderer))
{
if (currentLODLevels[renderer] != lodLevel)
{
ApplyLODLevel(renderer, lodLevel);
currentLODLevels[renderer] = lodLevel;
}
}
else
{
currentLODLevels.Add(renderer, lodLevel);
ApplyLODLevel(renderer, lodLevel);
}
}
private void ApplyLODLevel(Renderer renderer, int level)
{
// 应用LOD设置
// 例如:切换模型、调整着色器质量等
LODGroup lodGroup = renderer.GetComponent<LODGroup>();
if (lodGroup != null)
{
// Unity内置LOD系统会自动处理
}
}
private void OptimizeBatching(Renderer renderer)
{
// 动态批处理优化
if (renderer.sharedMaterial != null)
{
// 确保使用共享材质
renderer.material = renderer.sharedMaterial;
}
// 设置合适的静态标记
if (!renderer.GetComponent<Rigidbody>())
{
renderer.gameObject.isStatic = true;
}
}
}
7.2 游戏循环
游戏循环是游戏运行的主体框架,负责协调游戏中的各个系统,确保游戏状态的正确更新和渲染。
固定时间步长游戏循环
csharp
public class FixedTimeStepGameLoop : MonoBehaviour
{
public float fixedDeltaTime = 0.016667f; // 60 FPS
private float accumulator = 0.0f;
private float currentTime = 0.0f;
private float frameStart;
// 游戏状态
public class GameState
{
public Vector3 playerPosition;
public Quaternion playerRotation;
public float gameTime;
public GameState Clone()
{
return new GameState
{
playerPosition = playerPosition,
playerRotation = playerRotation,
gameTime = gameTime
};
}
public static GameState Interpolate(GameState a, GameState b, float t)
{
return new GameState
{
playerPosition = Vector3.Lerp(a.playerPosition, b.playerPosition, t),
playerRotation = Quaternion.Slerp(a.playerRotation, b.playerRotation, t),
gameTime = Mathf.Lerp(a.gameTime, b.gameTime, t)
};
}
}
private GameState previousState = new GameState();
private GameState currentState = new GameState();
void Start()
{
frameStart = Time.realtimeSinceStartup;
Time.fixedDeltaTime = fixedDeltaTime;
}
void Update()
{
float frameTime = Time.realtimeSinceStartup - frameStart;
frameStart = Time.realtimeSinceStartup;
// 防止螺旋死亡
frameTime = Mathf.Min(frameTime, 0.25f);
accumulator += frameTime;
// 固定时间步长更新
while (accumulator >= fixedDeltaTime)
{
previousState = currentState.Clone();
// 更新游戏逻辑
UpdateGameLogic(fixedDeltaTime);
currentTime += fixedDeltaTime;
accumulator -= fixedDeltaTime;
}
// 插值渲染
float interpolation = accumulator / fixedDeltaTime;
GameState interpolatedState = GameState.Interpolate(previousState,
currentState, interpolation);
// 渲染插值状态
RenderGameState(interpolatedState);
}
private void UpdateGameLogic(float deltaTime)
{
// 更新物理
UpdatePhysics(deltaTime);
// 更新AI
UpdateAI(deltaTime);
// 更新游戏规则
UpdateGameRules(deltaTime);
currentState.gameTime = currentTime;
}
private void UpdatePhysics(float deltaTime)
{
// 物理更新逻辑
}
private void UpdateAI(float deltaTime)
{
// AI更新逻辑
}
private void UpdateGameRules(float deltaTime)
{
// 游戏规则更新
}
private void RenderGameState(GameState state)
{
// 根据插值状态渲染
transform.position = state.playerPosition;
transform.rotation = state.playerRotation;
}
}
变时间步长游戏循环
csharp
public class VariableTimeStepGameLoop : MonoBehaviour
{
private float maxDeltaTime = 0.1f; // 最大时间步长
private float timeScale = 1.0f;
// 时间管理器
public class TimeManager
{
public float deltaTime { get; private set; }
public float unscaledDeltaTime { get; private set; }
public float time { get; private set; }
public float unscaledTime { get; private set; }
public float timeScale = 1.0f;
private float lastFrameTime;
public void Update()
{
float currentTime = Time.realtimeSinceStartup;
unscaledDeltaTime = currentTime - lastFrameTime;
unscaledDeltaTime = Mathf.Min(unscaledDeltaTime, 0.1f); // 防止过大
deltaTime = unscaledDeltaTime * timeScale;
time += deltaTime;
unscaledTime += unscaledDeltaTime;
lastFrameTime = currentTime;
}
public void SetTimeScale(float scale)
{
timeScale = Mathf.Max(0, scale);
}
}
private TimeManager timeManager = new TimeManager();
// 子系统更新优先级
private enum UpdatePriority
{
Input = 0,
Physics = 1,
AI = 2,
Animation = 3,
Rendering = 4
}
private class UpdateSystem
{
public UpdatePriority priority;
public System.Action<float> updateAction;
public bool isActive = true;
}
private List<UpdateSystem> updateSystems = new List<UpdateSystem>();
void Start()
{
// 注册更新系统
RegisterUpdateSystem(UpdatePriority.Input, UpdateInput);
RegisterUpdateSystem(UpdatePriority.Physics, UpdatePhysics);
RegisterUpdateSystem(UpdatePriority.AI, UpdateAI);
RegisterUpdateSystem(UpdatePriority.Animation, UpdateAnimation);
RegisterUpdateSystem(UpdatePriority.Rendering, UpdateRendering);
// 按优先级排序
updateSystems.Sort((a, b) => a.priority.CompareTo(b.priority));
}
void Update()
{
// 更新时间
timeManager.Update();
// 执行所有活跃的更新系统
foreach (var system in updateSystems)
{
if (system.isActive)
{
system.updateAction(timeManager.deltaTime);
}
}
// 性能监控
MonitorPerformance();
}
private void RegisterUpdateSystem(UpdatePriority priority,
System.Action<float> updateAction)
{
updateSystems.Add(new UpdateSystem
{
priority = priority,
updateAction = updateAction,
isActive = true
});
}
private void UpdateInput(float deltaTime)
{
// 处理输入
}
private void UpdatePhysics(float deltaTime)
{
// 物理更新
// 可能需要细分为多个固定步长
float fixedDeltaTime = 0.02f;
float physicsAccumulator = deltaTime;
while (physicsAccumulator >= fixedDeltaTime)
{
// 执行固定步长物理
Physics.Simulate(fixedDeltaTime);
physicsAccumulator -= fixedDeltaTime;
}
}
private void UpdateAI(float deltaTime)
{
// AI更新
}
private void UpdateAnimation(float deltaTime)
{
// 动画更新
}
private void UpdateRendering(float deltaTime)
{
// 渲染准备
}
private void MonitorPerformance()
{
if (timeManager.deltaTime > maxDeltaTime)
{
Debug.LogWarning($"Frame time exceeded max: {timeManager.deltaTime}s");
}
}
}
7.3 游戏循环的架构风格
游戏循环的架构设计直接影响游戏的性能、可维护性和扩展性。
组件化游戏循环架构
csharp
public class ComponentBasedGameLoop : MonoBehaviour
{
// 游戏组件接口
public interface IGameComponent
{
bool Enabled { get; set; }
int UpdateOrder { get; }
void Initialize();
void Update(float deltaTime);
void Destroy();
}
// 可绘制组件接口
public interface IDrawableComponent : IGameComponent
{
int DrawOrder { get; }
void Draw();
}
// 组件管理器
public class ComponentManager
{
private List<IGameComponent> components = new List<IGameComponent>();
private List<IDrawableComponent> drawableComponents = new List<IDrawableComponent>();
private bool isInitialized = false;
public void AddComponent(IGameComponent component)
{
components.Add(component);
if (component is IDrawableComponent drawable)
{
drawableComponents.Add(drawable);
}
if (isInitialized)
{
component.Initialize();
}
// 重新排序
SortComponents();
}
public void RemoveComponent(IGameComponent component)
{
component.Destroy();
components.Remove(component);
if (component is IDrawableComponent drawable)
{
drawableComponents.Remove(drawable);
}
}
public void Initialize()
{
foreach (var component in components)
{
component.Initialize();
}
isInitialized = true;
}
public void Update(float deltaTime)
{
foreach (var component in components)
{
if (component.Enabled)
{
component.Update(deltaTime);
}
}
}
public void Draw()
{
foreach (var drawable in drawableComponents)
{
if (drawable.Enabled)
{
drawable.Draw();
}
}
}
private void SortComponents()
{
components.Sort((a, b) => a.UpdateOrder.CompareTo(b.UpdateOrder));
drawableComponents.Sort((a, b) => a.DrawOrder.CompareTo(b.DrawOrder));
}
}
// 示例组件:输入系统
public class InputComponent : IGameComponent
{
public bool Enabled { get; set; } = true;
public int UpdateOrder => 0; // 最先更新
public void Initialize()
{
Debug.Log("Input system initialized");
}
public void Update(float deltaTime)
{
// 处理输入
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.Log("Space pressed");
}
}
public void Destroy()
{
Debug.Log("Input system destroyed");
}
}
// 示例组件:物理系统
public class PhysicsComponent : IGameComponent
{
public bool Enabled { get; set; } = true;
public int UpdateOrder => 1;
private float accumulator = 0f;
private const float FixedTimeStep = 0.02f;
public void Initialize()
{
Physics.autoSimulation = false;
}
public void Update(float deltaTime)
{
accumulator += deltaTime;
while (accumulator >= FixedTimeStep)
{
Physics.Simulate(FixedTimeStep);
accumulator -= FixedTimeStep;
}
}
public void Destroy()
{
Physics.autoSimulation = true;
}
}
private ComponentManager componentManager = new ComponentManager();
void Start()
{
// 添加游戏组件
componentManager.AddComponent(new InputComponent());
componentManager.AddComponent(new PhysicsComponent());
// 初始化所有组件
componentManager.Initialize();
}
void Update()
{
componentManager.Update(Time.deltaTime);
}
void LateUpdate()
{
componentManager.Draw();
}
}
事件驱动游戏循环
csharp
public class EventDrivenGameLoop : MonoBehaviour
{
// 事件系统
public class EventSystem
{
private Dictionary<System.Type, System.Delegate> eventHandlers =
new Dictionary<System.Type, System.Delegate>();
public void Subscribe<T>(System.Action<T> handler) where T : IGameEvent
{
System.Type eventType = typeof(T);
if (eventHandlers.ContainsKey(eventType))
{
eventHandlers[eventType] =
System.Delegate.Combine(eventHandlers[eventType], handler);
}
else
{
eventHandlers[eventType] = handler;
}
}
public void Unsubscribe<T>(System.Action<T> handler) where T : IGameEvent
{
System.Type eventType = typeof(T);
if (eventHandlers.ContainsKey(eventType))
{
eventHandlers[eventType] =
System.Delegate.Remove(eventHandlers[eventType], handler);
}
}
public void Publish<T>(T gameEvent) where T : IGameEvent
{
System.Type eventType = typeof(T);
if (eventHandlers.ContainsKey(eventType))
{
var handler = eventHandlers[eventType] as System.Action<T>;
handler?.Invoke(gameEvent);
}
}
}
// 游戏事件接口
public interface IGameEvent
{
float Timestamp { get; }
}
// 具体事件示例
public class InputEvent : IGameEvent
{
public float Timestamp { get; }
public KeyCode Key { get; }
public bool IsPressed { get; }
public InputEvent(KeyCode key, bool isPressed)
{
Timestamp = Time.time;
Key = key;
IsPressed = isPressed;
}
}
public class CollisionEvent : IGameEvent
{
public float Timestamp { get; }
public GameObject Object1 { get; }
public GameObject Object2 { get; }
public Vector3 CollisionPoint { get; }
public CollisionEvent(GameObject obj1, GameObject obj2, Vector3 point)
{
Timestamp = Time.time;
Object1 = obj1;
Object2 = obj2;
CollisionPoint = point;
}
}
public class GameStateChangeEvent : IGameEvent
{
public float Timestamp { get; }
public string OldState { get; }
public string NewState { get; }
public GameStateChangeEvent(string oldState, string newState)
{
Timestamp = Time.time;
OldState = oldState;
NewState = newState;
}
}
private EventSystem eventSystem = new EventSystem();
// 游戏状态机
public class GameStateMachine
{
private EventSystem eventSystem;
private string currentState = "Menu";
public GameStateMachine(EventSystem eventSystem)
{
this.eventSystem = eventSystem;
}
public void ChangeState(string newState)
{
string oldState = currentState;
currentState = newState;
eventSystem.Publish(new GameStateChangeEvent(oldState, newState));
}
}
private GameStateMachine stateMachine;
void Start()
{
stateMachine = new GameStateMachine(eventSystem);
// 订阅事件
eventSystem.Subscribe<InputEvent>(OnInputEvent);
eventSystem.Subscribe<CollisionEvent>(OnCollisionEvent);
eventSystem.Subscribe<GameStateChangeEvent>(OnStateChange);
}
void Update()
{
// 检测输入并发布事件
if (Input.GetKeyDown(KeyCode.Space))
{
eventSystem.Publish(new InputEvent(KeyCode.Space, true));
}
// 主游戏循环逻辑
UpdateGameSystems();
}
private void UpdateGameSystems()
{
// 各系统独立更新,通过事件通信
}
private void OnInputEvent(InputEvent e)
{
Debug.Log($"Input event: {e.Key} pressed at {e.Timestamp}");
}
private void OnCollisionEvent(CollisionEvent e)
{
Debug.Log($"Collision between {e.Object1.name} and {e.Object2.name}");
}
private void OnStateChange(GameStateChangeEvent e)
{
Debug.Log($"State changed from {e.OldState} to {e.NewState}");
}
}
多线程游戏循环架构
csharp
public class MultiThreadedGameLoop : MonoBehaviour
{
// 线程安全的任务队列
public class ThreadSafeTaskQueue<T>
{
private Queue<T> queue = new Queue<T>();
private object lockObject = new object();
public void Enqueue(T item)
{
lock (lockObject)
{
queue.Enqueue(item);
}
}
public bool TryDequeue(out T item)
{
lock (lockObject)
{
if (queue.Count > 0)
{
item = queue.Dequeue();
return true;
}
item = default(T);
return false;
}
}
public int Count
{
get
{
lock (lockObject)
{
return queue.Count;
}
}
}
}
// 工作线程任务
public abstract class WorkerTask
{
public abstract void Execute();
}
// AI计算任务
public class AICalculationTask : WorkerTask
{
public GameObject target;
public Vector3 result;
public override void Execute()
{
// 执行AI计算(pathfinding等)
// 这里简化处理
result = target.transform.position + Random.insideUnitSphere * 5;
}
}
// 物理预测任务
public class PhysicsPredictionTask : WorkerTask
{
public Rigidbody rigidbody;
public float timeStep;
public Vector3 predictedPosition;
public override void Execute()
{
// 预测物理对象位置
predictedPosition = rigidbody.position +
rigidbody.velocity * timeStep;
}
}
// 工作线程池
public class WorkerThreadPool
{
private Thread[] threads;
private ThreadSafeTaskQueue<WorkerTask> taskQueue;
private bool isRunning = true;
public WorkerThreadPool(int threadCount)
{
taskQueue = new ThreadSafeTaskQueue<WorkerTask>();
threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
threads[i] = new Thread(WorkerThreadFunction)
{
Name = $"WorkerThread_{i}"
};
threads[i].Start();
}
}
private void WorkerThreadFunction()
{
while (isRunning)
{
WorkerTask task;
if (taskQueue.TryDequeue(out task))
{
try
{
task.Execute();
}
catch (Exception e)
{
Debug.LogError($"Worker task error: {e}");
}
}
else
{
Thread.Sleep(1);
}
}
}
public void QueueTask(WorkerTask task)
{
taskQueue.Enqueue(task);
}
public void Shutdown()
{
isRunning = false;
foreach (var thread in threads)
{
thread.Join();
}
}
}
private WorkerThreadPool threadPool;
private ThreadSafeTaskQueue<WorkerTask> completedTasks;
void Start()
{
int workerCount = SystemInfo.processorCount - 1; // 留一个给主线程
threadPool = new WorkerThreadPool(Mathf.Max(1, workerCount));
completedTasks = new ThreadSafeTaskQueue<WorkerTask>();
}
void Update()
{
// 主线程:分发任务
DispatchTasks();
// 主线程:处理完成的任务
ProcessCompletedTasks();
// 主线程:更新Unity对象
UpdateUnityObjects();
}
private void DispatchTasks()
{
// 分发AI任务
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
foreach (var enemy in enemies)
{
var task = new AICalculationTask { target = enemy };
threadPool.QueueTask(task);
}
}
private void ProcessCompletedTasks()
{
WorkerTask task;
while (completedTasks.TryDequeue(out task))
{
if (task is AICalculationTask aiTask)
{
// 应用AI计算结果
aiTask.target.transform.position =
Vector3.Lerp(aiTask.target.transform.position,
aiTask.result, Time.deltaTime);
}
}
}
private void UpdateUnityObjects()
{
// 只在主线程更新Unity对象
}
void OnDestroy()
{
threadPool?.Shutdown();
}
}
7.4 抽象时间线
抽象时间线允许游戏中不同系统以不同的时间速率运行,实现慢动作、快进等效果。
时间线系统实现
csharp
public class TimelineSystem : MonoBehaviour
{
// 时间线定义
public class Timeline
{
public string name;
public float timeScale = 1.0f;
public float currentTime = 0.0f;
public bool isPaused = false;
private List<TimelineEvent> events = new List<TimelineEvent>();
private List<ITimelineUpdateable> updateables = new List<ITimelineUpdateable>();
public void Update(float deltaTime)
{
if (!isPaused)
{
float scaledDelta = deltaTime * timeScale;
currentTime += scaledDelta;
// 更新所有可更新对象
foreach (var updateable in updateables)
{
updateable.UpdateTimeline(scaledDelta);
}
// 处理时间线事件
ProcessEvents();
}
}
public void RegisterUpdateable(ITimelineUpdateable updateable)
{
if (!updateables.Contains(updateable))
{
updateables.Add(updateable);
}
}
public void UnregisterUpdateable(ITimelineUpdateable updateable)
{
updateables.Remove(updateable);
}
public void ScheduleEvent(TimelineEvent timelineEvent)
{
events.Add(timelineEvent);
events.Sort((a, b) => a.triggerTime.CompareTo(b.triggerTime));
}
private void ProcessEvents()
{
while (events.Count > 0 && events[0].triggerTime <= currentTime)
{
TimelineEvent evt = events[0];
events.RemoveAt(0);
evt.Execute();
}
}
}
// 时间线事件
public abstract class TimelineEvent
{
public float triggerTime;
public abstract void Execute();
}
// 可更新接口
public interface ITimelineUpdateable
{
void UpdateTimeline(float deltaTime);
}
// 时间线管理器
private Dictionary<string, Timeline> timelines = new Dictionary<string, Timeline>();
private Timeline globalTimeline;
void Start()
{
// 创建全局时间线
globalTimeline = new Timeline { name = "Global" };
timelines["Global"] = globalTimeline;
// 创建其他时间线
CreateTimeline("Player", 1.0f);
CreateTimeline("Enemy", 1.0f);
CreateTimeline("Environment", 1.0f);
CreateTimeline("UI", 1.0f);
}
public Timeline CreateTimeline(string name, float initialTimeScale = 1.0f)
{
if (!timelines.ContainsKey(name))
{
Timeline timeline = new Timeline
{
name = name,
timeScale = initialTimeScale
};
timelines[name] = timeline;
return timeline;
}
return timelines[name];
}
public Timeline GetTimeline(string name)
{
return timelines.ContainsKey(name) ? timelines[name] : null;
}
void Update()
{
float deltaTime = Time.deltaTime;
// 更新所有时间线
foreach (var timeline in timelines.Values)
{
timeline.Update(deltaTime);
}
}
// 时间操纵功能
public void SetTimeScale(string timelineName, float timeScale)
{
Timeline timeline = GetTimeline(timelineName);
if (timeline != null)
{
timeline.timeScale = timeScale;
}
}
public void PauseTimeline(string timelineName)
{
Timeline timeline = GetTimeline(timelineName);
if (timeline != null)
{
timeline.isPaused = true;
}
}
public void ResumeTimeline(string timelineName)
{
Timeline timeline = GetTimeline(timelineName);
if (timeline != null)
{
timeline.isPaused = false;
}
}
// 示例:子弹时间效果
public void ActivateBulletTime(float duration, float timeScale = 0.2f)
{
// 减慢除UI外的所有时间线
foreach (var timeline in timelines.Values)
{
if (timeline.name != "UI")
{
timeline.timeScale = timeScale;
}
}
// 计划恢复正常时间
globalTimeline.ScheduleEvent(new RestoreTimeScaleEvent
{
triggerTime = globalTimeline.currentTime + duration,
targetTimeScale = 1.0f
});
}
// 时间线事件示例
public class RestoreTimeScaleEvent : TimelineEvent
{
public float targetTimeScale;
public override void Execute()
{
TimelineSystem system = FindObjectOfType<TimelineSystem>();
foreach (var timeline in system.timelines.Values)
{
if (timeline.name != "UI")
{
timeline.timeScale = targetTimeScale;
}
}
}
}
}
// 使用时间线的组件示例
public class TimelineAnimator : MonoBehaviour, TimelineSystem.ITimelineUpdateable
{
public string timelineName = "Global";
private Animator animator;
private TimelineSystem.Timeline timeline;
void Start()
{
animator = GetComponent<Animator>();
TimelineSystem timelineSystem = FindObjectOfType<TimelineSystem>();
timeline = timelineSystem.GetTimeline(timelineName);
timeline?.RegisterUpdateable(this);
}
public void UpdateTimeline(float deltaTime)
{
// 使用时间线的时间更新动画
animator.speed = timeline.timeScale;
}
void OnDestroy()
{
timeline?.UnregisterUpdateable(this);
}
}
高级时间控制
csharp
public class AdvancedTimeControl : MonoBehaviour
{
// 时间曲线
[System.Serializable]
public class TimeCurve
{
public AnimationCurve curve;
public float duration;
public bool loop;
private float currentTime = 0;
public float Evaluate(float deltaTime)
{
currentTime += deltaTime;
if (loop)
{
currentTime = currentTime % duration;
}
else
{
currentTime = Mathf.Clamp(currentTime, 0, duration);
}
return curve.Evaluate(currentTime / duration);
}
public void Reset()
{
currentTime = 0;
}
}
// 时间层
public class TimeLayer
{
public string name;
public float baseTimeScale = 1.0f;
public TimeCurve timeCurve;
public List<GameObject> affectedObjects = new List<GameObject>();
private float effectiveTimeScale;
public float GetEffectiveTimeScale(float deltaTime)
{
if (timeCurve != null && timeCurve.curve != null)
{
effectiveTimeScale = baseTimeScale * timeCurve.Evaluate(deltaTime);
}
else
{
effectiveTimeScale = baseTimeScale;
}
return effectiveTimeScale;
}
}
private List<TimeLayer> timeLayers = new List<TimeLayer>();
// 时间效果
public abstract class TimeEffect
{
public float duration;
public float elapsedTime;
public bool isActive = true;
public abstract void Apply(TimeLayer layer, float deltaTime);
public virtual void Update(float deltaTime)
{
elapsedTime += deltaTime;
if (duration > 0 && elapsedTime >= duration)
{
isActive = false;
}
}
}
// 渐变时间效果
public class TimeScaleTransition : TimeEffect
{
public float startScale;
public float endScale;
public AnimationCurve transitionCurve;
public override void Apply(TimeLayer layer, float deltaTime)
{
float t = elapsedTime / duration;
float curveValue = transitionCurve?.Evaluate(t) ?? t;
layer.baseTimeScale = Mathf.Lerp(startScale, endScale, curveValue);
}
}
// 脉冲时间效果
public class TimePulseEffect : TimeEffect
{
public float pulseFrequency = 1.0f;
public float pulseAmplitude = 0.5f;
public float baseScale = 1.0f;
public override void Apply(TimeLayer layer, float deltaTime)
{
float pulse = Mathf.Sin(elapsedTime * pulseFrequency * 2 * Mathf.PI) *
pulseAmplitude;
layer.baseTimeScale = baseScale + pulse;
}
}
private Dictionary<TimeLayer, List<TimeEffect>> activeEffects =
new Dictionary<TimeLayer, List<TimeEffect>>();
void Start()
{
// 创建默认时间层
CreateTimeLayer("Player");
CreateTimeLayer("Enemies");
CreateTimeLayer("Projectiles");
CreateTimeLayer("Environment");
}
public TimeLayer CreateTimeLayer(string name)
{
TimeLayer layer = new TimeLayer { name = name };
timeLayers.Add(layer);
activeEffects[layer] = new List<TimeEffect>();
return layer;
}
public void ApplyTimeEffect(string layerName, TimeEffect effect)
{
TimeLayer layer = timeLayers.Find(l => l.name == layerName);
if (layer != null)
{
activeEffects[layer].Add(effect);
}
}
void Update()
{
float deltaTime = Time.deltaTime;
foreach (var layer in timeLayers)
{
// 应用所有活跃效果
List<TimeEffect> effects = activeEffects[layer];
for (int i = effects.Count - 1; i >= 0; i--)
{
TimeEffect effect = effects[i];
effect.Update(deltaTime);
if (effect.isActive)
{
effect.Apply(layer, deltaTime);
}
else
{
effects.RemoveAt(i);
}
}
// 获取最终时间缩放
float finalTimeScale = layer.GetEffectiveTimeScale(deltaTime);
// 应用到受影响的对象
foreach (var obj in layer.affectedObjects)
{
ApplyTimeScaleToObject(obj, finalTimeScale);
}
}
}
private void ApplyTimeScaleToObject(GameObject obj, float timeScale)
{
// 应用到动画
Animator animator = obj.GetComponent<Animator>();
if (animator != null)
{
animator.speed = timeScale;
}
// 应用到粒子系统
ParticleSystem particles = obj.GetComponent<ParticleSystem>();
if (particles != null)
{
var main = particles.main;
main.simulationSpeed = timeScale;
}
// 应用到刚体
Rigidbody rb = obj.GetComponent<Rigidbody>();
if (rb != null)
{
// 这里需要自定义物理时间缩放逻辑
// Unity的物理引擎不直接支持个体时间缩放
}
}
// 便捷方法
public void SlowMotion(string layerName, float timeScale, float duration)
{
ApplyTimeEffect(layerName, new TimeScaleTransition
{
startScale = 1.0f,
endScale = timeScale,
duration = duration * 0.2f, // 过渡时间
transitionCurve = AnimationCurve.EaseInOut(0, 0, 1, 1)
});
// 自动恢复
StartCoroutine(RestoreTimeScale(layerName, duration));
}
private IEnumerator RestoreTimeScale(string layerName, float delay)
{
yield return new WaitForSeconds(delay);
ApplyTimeEffect(layerName, new TimeScaleTransition
{
startScale = GetTimeLayer(layerName).baseTimeScale,
endScale = 1.0f,
duration = 0.5f,
transitionCurve = AnimationCurve.EaseInOut(0, 0, 1, 1)
});
}
private TimeLayer GetTimeLayer(string name)
{
return timeLayers.Find(l => l.name == name);
}
}
7.5 测量及处理时间
准确的时间测量和处理对于游戏的流畅运行至关重要。
高精度时间测量
csharp
public class HighPrecisionTimer : MonoBehaviour
{
// 高精度计时器
public class PrecisionTimer
{
private System.Diagnostics.Stopwatch stopwatch;
private double frequency;
public PrecisionTimer()
{
stopwatch = new System.Diagnostics.Stopwatch();
frequency = System.Diagnostics.Stopwatch.Frequency;
}
public void Start()
{
stopwatch.Start();
}
public void Stop()
{
stopwatch.Stop();
}
public void Reset()
{
stopwatch.Reset();
}
public double ElapsedSeconds
{
get { return stopwatch.ElapsedTicks / frequency; }
}
public double ElapsedMilliseconds
{
get { return ElapsedSeconds * 1000.0; }
}
public double ElapsedMicroseconds
{
get { return ElapsedSeconds * 1000000.0; }
}
}
// 性能分析器
public class PerformanceProfiler
{
private Dictionary<string, ProfileData> profiles =
new Dictionary<string, ProfileData>();
private class ProfileData
{
public PrecisionTimer timer = new PrecisionTimer();
public double totalTime = 0;
public int sampleCount = 0;
public double minTime = double.MaxValue;
public double maxTime = double.MinValue;
public Queue<double> recentSamples = new Queue<double>();
private const int MaxSamples = 100;
public void AddSample(double time)
{
totalTime += time;
sampleCount++;
minTime = Math.Min(minTime, time);
maxTime = Math.Max(maxTime, time);
recentSamples.Enqueue(time);
if (recentSamples.Count > MaxSamples)
{
recentSamples.Dequeue();
}
}
public double AverageTime
{
get { return sampleCount > 0 ? totalTime / sampleCount : 0; }
}
public double RecentAverage
{
get
{
if (recentSamples.Count == 0) return 0;
return recentSamples.Average();
}
}
}
public void BeginProfile(string name)
{
if (!profiles.ContainsKey(name))
{
profiles[name] = new ProfileData();
}
profiles[name].timer.Reset();
profiles[name].timer.Start();
}
public void EndProfile(string name)
{
if (profiles.ContainsKey(name))
{
profiles[name].timer.Stop();
double elapsed = profiles[name].timer.ElapsedMilliseconds;
profiles[name].AddSample(elapsed);
}
}
public string GetReport()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine("Performance Report:");
sb.AppendLine("==================");
foreach (var kvp in profiles.OrderByDescending(x => x.Value.totalTime))
{
var data = kvp.Value;
sb.AppendLine($"{kvp.Key}:");
sb.AppendLine($" Average: {data.AverageTime:F3}ms");
sb.AppendLine($" Recent Avg: {data.RecentAverage:F3}ms");
sb.AppendLine($" Min: {data.minTime:F3}ms");
sb.AppendLine($" Max: {data.maxTime:F3}ms");
sb.AppendLine($" Total: {data.totalTime:F3}ms");
sb.AppendLine($" Samples: {data.sampleCount}");
sb.AppendLine();
}
return sb.ToString();
}
}
private static PerformanceProfiler profiler = new PerformanceProfiler();
// 自动性能分析作用域
public struct ProfileScope : IDisposable
{
private string name;
public ProfileScope(string name)
{
this.name = name;
profiler.BeginProfile(name);
}
public void Dispose()
{
profiler.EndProfile(name);
}
}
// 使用示例
void Update()
{
using (new ProfileScope("Update"))
{
using (new ProfileScope("Physics"))
{
UpdatePhysics();
}
using (new ProfileScope("AI"))
{
UpdateAI();
}
using (new ProfileScope("Rendering"))
{
UpdateRendering();
}
}
// 每秒输出一次报告
if (Time.frameCount % 60 == 0)
{
Debug.Log(profiler.GetReport());
}
}
private void UpdatePhysics()
{
// 物理更新逻辑
System.Threading.Thread.Sleep(1); // 模拟耗时
}
private void UpdateAI()
{
// AI更新逻辑
System.Threading.Thread.Sleep(2); // 模拟耗时
}
private void UpdateRendering()
{
// 渲染更新逻辑
System.Threading.Thread.Sleep(3); // 模拟耗时
}
}
时间平滑和预测
csharp
public class TimeSmoothing : MonoBehaviour
{
// 时间平滑器
public class TimeSmoother
{
private Queue<float> samples = new Queue<float>();
private int maxSamples;
private float smoothingFactor;
public TimeSmoother(int maxSamples = 10, float smoothingFactor = 0.9f)
{
this.maxSamples = maxSamples;
this.smoothingFactor = smoothingFactor;
}
public float AddSample(float sample)
{
samples.Enqueue(sample);
if (samples.Count > maxSamples)
{
samples.Dequeue();
}
return GetSmoothedValue();
}
public float GetSmoothedValue()
{
if (samples.Count == 0) return 0;
// 加权移动平均
float weight = 1.0f;
float totalWeight = 0;
float weightedSum = 0;
foreach (float sample in samples.Reverse())
{
weightedSum += sample * weight;
totalWeight += weight;
weight *= smoothingFactor;
}
return weightedSum / totalWeight;
}
}
// 时间预测器
public class TimePredictor
{
private List<float> timeHistory = new List<float>();
private int historySize;
public TimePredictor(int historySize = 30)
{
this.historySize = historySize;
}
public void RecordTime(float time)
{
timeHistory.Add(time);
if (timeHistory.Count > historySize)
{
timeHistory.RemoveAt(0);
}
}
public float PredictNextFrameTime()
{
if (timeHistory.Count < 2) return 0.016f; // 默认60fps
// 简单线性预测
float sum = 0;
for (int i = 1; i < timeHistory.Count; i++)
{
sum += timeHistory[i] - timeHistory[i - 1];
}
return sum / (timeHistory.Count - 1);
}
public float PredictTimeAtFrame(int framesAhead)
{
float predictedFrameTime = PredictNextFrameTime();
return Time.time + (predictedFrameTime * framesAhead);
}
}
// 自适应时间步长
public class AdaptiveTimeStep
{
private float targetFrameTime;
private float minTimeStep;
private float maxTimeStep;
private TimeSmoother smoother;
public AdaptiveTimeStep(float targetFPS = 60f)
{
targetFrameTime = 1.0f / targetFPS;
minTimeStep = targetFrameTime * 0.5f;
maxTimeStep = targetFrameTime * 2.0f;
smoother = new TimeSmoother(5, 0.8f);
}
public float GetAdaptiveTimeStep(float actualFrameTime)
{
// 平滑实际帧时间
float smoothedTime = smoother.AddSample(actualFrameTime);
// 计算自适应时间步长
float adaptiveStep = smoothedTime;
// 限制在合理范围内
adaptiveStep = Mathf.Clamp(adaptiveStep, minTimeStep, maxTimeStep);
// 如果性能很差,使用固定步长避免螺旋死亡
if (smoothedTime > maxTimeStep * 1.5f)
{
return targetFrameTime;
}
return adaptiveStep;
}
}
private TimeSmoother deltaSmoother = new TimeSmoother(10);
private TimePredictor timePredictor = new TimePredictor(30);
private AdaptiveTimeStep adaptiveTimeStep = new AdaptiveTimeStep(60);
void Update()
{
// 记录和平滑时间
float rawDeltaTime = Time.deltaTime;
float smoothedDeltaTime = deltaSmoother.AddSample(rawDeltaTime);
// 预测下一帧时间
timePredictor.RecordTime(Time.time);
float predictedNextFrameTime = timePredictor.PredictNextFrameTime();
// 获取自适应时间步长
float adaptiveStep = adaptiveTimeStep.GetAdaptiveTimeStep(rawDeltaTime);
// 使用处理后的时间值
UpdateGameLogic(smoothedDeltaTime);
// 调试信息
if (Time.frameCount % 60 == 0)
{
Debug.Log($"Raw Delta: {rawDeltaTime:F4}, " +
$"Smoothed: {smoothedDeltaTime:F4}, " +
$"Predicted: {predictedNextFrameTime:F4}, " +
$"Adaptive: {adaptiveStep:F4}");
}
}
private void UpdateGameLogic(float deltaTime)
{
// 使用平滑后的时间更新游戏逻辑
}
}
时间同步系统
csharp
public class TimeSynchronization : MonoBehaviour
{
// 网络时间同步
public class NetworkTimeSync
{
private double serverTime;
private double clientTime;
private double offset;
private double rtt; // Round Trip Time
private Queue<double> offsetSamples = new Queue<double>();
private const int MaxSamples = 10;
public void SendTimeRequest()
{
double requestTime = GetHighPrecisionTime();
// 发送时间请求到服务器
SendToServer(new TimeRequest { clientTime = requestTime });
}
public void ReceiveTimeResponse(TimeResponse response)
{
double receiveTime = GetHighPrecisionTime();
double sendTime = response.clientTime;
// 计算往返时间
rtt = receiveTime - sendTime;
// 估算当前服务器时间
double estimatedServerTime = response.serverTime + (rtt / 2);
// 计算时间偏移
double newOffset = estimatedServerTime - receiveTime;
// 保存偏移样本
offsetSamples.Enqueue(newOffset);
if (offsetSamples.Count > MaxSamples)
{
offsetSamples.Dequeue();
}
// 使用中位数作为最终偏移(抗干扰)
offset = GetMedian(offsetSamples.ToArray());
}
public double GetSynchronizedTime()
{
return GetHighPrecisionTime() + offset;
}
private double GetHighPrecisionTime()
{
return System.Diagnostics.Stopwatch.GetTimestamp() /
(double)System.Diagnostics.Stopwatch.Frequency;
}
private double GetMedian(double[] values)
{
Array.Sort(values);
int n = values.Length;
if (n % 2 == 0)
{
return (values[n / 2 - 1] + values[n / 2]) / 2;
}
return values[n / 2];
}
private void SendToServer(TimeRequest request)
{
// 实际网络发送逻辑
}
}
// 时间请求和响应结构
[System.Serializable]
public struct TimeRequest
{
public double clientTime;
}
[System.Serializable]
public struct TimeResponse
{
public double clientTime;
public double serverTime;
}
// 本地时间校准
public class LocalTimeCalibration
{
private double calibrationOffset = 0;
private double driftRate = 0;
private double lastCalibrationTime;
public void Calibrate(double referenceTime, double localTime)
{
double newOffset = referenceTime - localTime;
if (lastCalibrationTime > 0)
{
double timeSinceLastCalibration = localTime - lastCalibrationTime;
double offsetChange = newOffset - calibrationOffset;
// 计算漂移率
driftRate = offsetChange / timeSinceLastCalibration;
}
calibrationOffset = newOffset;
lastCalibrationTime = localTime;
}
public double GetCalibratedTime(double localTime)
{
double timeSinceCalibration = localTime - lastCalibrationTime;
double estimatedDrift = driftRate * timeSinceCalibration;
return localTime + calibrationOffset + estimatedDrift;
}
}
private NetworkTimeSync networkSync = new NetworkTimeSync();
private LocalTimeCalibration localCalibration = new LocalTimeCalibration();
private float syncInterval = 30f; // 30秒同步一次
private float nextSyncTime = 0;
void Start()
{
// 初始时间同步
StartCoroutine(InitialTimeSync());
}
IEnumerator InitialTimeSync()
{
// 发送多次请求以获得准确的时间
for (int i = 0; i < 5; i++)
{
networkSync.SendTimeRequest();
yield return new WaitForSeconds(0.5f);
}
}
void Update()
{
// 定期同步
if (Time.time >= nextSyncTime)
{
networkSync.SendTimeRequest();
nextSyncTime = Time.time + syncInterval;
}
// 使用同步的时间
double synchronizedTime = networkSync.GetSynchronizedTime();
// 本地校准
if (Time.frameCount % 300 == 0) // 每5秒校准一次
{
localCalibration.Calibrate(synchronizedTime, Time.time);
}
}
public double GetGameTime()
{
return localCalibration.GetCalibratedTime(Time.time);
}
}
7.6 多处理器的游戏循环
现代游戏需要充分利用多核处理器的性能优势。
并行游戏循环架构
csharp
public class ParallelGameLoop : MonoBehaviour
{
// 任务调度器
public class TaskScheduler
{
private class ScheduledTask
{
public System.Action<float> action;
public float priority;
public bool canRunInParallel;
public List<string> dependencies = new List<string>();
public string name;
}
private List<ScheduledTask> tasks = new List<ScheduledTask>();
private Dictionary<string, ScheduledTask> taskMap =
new Dictionary<string, ScheduledTask>();
public void RegisterTask(string name, System.Action<float> action,
float priority = 0, bool canRunInParallel = true)
{
var task = new ScheduledTask
{
name = name,
action = action,
priority = priority,
canRunInParallel = canRunInParallel
};
tasks.Add(task);
taskMap[name] = task;
}
public void AddDependency(string taskName, string dependsOn)
{
if (taskMap.ContainsKey(taskName))
{
taskMap[taskName].dependencies.Add(dependsOn);
}
}
public void ExecuteTasks(float deltaTime)
{
// 按优先级排序
tasks.Sort((a, b) => b.priority.CompareTo(a.priority));
// 构建执行计划
List<List<ScheduledTask>> executionPlan = BuildExecutionPlan();
// 执行任务
foreach (var batch in executionPlan)
{
if (batch.Count == 1 || !batch[0].canRunInParallel)
{
// 串行执行
foreach (var task in batch)
{
task.action(deltaTime);
}
}
else
{
// 并行执行
Parallel.ForEach(batch, task =>
{
task.action(deltaTime);
});
}
}
}
private List<List<ScheduledTask>> BuildExecutionPlan()
{
List<List<ScheduledTask>> plan = new List<List<ScheduledTask>>();
HashSet<string> completed = new HashSet<string>();
List<ScheduledTask> remaining = new List<ScheduledTask>(tasks);
while (remaining.Count > 0)
{
List<ScheduledTask> batch = new List<ScheduledTask>();
for (int i = remaining.Count - 1; i >= 0; i--)
{
var task = remaining[i];
// 检查依赖是否满足
bool dependenciesMet = true;
foreach (var dep in task.dependencies)
{
if (!completed.Contains(dep))
{
dependenciesMet = false;
break;
}
}
if (dependenciesMet)
{
batch.Add(task);
remaining.RemoveAt(i);
}
}
if (batch.Count > 0)
{
plan.Add(batch);
foreach (var task in batch)
{
completed.Add(task.name);
}
}
else
{
Debug.LogError("Circular dependency detected!");
break;
}
}
return plan;
}
}
// 并行物理系统
public class ParallelPhysicsSystem
{
private struct PhysicsJob
{
public Rigidbody rigidbody;
public Vector3 force;
public Vector3 torque;
}
private List<PhysicsJob> physicsJobs = new List<PhysicsJob>();
private object jobLock = new object();
public void AddForce(Rigidbody rb, Vector3 force, Vector3 torque)
{
lock (jobLock)
{
physicsJobs.Add(new PhysicsJob
{
rigidbody = rb,
force = force,
torque = torque
});
}
}
public void ProcessPhysicsJobs()
{
List<PhysicsJob> jobsToProcess;
lock (jobLock)
{
jobsToProcess = new List<PhysicsJob>(physicsJobs);
physicsJobs.Clear();
}
// 并行处理物理作业
Parallel.ForEach(jobsToProcess, job =>
{
// 注意:Unity的物理API不是线程安全的
// 这里仅作示例,实际应用需要其他方案
lock (job.rigidbody)
{
// 计算物理更新
Vector3 newVelocity = job.rigidbody.velocity + job.force;
Vector3 newAngularVelocity = job.rigidbody.angularVelocity + job.torque;
// 存储结果,稍后在主线程应用
StorePhysicsResult(job.rigidbody, newVelocity, newAngularVelocity);
}
});
}
private Dictionary<Rigidbody, (Vector3 velocity, Vector3 angularVelocity)>
physicsResults = new Dictionary<Rigidbody, (Vector3, Vector3)>();
private void StorePhysicsResult(Rigidbody rb, Vector3 velocity,
Vector3 angularVelocity)
{
lock (physicsResults)
{
physicsResults[rb] = (velocity, angularVelocity);
}
}
public void ApplyPhysicsResults()
{
lock (physicsResults)
{
foreach (var kvp in physicsResults)
{
kvp.Key.velocity = kvp.Value.velocity;
kvp.Key.angularVelocity = kvp.Value.angularVelocity;
}
physicsResults.Clear();
}
}
}
// 并行AI系统
public class ParallelAISystem
{
public interface IAIAgent
{
void Think(float deltaTime);
void Act();
}
private List<IAIAgent> agents = new List<IAIAgent>();
private ConcurrentBag<System.Action> aiActions = new ConcurrentBag<System.Action>();
public void RegisterAgent(IAIAgent agent)
{
agents.Add(agent);
}
public void UpdateAI(float deltaTime)
{
// 并行思考阶段
Parallel.ForEach(agents, agent =>
{
agent.Think(deltaTime);
});
// 串行执行阶段(如果需要修改Unity对象)
foreach (var agent in agents)
{
agent.Act();
}
}
}
private TaskScheduler taskScheduler = new TaskScheduler();
private ParallelPhysicsSystem physicsSystem = new ParallelPhysicsSystem();
private ParallelAISystem aiSystem = new ParallelAISystem();
void Start()
{
// 注册游戏系统任务
taskScheduler.RegisterTask("Input", UpdateInput, 100, false);
taskScheduler.RegisterTask("Physics", UpdatePhysics, 90, true);
taskScheduler.RegisterTask("AI", UpdateAI, 80, true);
taskScheduler.RegisterTask("Animation", UpdateAnimation, 70, true);
taskScheduler.RegisterTask("Rendering", PrepareRendering, 60, false);
// 设置依赖关系
taskScheduler.AddDependency("Physics", "Input");
taskScheduler.AddDependency("AI", "Input");
taskScheduler.AddDependency("Animation", "Physics");
taskScheduler.AddDependency("Animation", "AI");
taskScheduler.AddDependency("Rendering", "Animation");
}
void Update()
{
float deltaTime = Time.deltaTime;
// 执行所有任务
taskScheduler.ExecuteTasks(deltaTime);
}
private void UpdateInput(float deltaTime)
{
// 输入处理(必须在主线程)
}
private void UpdatePhysics(float deltaTime)
{
// 处理物理作业
physicsSystem.ProcessPhysicsJobs();
// 在主线程应用结果
UnityMainThreadDispatcher.Instance.Enqueue(() =>
{
physicsSystem.ApplyPhysicsResults();
});
}
private void UpdateAI(float deltaTime)
{
aiSystem.UpdateAI(deltaTime);
}
private void UpdateAnimation(float deltaTime)
{
// 动画更新
}
private void PrepareRendering(float deltaTime)
{
// 准备渲染数据
}
}
// Unity主线程分发器
public class UnityMainThreadDispatcher : MonoBehaviour
{
private static UnityMainThreadDispatcher instance;
private Queue<System.Action> actionQueue = new Queue<System.Action>();
public static UnityMainThreadDispatcher Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<UnityMainThreadDispatcher>();
if (instance == null)
{
GameObject go = new GameObject("MainThreadDispatcher");
instance = go.AddComponent<UnityMainThreadDispatcher>();
DontDestroyOnLoad(go);
}
}
return instance;
}
}
public void Enqueue(System.Action action)
{
lock (actionQueue)
{
actionQueue.Enqueue(action);
}
}
void Update()
{
lock (actionQueue)
{
while (actionQueue.Count > 0)
{
actionQueue.Dequeue()?.Invoke();
}
}
}
}
Job System集成
csharp
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
public class JobSystemGameLoop : MonoBehaviour
{
// 物理模拟Job
[BurstCompile]
struct PhysicsSimulationJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float3> positions;
[ReadOnly] public NativeArray<float3> velocities;
[ReadOnly] public float deltaTime;
[WriteOnly] public NativeArray<float3> newPositions;
[WriteOnly] public NativeArray<float3> newVelocities;
public void Execute(int index)
{
float3 pos = positions[index];
float3 vel = velocities[index];
// 简单的物理模拟
vel.y -= 9.81f * deltaTime; // 重力
pos += vel * deltaTime;
// 地面碰撞
if (pos.y < 0)
{
pos.y = 0;
vel.y = -vel.y * 0.8f; // 弹性碰撞
}
newPositions[index] = pos;
newVelocities[index] = vel;
}
}
// 路径寻找Job
[BurstCompile]
struct PathfindingJob : IJob
{
[ReadOnly] public float3 start;
[ReadOnly] public float3 goal;
[ReadOnly] public NativeArray<int> gridData;
public int gridWidth;
public int gridHeight;
[WriteOnly] public NativeList<float3> path;
public void Execute()
{
// A*路径寻找算法的简化版本
NativeList<int2> openList = new NativeList<int2>(Allocator.Temp);
NativeHashMap<int2, float> gScore = new NativeHashMap<int2, float>(100, Allocator.Temp);
NativeHashMap<int2, float> fScore = new NativeHashMap<int2, float>(100, Allocator.Temp);
NativeHashMap<int2, int2> cameFrom = new NativeHashMap<int2, int2>(100, Allocator.Temp);
int2 startNode = new int2((int)start.x, (int)start.z);
int2 goalNode = new int2((int)goal.x, (int)goal.z);
openList.Add(startNode);
gScore[startNode] = 0;
fScore[startNode] = Heuristic(startNode, goalNode);
while (openList.Length > 0)
{
int2 current = GetLowestFScore(openList, fScore);
if (current.Equals(goalNode))
{
ReconstructPath(cameFrom, current, path);
break;
}
openList.RemoveAtSwapBack(GetIndex(openList, current));
// 检查邻居
for (int dx = -1; dx <= 1; dx++)
{
for (int dy = -1; dy <= 1; dy++)
{
if (dx == 0 && dy == 0) continue;
int2 neighbor = current + new int2(dx, dy);
if (!IsWalkable(neighbor)) continue;
float tentativeGScore = gScore[current] + Distance(current, neighbor);
if (!gScore.ContainsKey(neighbor) || tentativeGScore < gScore[neighbor])
{
cameFrom[neighbor] = current;
gScore[neighbor] = tentativeGScore;
fScore[neighbor] = tentativeGScore + Heuristic(neighbor, goalNode);
if (!Contains(openList, neighbor))
{
openList.Add(neighbor);
}
}
}
}
}
openList.Dispose();
gScore.Dispose();
fScore.Dispose();
cameFrom.Dispose();
}
float Heuristic(int2 a, int2 b)
{
return math.distance(new float2(a), new float2(b));
}
float Distance(int2 a, int2 b)
{
return math.distance(new float2(a), new float2(b));
}
bool IsWalkable(int2 pos)
{
if (pos.x < 0 || pos.x >= gridWidth || pos.y < 0 || pos.y >= gridHeight)
return false;
int index = pos.y * gridWidth + pos.x;
return gridData[index] == 0; // 0表示可通行
}
int2 GetLowestFScore(NativeList<int2> list, NativeHashMap<int2, float> scores)
{
int2 lowest = list[0];
float lowestScore = scores[lowest];
for (int i = 1; i < list.Length; i++)
{
float score = scores[list[i]];
if (score < lowestScore)
{
lowest = list[i];
lowestScore = score;
}
}
return lowest;
}
int GetIndex(NativeList<int2> list, int2 value)
{
for (int i = 0; i < list.Length; i++)
{
if (list[i].Equals(value))
return i;
}
return -1;
}
bool Contains(NativeList<int2> list, int2 value)
{
return GetIndex(list, value) >= 0;
}
void ReconstructPath(NativeHashMap<int2, int2> cameFrom, int2 current, NativeList<float3> path)
{
path.Add(new float3(current.x, 0, current.y));
while (cameFrom.ContainsKey(current))
{
current = cameFrom[current];
path.Add(new float3(current.x, 0, current.y));
}
}
}
// Job管理器
public class JobManager
{
private NativeArray<float3> positions;
private NativeArray<float3> velocities;
private NativeArray<float3> newPositions;
private NativeArray<float3> newVelocities;
private JobHandle physicsJobHandle;
private List<JobHandle> pathfindingHandles = new List<JobHandle>();
public void Initialize(int entityCount)
{
positions = new NativeArray<float3>(entityCount, Allocator.Persistent);
velocities = new NativeArray<float3>(entityCount, Allocator.Persistent);
newPositions = new NativeArray<float3>(entityCount, Allocator.Persistent);
newVelocities = new NativeArray<float3>(entityCount, Allocator.Persistent);
// 初始化数据
for (int i = 0; i < entityCount; i++)
{
positions[i] = new float3(
UnityEngine.Random.Range(-50f, 50f),
UnityEngine.Random.Range(10f, 50f),
UnityEngine.Random.Range(-50f, 50f)
);
velocities[i] = new float3(
UnityEngine.Random.Range(-5f, 5f),
0,
UnityEngine.Random.Range(-5f, 5f)
);
}
}
public void SchedulePhysicsJobs(float deltaTime)
{
var physicsJob = new PhysicsSimulationJob
{
positions = positions,
velocities = velocities,
deltaTime = deltaTime,
newPositions = newPositions,
newVelocities = newVelocities
};
physicsJobHandle = physicsJob.Schedule(positions.Length, 64);
}
public void CompletePhysicsJobs()
{
physicsJobHandle.Complete();
// 交换缓冲区
var temp = positions;
positions = newPositions;
newPositions = temp;
temp = velocities;
velocities = newVelocities;
newVelocities = temp;
}
public void Dispose()
{
physicsJobHandle.Complete();
positions.Dispose();
velocities.Dispose();
newPositions.Dispose();
newVelocities.Dispose();
}
}
private JobManager jobManager = new JobManager();
void Start()
{
jobManager.Initialize(1000);
}
void Update()
{
// 调度Jobs
jobManager.SchedulePhysicsJobs(Time.deltaTime);
// 在等待Jobs完成时可以做其他工作
UpdateUI();
ProcessInput();
// 完成Jobs
jobManager.CompletePhysicsJobs();
// 应用结果
ApplyResults();
}
void OnDestroy()
{
jobManager.Dispose();
}
private void UpdateUI()
{
// UI更新逻辑
}
private void ProcessInput()
{
// 输入处理逻辑
}
private void ApplyResults()
{
// 应用Job计算结果
}
}
7.7 网络多人游戏循环
网络游戏需要特殊的循环设计来处理延迟、同步和预测。
客户端预测和服务器协调
csharp
public class NetworkGameLoop : MonoBehaviour
{
// 网络时间管理
public class NetworkTime
{
private float serverTime;
private float clientTime;
private float latency;
private float timeOffset;
public void UpdateServerTime(float newServerTime, float roundTripTime)
{
latency = roundTripTime / 2f;
float estimatedServerTime = newServerTime + latency;
timeOffset = estimatedServerTime - Time.time;
serverTime = estimatedServerTime;
}
public float GetServerTime()
{
return Time.time + timeOffset;
}
public float GetClientTime()
{
return Time.time;
}
public float GetLatency()
{
return latency;
}
}
// 客户端预测系统
public class ClientPrediction
{
private class PredictedState
{
public int inputSequence;
public Vector3 position;
public Quaternion rotation;
public Vector3 velocity;
public float timestamp;
}
private Queue<PredictedState> stateHistory = new Queue<PredictedState>();
private int maxHistorySize = 60; // 1秒的历史(60fps)
public void RecordState(int inputSequence, Vector3 position,
Quaternion rotation, Vector3 velocity)
{
var state = new PredictedState
{
inputSequence = inputSequence,
position = position,
rotation = rotation,
velocity = velocity,
timestamp = Time.time
};
stateHistory.Enqueue(state);
// 限制历史大小
while (stateHistory.Count > maxHistorySize)
{
stateHistory.Dequeue();
}
}
public void ReconcileWithServer(int acknowledgedInput, Vector3 serverPosition,
Quaternion serverRotation, Vector3 serverVelocity)
{
// 找到对应的历史状态
PredictedState[] history = stateHistory.ToArray();
int reconcileIndex = -1;
for (int i = 0; i < history.Length; i++)
{
if (history[i].inputSequence == acknowledgedInput)
{
reconcileIndex = i;
break;
}
}
if (reconcileIndex >= 0)
{
// 检查预测误差
Vector3 positionError = serverPosition - history[reconcileIndex].position;
float rotationError = Quaternion.Angle(serverRotation,
history[reconcileIndex].rotation);
// 如果误差太大,进行修正
if (positionError.magnitude > 0.1f || rotationError > 5f)
{
// 重新模拟从服务器状态开始的所有输入
ResimulateFromState(reconcileIndex, serverPosition,
serverRotation, serverVelocity);
}
}
}
private void ResimulateFromState(int startIndex, Vector3 position,
Quaternion rotation, Vector3 velocity)
{
// 从服务器确认的状态开始重新模拟
PredictedState[] history = stateHistory.ToArray();
for (int i = startIndex + 1; i < history.Length; i++)
{
// 重新应用输入
// 这里需要存储和重放输入
}
}
}
// 输入缓冲系统
public class InputBuffer
{
public struct InputCommand
{
public int sequence;
public float timestamp;
public Vector2 movement;
public bool jump;
public bool fire;
public Vector3 lookDirection;
}
private Queue<InputCommand> unacknowledgedInputs = new Queue<InputCommand>();
private int currentSequence = 0;
public InputCommand CreateInput(Vector2 movement, bool jump, bool fire,
Vector3 lookDirection)
{
var input = new InputCommand
{
sequence = currentSequence++,
timestamp = Time.time,
movement = movement,
jump = jump,
fire = fire,
lookDirection = lookDirection
};
unacknowledgedInputs.Enqueue(input);
return input;
}
public void AcknowledgeInput(int sequence)
{
// 移除已确认的输入
while (unacknowledgedInputs.Count > 0 &&
unacknowledgedInputs.Peek().sequence <= sequence)
{
unacknowledgedInputs.Dequeue();
}
}
public InputCommand[] GetUnacknowledgedInputs()
{
return unacknowledgedInputs.ToArray();
}
}
// 延迟补偿系统
public class LagCompensation
{
private class WorldSnapshot
{
public float timestamp;
public Dictionary<int, EntityState> entities = new Dictionary<int, EntityState>();
}
private class EntityState
{
public Vector3 position;
public Quaternion rotation;
public Bounds bounds;
}
private Queue<WorldSnapshot> snapshotHistory = new Queue<WorldSnapshot>();
private float historyDuration = 1.0f; // 保留1秒的历史
public void RecordSnapshot()
{
var snapshot = new WorldSnapshot
{
timestamp = Time.time
};
// 记录所有实体状态
GameObject[] entities = GameObject.FindGameObjectsWithTag("NetworkEntity");
foreach (var entity in entities)
{
int entityId = entity.GetComponent<NetworkEntity>().entityId;
snapshot.entities[entityId] = new EntityState
{
position = entity.transform.position,
rotation = entity.transform.rotation,
bounds = entity.GetComponent<Collider>().bounds
};
}
snapshotHistory.Enqueue(snapshot);
// 清理旧快照
float cutoffTime = Time.time - historyDuration;
while (snapshotHistory.Count > 0 &&
snapshotHistory.Peek().timestamp < cutoffTime)
{
snapshotHistory.Dequeue();
}
}
public bool RaycastAtTime(float timestamp, Vector3 origin, Vector3 direction,
out RaycastHit hit, float maxDistance, int layerMask)
{
// 找到最接近的历史快照
WorldSnapshot snapshot = GetSnapshotAtTime(timestamp);
if (snapshot != null)
{
// 临时移动所有实体到历史位置
Dictionary<Transform, Vector3> originalPositions =
new Dictionary<Transform, Vector3>();
Dictionary<Transform, Quaternion> originalRotations =
new Dictionary<Transform, Quaternion>();
foreach (var kvp in snapshot.entities)
{
GameObject entity = FindEntityById(kvp.Key);
if (entity != null)
{
Transform transform = entity.transform;
originalPositions[transform] = transform.position;
originalRotations[transform] = transform.rotation;
transform.position = kvp.Value.position;
transform.rotation = kvp.Value.rotation;
}
}
// 执行射线检测
bool result = Physics.Raycast(origin, direction, out hit,
maxDistance, layerMask);
// 恢复原始位置
foreach (var kvp in originalPositions)
{
kvp.Key.position = kvp.Value;
}
foreach (var kvp in originalRotations)
{
kvp.Key.rotation = kvp.Value;
}
return result;
}
// 如果没有历史数据,使用当前状态
return Physics.Raycast(origin, direction, out hit, maxDistance, layerMask);
}
private WorldSnapshot GetSnapshotAtTime(float timestamp)
{
WorldSnapshot closest = null;
float minDelta = float.MaxValue;
foreach (var snapshot in snapshotHistory)
{
float delta = Mathf.Abs(snapshot.timestamp - timestamp);
if (delta < minDelta)
{
minDelta = delta;
closest = snapshot;
}
}
return closest;
}
private GameObject FindEntityById(int id)
{
GameObject[] entities = GameObject.FindGameObjectsWithTag("NetworkEntity");
foreach (var entity in entities)
{
if (entity.GetComponent<NetworkEntity>().entityId == id)
{
return entity;
}
}
return null;
}
}
// 网络实体组件
[System.Serializable]
public class NetworkEntity : MonoBehaviour
{
public int entityId;
public bool isLocalPlayer;
public bool isPredicted;
private Vector3 networkPosition;
private Quaternion networkRotation;
private float lastUpdateTime;
void Start()
{
if (isLocalPlayer)
{
// 本地玩家使用预测
isPredicted = true;
}
}
public void SetNetworkState(Vector3 position, Quaternion rotation)
{
networkPosition = position;
networkRotation = rotation;
lastUpdateTime = Time.time;
}
void Update()
{
if (!isLocalPlayer && !isPredicted)
{
// 插值到网络位置
float timeSinceUpdate = Time.time - lastUpdateTime;
float lerpFactor = Mathf.Clamp01(timeSinceUpdate / 0.1f); // 100ms插值
transform.position = Vector3.Lerp(transform.position,
networkPosition, lerpFactor);
transform.rotation = Quaternion.Slerp(transform.rotation,
networkRotation, lerpFactor);
}
}
}
private NetworkTime networkTime = new NetworkTime();
private ClientPrediction clientPrediction = new ClientPrediction();
private InputBuffer inputBuffer = new InputBuffer();
private LagCompensation lagCompensation = new LagCompensation();
void Start()
{
// 启动网络时间同步
StartCoroutine(SyncNetworkTime());
}
void Update()
{
// 记录世界快照(用于延迟补偿)
lagCompensation.RecordSnapshot();
// 处理本地输入
if (IsLocalPlayer())
{
ProcessLocalInput();
}
// 更新网络实体
UpdateNetworkEntities();
}
void FixedUpdate()
{
// 固定时间步长的物理更新
if (IsLocalPlayer())
{
// 客户端预测
SimulateLocalPlayer();
}
}
private void ProcessLocalInput()
{
Vector2 movement = new Vector2(Input.GetAxis("Horizontal"),
Input.GetAxis("Vertical"));
bool jump = Input.GetButtonDown("Jump");
bool fire = Input.GetButton("Fire1");
Vector3 lookDirection = GetLookDirection();
// 创建输入命令
var inputCmd = inputBuffer.CreateInput(movement, jump, fire, lookDirection);
// 发送到服务器
SendInputToServer(inputCmd);
}
private void SimulateLocalPlayer()
{
// 应用客户端预测
GameObject player = GetLocalPlayer();
if (player != null)
{
// 这里实现具体的移动逻辑
// 记录预测状态
clientPrediction.RecordState(
GetCurrentInputSequence(),
player.transform.position,
player.transform.rotation,
player.GetComponent<Rigidbody>().velocity
);
}
}
private void UpdateNetworkEntities()
{
// 更新所有网络实体的状态
}
IEnumerator SyncNetworkTime()
{
while (true)
{
// 发送时间同步请求
SendTimeSyncRequest();
yield return new WaitForSeconds(5f); // 每5秒同步一次
}
}
// 网络消息处理
public void OnReceiveServerUpdate(ServerUpdateMessage message)
{
// 更新服务器时间
networkTime.UpdateServerTime(message.serverTime, message.roundTripTime);
// 处理实体更新
foreach (var entityUpdate in message.entities)
{
GameObject entity = FindEntityById(entityUpdate.id);
if (entity != null)
{
NetworkEntity netEntity = entity.GetComponent<NetworkEntity>();
if (netEntity.isLocalPlayer)
{
// 本地玩家:进行预测协调
clientPrediction.ReconcileWithServer(
message.acknowledgedInput,
entityUpdate.position,
entityUpdate.rotation,
entityUpdate.velocity
);
}
else
{
// 其他玩家:设置网络状态
netEntity.SetNetworkState(entityUpdate.position,
entityUpdate.rotation);
}
}
}
// 确认输入
inputBuffer.AcknowledgeInput(message.acknowledgedInput);
}
// 辅助方法
private bool IsLocalPlayer()
{
// 判断是否是本地玩家
return true; // 示例实现
}
private GameObject GetLocalPlayer()
{
// 获取本地玩家对象
return GameObject.FindWithTag("LocalPlayer");
}
private Vector3 GetLookDirection()
{
// 获取视线方向
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
return ray.direction;
}
private int GetCurrentInputSequence()
{
// 获取当前输入序列号
return 0; // 示例实现
}
private GameObject FindEntityById(int id)
{
// 根据ID查找实体
return null; // 示例实现
}
private void SendInputToServer(InputBuffer.InputCommand input)
{
// 发送输入到服务器
}
private void SendTimeSyncRequest()
{
// 发送时间同步请求
}
}
// 服务器更新消息
[System.Serializable]
public class ServerUpdateMessage
{
public float serverTime;
public float roundTripTime;
public int acknowledgedInput;
public List<EntityUpdate> entities = new List<EntityUpdate>();
}
[System.Serializable]
public class EntityUpdate
{
public int id;
public Vector3 position;
public Quaternion rotation;
public Vector3 velocity;
}
确定性模拟
csharp
public class DeterministicGameLoop : MonoBehaviour
{
// 定点数学库
public struct FixedPoint
{
private long rawValue;
private const int SHIFT_AMOUNT = 16; // 16位小数
private const long ONE = 1L << SHIFT_AMOUNT;
public static FixedPoint FromFloat(float value)
{
return new FixedPoint { rawValue = (long)(value * ONE) };
}
public static FixedPoint FromInt(int value)
{
return new FixedPoint { rawValue = value * ONE };
}
public float ToFloat()
{
return (float)rawValue / ONE;
}
// 运算符重载
public static FixedPoint operator +(FixedPoint a, FixedPoint b)
{
return new FixedPoint { rawValue = a.rawValue + b.rawValue };
}
public static FixedPoint operator -(FixedPoint a, FixedPoint b)
{
return new FixedPoint { rawValue = a.rawValue - b.rawValue };
}
public static FixedPoint operator *(FixedPoint a, FixedPoint b)
{
return new FixedPoint { rawValue = (a.rawValue * b.rawValue) >> SHIFT_AMOUNT };
}
public static FixedPoint operator /(FixedPoint a, FixedPoint b)
{
return new FixedPoint { rawValue = (a.rawValue << SHIFT_AMOUNT) / b.rawValue };
}
}
// 定点向量
public struct FixedVector3
{
public FixedPoint x, y, z;
public static FixedVector3 FromVector3(Vector3 v)
{
return new FixedVector3
{
x = FixedPoint.FromFloat(v.x),
y = FixedPoint.FromFloat(v.y),
z = FixedPoint.FromFloat(v.z)
};
}
public Vector3 ToVector3()
{
return new Vector3(x.ToFloat(), y.ToFloat(), z.ToFloat());
}
public static FixedVector3 operator +(FixedVector3 a, FixedVector3 b)
{
return new FixedVector3
{
x = a.x + b.x,
y = a.y + b.y,
z = a.z + b.z
};
}
}
// 确定性随机数生成器
public class DeterministicRandom
{
private uint seed;
public DeterministicRandom(uint seed)
{
this.seed = seed;
}
public uint Next()
{
// Linear Congruential Generator
seed = seed * 1664525u + 1013904223u;
return seed;
}
public float NextFloat()
{
return (float)(Next() & 0xFFFFFF) / 16777216f;
}
public int NextInt(int min, int max)
{
return min + (int)(Next() % (uint)(max - min));
}
}
// 游戏状态
public class GameState
{
public int frame;
public uint checksum;
public Dictionary<int, EntityState> entities = new Dictionary<int, EntityState>();
public DeterministicRandom random;
public GameState Clone()
{
var clone = new GameState
{
frame = frame,
checksum = checksum,
random = new DeterministicRandom(random.Next())
};
foreach (var kvp in entities)
{
clone.entities[kvp.Key] = kvp.Value.Clone();
}
return clone;
}
public uint CalculateChecksum()
{
uint sum = 0;
foreach (var entity in entities.Values)
{
sum ^= entity.GetChecksum();
}
return sum;
}
}
// 实体状态
public class EntityState
{
public int id;
public FixedVector3 position;
public FixedVector3 velocity;
public FixedPoint health;
public EntityState Clone()
{
return new EntityState
{
id = id,
position = position,
velocity = velocity,
health = health
};
}
public uint GetChecksum()
{
uint sum = (uint)id;
sum ^= (uint)position.x.ToFloat() * 73856093u;
sum ^= (uint)position.y.ToFloat() * 19349663u;
sum ^= (uint)position.z.ToFloat() * 83492791u;
return sum;
}
}
// 输入帧
public class InputFrame
{
public int frame;
public Dictionary<int, PlayerInput> inputs = new Dictionary<int, PlayerInput>();
}
public struct PlayerInput
{
public byte movement; // 8方向移动
public byte actions; // 8个动作按钮
public ushort angle; // 360度角度(0-65535映射到0-360)
}
// 回滚系统
public class RollbackSystem
{
private Dictionary<int, GameState> stateHistory = new Dictionary<int, GameState>();
private int maxHistoryFrames = 120; // 2秒@60fps
public void SaveState(int frame, GameState state)
{
stateHistory[frame] = state.Clone();
// 清理旧状态
List<int> framesToRemove = new List<int>();
foreach (var kvp in stateHistory)
{
if (frame - kvp.Key > maxHistoryFrames)
{
framesToRemove.Add(kvp.Key);
}
}
foreach (int f in framesToRemove)
{
stateHistory.Remove(f);
}
}
public GameState GetState(int frame)
{
if (stateHistory.ContainsKey(frame))
{
return stateHistory[frame].Clone();
}
// 找到最近的状态
int closestFrame = -1;
foreach (int f in stateHistory.Keys)
{
if (f < frame && f > closestFrame)
{
closestFrame = f;
}
}
return closestFrame >= 0 ? stateHistory[closestFrame].Clone() : null;
}
}
private GameState currentState;
private RollbackSystem rollbackSystem = new RollbackSystem();
private Queue<InputFrame> inputQueue = new Queue<InputFrame>();
private int currentFrame = 0;
private int confirmedFrame = 0;
void Start()
{
// 初始化游戏状态
currentState = new GameState
{
frame = 0,
random = new DeterministicRandom(12345)
};
// 创建初始实体
CreateInitialEntities();
}
void FixedUpdate()
{
// 收集本地输入
PlayerInput localInput = CollectLocalInput();
// 创建输入帧
InputFrame inputFrame = new InputFrame
{
frame = currentFrame,
inputs = { [GetLocalPlayerId()] = localInput }
};
// 发送输入到其他客户端
BroadcastInput(inputFrame);
// 处理输入队列
ProcessInputQueue();
// 步进模拟
StepSimulation();
currentFrame++;
}
private void ProcessInputQueue()
{
// 处理接收到的输入
while (inputQueue.Count > 0)
{
InputFrame frame = inputQueue.Peek();
if (frame.frame <= confirmedFrame)
{
// 已确认的帧,跳过
inputQueue.Dequeue();
continue;
}
if (frame.frame > currentFrame)
{
// 未来的帧,等待
break;
}
// 需要回滚
if (frame.frame < currentFrame)
{
Rollback(frame.frame);
}
// 应用输入
ApplyInputFrame(frame);
inputQueue.Dequeue();
}
}
private void Rollback(int targetFrame)
{
Debug.Log($"Rolling back from frame {currentFrame} to {targetFrame}");
// 恢复到目标帧的状态
GameState state = rollbackSystem.GetState(targetFrame);
if (state != null)
{
currentState = state;
currentFrame = targetFrame;
// 重新模拟到当前帧
while (currentFrame < Time.frameCount)
{
StepSimulation();
currentFrame++;
}
}
}
private void StepSimulation()
{
// 保存当前状态
rollbackSystem.SaveState(currentFrame, currentState);
// 确定性物理模拟
foreach (var entity in currentState.entities.Values)
{
// 应用重力
entity.velocity.y = entity.velocity.y - FixedPoint.FromFloat(9.81f * 0.016f);
// 更新位置
entity.position = entity.position + entity.velocity;
// 地面碰撞
if (entity.position.y.ToFloat() < 0)
{
entity.position.y = FixedPoint.FromFloat(0);
entity.velocity.y = FixedPoint.FromFloat(0);
}
}
// 计算校验和
currentState.checksum = currentState.CalculateChecksum();
// 每隔一定帧数验证校验和
if (currentFrame % 60 == 0)
{
VerifyChecksum();
}
}
private void VerifyChecksum()
{
// 与其他客户端比较校验和
// 如果不一致,需要从主机重新同步状态
}
private PlayerInput CollectLocalInput()
{
PlayerInput input = new PlayerInput();
// 收集移动输入(8方向)
Vector2 movement = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
if (movement.magnitude > 0.1f)
{
float angle = Mathf.Atan2(movement.y, movement.x) * Mathf.Rad2Deg;
int direction = Mathf.RoundToInt((angle + 180) / 45) % 8;
input.movement = (byte)direction;
}
// 收集动作输入
if (Input.GetButton("Fire1")) input.actions |= 1;
if (Input.GetButton("Fire2")) input.actions |= 2;
if (Input.GetButton("Jump")) input.actions |= 4;
// 收集角度输入(鼠标方向)
Vector3 mousePos = Input.mousePosition;
Vector3 screenCenter = new Vector3(Screen.width / 2, Screen.height / 2, 0);
Vector3 dir = mousePos - screenCenter;
float mouseAngle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
input.angle = (ushort)((mouseAngle + 180) / 360 * 65535);
return input;
}
private void ApplyInputFrame(InputFrame frame)
{
foreach (var kvp in frame.inputs)
{
int playerId = kvp.Key;
PlayerInput input = kvp.Value;
if (currentState.entities.ContainsKey(playerId))
{
ApplyInputToEntity(currentState.entities[playerId], input);
}
}
}
private void ApplyInputToEntity(EntityState entity, PlayerInput input)
{
// 应用移动
if (input.movement < 8)
{
float angle = input.movement * 45 * Mathf.Deg2Rad;
FixedVector3 moveDir = new FixedVector3
{
x = FixedPoint.FromFloat(Mathf.Cos(angle) * 5),
z = FixedPoint.FromFloat(Mathf.Sin(angle) * 5)
};
entity.velocity = moveDir;
}
// 应用动作
if ((input.actions & 4) != 0) // Jump
{
if (entity.position.y.ToFloat() <= 0.1f)
{
entity.velocity.y = FixedPoint.FromFloat(10);
}
}
}
private void CreateInitialEntities()
{
// 创建玩家实体
for (int i = 0; i < 4; i++)
{
currentState.entities[i] = new EntityState
{
id = i,
position = FixedVector3.FromVector3(new Vector3(i * 5, 0, 0)),
velocity = FixedVector3.FromVector3(Vector3.zero),
health = FixedPoint.FromFloat(100)
};
}
}
private int GetLocalPlayerId()
{
return 0; // 示例实现
}
private void BroadcastInput(InputFrame frame)
{
// 发送输入到其他客户端
}
public void OnReceiveInput(InputFrame frame)
{
// 接收其他客户端的输入
inputQueue.Enqueue(frame);
}
}
总结
本章详细介绍了游戏循环及实时模拟的各个方面:
渲染循环 – 高效的渲染架构和优化技术
游戏循环 – 固定和可变时间步长的实现
架构风格 – 组件化、事件驱动和多线程架构
抽象时间线 – 灵活的时间控制系统
时间测量 – 高精度计时和时间同步
多处理器 – 并行计算和Job System
网络游戏 – 客户端预测、延迟补偿和确定性模拟
这些技术共同构成了现代游戏引擎的核心,通过合理的设计和实现,可以创建出流畅、响应迅速且可扩展的游戏系统。
© 版权声明
文章版权归作者所有,未经允许请勿转载。如内容涉嫌侵权,请在本页底部进入<联系我们>进行举报投诉!
THE END
暂无评论内容