
1. 值语义与移动语义。游戏对象频繁创建销毁,std::move 与移动构造避免了不必要的深拷贝。比如把缓冲区从一个容器转移到另一个容器,用移动而非复制,是性能常识。
2. RAII 与确定性析构。GPU 资源、文件句柄、音频设备都必须在确定时刻释放。C++ 的 RAII 让"作用域结束即释放"成为默认行为,比手动 new/delete 或依赖 GC 更可控。
3. 模板与零成本抽象。数学库(向量、矩阵、四元数)用模板实现,编译期展开,运行期没有函数调用开销。constexpr 让常量计算在编译期完成。
4. 缓存友好。游戏性能的瓶颈常在内存访问而非计算。结构体布局(AoS vs SoA)、连续内存容器(std::vector 优先于 std::list)、避免指针追逐,这些比算法优化更影响帧率。
5. 多线程与原子操作。渲染、物理、音频、资源加载常在不同线程。std::thread、std::atomic、任务队列是标配,但线程安全始终是复杂度的主要来源。
几乎所有实时游戏共享同一个骨架——游戏循环(Game Loop):
while (running) {
processInput();
update(deltaTime);
render();
}看似简单,细节决定成败:
std::chrono::steady_clock)而非系统时间,避免时钟回拨。一个简化但结构完整的循环骨架:
#include <chrono>
using Clock = std::chrono::steady_clock;
void run() {
const double dt = 1.0 / 60.0; // 固定步长
auto last = Clock::now();
double accumulator = 0.0;
while (running) {
auto now = Clock::now();
double frame = std::chrono::duration<double>(now - last).count();
last = now;
accumulator += frame;
processInput();
while (accumulator >= dt) {
update(dt); // 固定步长更新
accumulator -= dt;
}
render(accumulator / dt); // 用插值比例渲染
}
}这段代码体现了游戏循环的两个关键决策:更新用固定步长保证确定性,渲染用插值比例保证平滑。
早期游戏用"深继承"组织对象:GameObject → Character → Player。继承链一深,改一处基类可能影响所有子类,是典型的脆弱设计。现代主流是实体-组件-系统(ECS) 或组件模式。
核心思想:组合优于继承。一个实体是一个 ID,组件是纯数据(位置、速度、渲染、碰撞体),系统负责遍历拥有特定组件组合的实体并执行逻辑。
struct Transform { float x, y; };
struct Velocity { float dx, dy; };
// 系统:遍历所有同时拥有 Transform 与 Velocity 的实体
void movementSystem(Registry& reg, float dt) {
reg.view<Transform, Velocity>([](Transform& t, Velocity& v) {
t.x += v.dx * dt;
t.y += v.dy * dt;
});
}ECS 的好处是数据连续存储、缓存友好、易于并行。代价是架构复杂、调试不如面向对象直观。对中小项目,简单的"组件模式 + 对象池"可能更务实;对需要成千上万实体的项目,ECS 的收益才真正显现。
渲染是游戏中最"重"的环节。无论用 OpenGL、Vulkan、DirectX 还是封装好的引擎,概念是相通的:
1. 场景组织:视锥剔除(Frustum Culling)剔除摄像机外的物体,空间划分(四叉树、BVH)加速查询。
2. 提交绘制调用:把顶点数据、材质、变换矩阵提交给 GPU。绘制调用(Draw Call)是主要瓶颈之一,优化手段包括合批(Batching)、实例化(Instancing)、纹理图集(Atlas)。
3. 顶点与片元着色:顶点着色器做变换,片元着色器算颜色。GPU 并行执行,但状态切换与带宽是瓶颈。
4. 后处理:Bloom、景深、色调映射、抗锯齿,通常在屏幕空间完成。
一个最小的 OpenGL 绘制循环片段,能说明"状态设置 + 提交"的节奏:
glUseProgram(shaderProgram);
glBindVertexArray(vao);
glUniformMatrix4fv(mvpLocation, 1, GL_FALSE, &mvp[0][0]);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);真正的性能优化往往不是"写更快的着色器",而是减少状态切换、减少绘制调用、降低带宽。这是渲染优化的第一性原理。
游戏物理并不要求严格的物理准确性,而要求稳定、可控、可预测。常见做法:
经验原则:能用运动学解决的问题,别交给动力学。动力学适合大量碎片、布料、载具,角色与 UI 相关的碰撞尽量手动控制。
游戏性能优化的核心是减少缓存未命中与内存分配:
new/delete;游戏不只是"跑起来的程序",而是持续演进的工程:
一个务实的成长路径:
看书不如做项目,做项目不如发布一个作品。发布的压力会逼你解决那些"平时能绕过"的问题——崩溃、内存泄漏、加载速度、平台兼容。
C++ 游戏编程的本质,是在严格的性能与时间约束下,组织一个持续增长的实时系统。语言特性提供了零成本抽象的可能,架构决定了系统能否演进,而性能优化最终落在缓存与内存访问上。
游戏循环是骨架,ECS 是组织方式,渲染与物理是血肉,工具链是神经系统。真正拉开差距的,不是你会不会写某个算法,而是你能否让这一帧在 16 毫秒内稳定地、可预测地、可维护地跑完。每一帧都是一次小型的工程决策集合——这正是 C++ 游戏编程最迷人、也最考验人的地方。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。