发布于 2026-01-06 0 阅读
0

探索 C++ 在游戏开发中的应用

探索 C++ 在游戏开发中的应用

开发电子游戏时需要考虑哪些因素

要想制作一款属于自己的电子游戏,首先你需要一个有趣的游戏创意,而这正是游戏制作的一大难关。假设你已经有了游戏创意,并想把它变成现实。为此,你需要编写代码来实现所有功能,设计美术和音效来吸引玩家,并且竭尽所能确保游戏运行流畅,为用户带来愉悦的游戏体验。

确保游戏流畅运行是许多游戏开发者的首要任务,而选择合适的编程语言通常与此息息相关。C++ 以其“速度、灵活性以及直接与硬件通信的能力”而闻名。[1]在深入探讨 C++ 高效运行的原因之前,让我们先来了解一下其他一些用于游戏开发的语言,以及它们在哪些方面可能比 C++ 更具优势。

C++与其他游戏开发语言的比较

在讨论 C# 和 Java 之前,我想先重点介绍一下游戏开发中出于不同原因使用的其他几种语言。

JavaScript、Python 和 Lua

JavaScript 广泛应用于网页、服务器,甚至树莓派(Raspberry Pi)上,后者已成为制作便携式游戏系统的热门选择。一些游戏引擎支持 JavaScript 代码,例如 Impact.js、LÖVE 和 Crafty.js。[2] Python 和 Lua 在学习新的编程语言时更容易上手,并且都得到了许多开源游戏引擎的支持。这三种语言主要用于 2D 游戏。它们也可以用于 3D 游戏,但由于 JavaScript 对 3D 游戏的支持有限,而 Lua 则完全不支持,因此建议使用 Python 来开发 3D 游戏。我计划在以后的文章中花更多时间研究这些语言在游戏领域的应用。

C#(升号)

C# 得到了许多游戏引擎的支持,包括 Unity 和 MonoGame。C# 与 C++ 类似,据报道,“使用 Visual Studio 和 VS Code 作为集成开发环境 (IDE) 时,C# 的设置更简单、更便捷。” [2] C++ 和 C# 之间也存在许多差异,在选择编程语言时应考虑这些差异。我们将重点介绍其中几个差异[3]

内存管理

在 C++ 中,程序员需要手动管理内存,而 C# 则有自动垃圾回收机制。C# 在这方面更方便,但 C++ 赋予程序员对内存使用方式的更多控制权,这有助于优化代码,使其运行速度更快,性能更流畅。如果项目规模较小,则可能不需要如此精细的内存控制。

指针

指针是一种引用内存地址的方式,我将在本文后面详细介绍。在 C++ 中,指针可以在程序的任何位置使用,而 C# 只能在不安全模式下使用指针。“不安全代码由于其固有的复杂语法以及潜在的内存相关错误(例如堆栈溢出、访问和覆盖系统内存),可能会导致稳定性和安全性问题。” [4]这意味着在 C# 中使用不安全模式时需要格外小心,也就是说,在编写项目代码时可能需要避免使用指针。

Java

Java 是一种面向对象的编程语言,类似于 C++,并且“Java 虚拟机 (JVM) 用于运行字节码,这使其几乎与任何平台兼容。” [2] Java 和 C++ 之间的区别与 C# 类似,让我们来探讨一下原因[5]

内存管理

在 Java 中,内存管理由 Java 虚拟机 (JVM) 控制,而 C++ 是手动控制的。

平台依赖性

两种语言都可以在任何平台上运行。Java 使用 Java 虚拟机 (JVM) 来实现这一点。而 C++ 则需要正确的编译器来编译代码,使其适用于正确的平台。

指针

Java 可以使用指针,但对指针的支持有限。C++ 完全支持指针,甚至允许按引用调用值,而 Java 只能按指针的值进行调用。

选择 C++ 进行视频游戏开发

在开发电子游戏时,资源复用是自然而然的事情。设置障碍让玩家克服,并在之后再次遇到相同或类似的障碍,可以营造一种游戏进程感,展现玩家在游戏过程中的成长,或者让他们学会用不同的视角看待和克服障碍。这种结构听起来很像编程中的类,一种在整个应用程序中多次复用代码的方法。选择面向对象的编程语言对于更顺畅的开发过程至关重要。幸运的是,我提到的这些语言都支持基于类的编程技术。接下来,我们来看看为什么 C++ 经常被游戏项目选用。

就性能而言,使用 C++ 开发的视频游戏在运行速度和流畅度方面优于其他语言,尤其是在开发可扩展性强的游戏时。C++ 通过赋予程序员对内存管理的直接控制权来实现这一点。其他语言则通过垃圾回收器自动处理这些任务。“理解指针、内存分配和内存泄漏可以帮助你的游戏流畅运行,避免浪费宝贵的资源。” [1]我们将通过一些代码示例来详细讲解这三点。

指针

指针是一种变量,它将内存地址存储为其值。由于指针存储的是地址,因此我们可以进行按引用调用。指针可以“创建和操作动态数据结构” [6],并可用于遍历这些数据结构。

要使用指针,需要定义一个与要引用的数据类型相匹配的指针变量。数据类型必须与指针关联,以便指针“知道数据存储在多少字节中”。[6]使用一元运算符 & 指定要存储在先前创建的指针上的变量地址。要访问存储在某个地址上的值,请使用一元运算符 * 指定指针。

#include <bits/stdc++.h>
using namespace std;

void pointers() {
    int var = 20;

    // Declare pointer variable
    // Note that the data type of ptr and var must be the same
    int* ptr;

    // Declare pointer of a pointer variable
    int** ptr2;

    // Assign the address of a variable to a pointer
    ptr = &var;

    // Assign the address of a pointer to another pointer
    ptr2 = &ptr;


    // ptr holds the address of var
    cout << "Value at ptr = " << ptr << endl;

    // var holds the value of 20
    cout << "Value at var = " << var << endl;

    // * dereferences ptr to give the value of 20 
      // located at the address assigned to ptr
    cout << "Value at *ptr = " << *ptr << endl;

    // ptr2 holds the address of ptr
    // Even pointers have addresses of their own
    cout << "Value at ptr2 = " << ptr2 << endl;

    // Dereferencing ptr2 once reveals that 
      // ptr2 references the same address as ptr
    cout << "Value at *ptr2 = " << *ptr2 << endl;

    // Dereferencing ptr2 twice reveals 20, 
      // the value you receive when dereferencing ptr once
    cout << "Value at **ptr2 = " << **ptr2 << endl;
}

int main() {
    pointers();
    /*
     * Value at ptr = 0x6caebffc54
     * Value at var = 20
     * Value at *ptr = 20
     * Value at ptr2 = 0x6caebffc48
     * Value at *ptr2 = 0x6caebffc54
     * Value at **ptr2 = 20
     */
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

由于我们存储了其他数据类型的地址,因此可以模拟按引用调用。我们可以在函数内部修改任何数据类型,并在代码后续部分重用更新后的数据。

动态内存分配

如果我们预期输入数据量达到某个最大值,我们可以预先为程序预留足够的内存来应对最坏情况。但随着项目规模的不断扩大,这可能会成为一个问题。如果我们能够只在确定所需内存量后才分配内存呢?这样可以避免不必要的内存使用,并使代码运行更加高效。这就是动态内存分配的核心原则:只分配完成当前任务所需的内存空间,并在任务完成后立即释放剩余空间。

请查看以下代码[7],其中添加了注释,以帮助您了解我们如何实现动态内存分配:

#include <iostream>
#include <new>
using namespace std;

int main() {
    // i => Used for looping
    int i;
    // n => Used for capturing the input for the first question
    int n;
    // ptr => Pointer used to reference memory that is allocated
    int* ptr;

    // n is assigned the input from the user
    cout << "How many numbers would you like to type? ";
    cin >> n;

    // new => Allocates space match the input for n
    ptr = new (nothrow) int[n];

    // Check to make sure ptr points to a valid object
    if (ptr == nullptr) {
        cout << "Memory allocation error!" << endl;
    }

    else {
        for (i=0; i<n; i++) {
            // Ask for a number n times
            cout << "Enter number: ";

            // Store the number entered in memory
            cin >> ptr[i];
        }

        // Reveal to the user the choices they made
        cout << "You have entered: ";

        for (i=0; i<n; i++) {
            // Pull each number from the allocated memory
            cout << ptr[i] << ", ";
        }

        // After we finish with the task,
            // we free up the space taken using delete[]
        delete[] ptr;
    }
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

输出:
输出结果提示用户选择要输入的数字个数。用户选择 6。然后输入 4、8、15、16、23、42。之后,程序列出所有输入的数字,然后结束程序。

在 C++ 编程中,使用 delete 关键字至关重要。由于程序员需要手动清理内存,因此养成在内存使用完毕后及时清理的良好习惯,可以减少挫败感和 bug,并避免可怕的内存泄漏。

内存泄漏

在 C++ 中,没有自动垃圾回收机制,这意味着程序员在程序生命周期内动态分配的任何内存,都需要在使用完毕后手动释放。如果程序员忘记释放这些内存,它们就会一直占用空间,直到程序停止运行,其他进程也无法访问。通常,一个函数可能需要被多次调用,如果每次调用都分配了新的内存而没有及时释放,就会积累大量的未使用内存。这种不必要的内存占用被称为内存泄漏。内存泄漏会显著降低程序的运行速度,但为了提高代码的运行速度,这是可以避免的。[8]

关于 C++ 在游戏开发中的应用的最后思考

完全掌控内存使用是一大优势,或者说是“锦上添花”。我们创建指针来存储新创建的数据结构的地址。存储的内存使用完毕后,我们会将数据从内存中移除,以便在程序后续执行时释放空间,防止内存泄漏。C++ 的面向对象编程语言特性也体现了代码重用的重要性。

C++并非游戏开发语言的唯一选择。Java具有良好的可移植性,可以跨平台使用,但在制作3D游戏方面存在局限性。C#(夏普)比C++更简单,但除非使用不安全模式,否则它对指针的支持几乎为零。不安全模式不建议使用,除非您在该模式下编程时格外小心。

由于 C++ 允许程序员直接控制内存,因此可以利用指针和动态内存分配实现极快的运行速度。速度是设计大型视频游戏时需要考虑的重要因素,因为响应迟缓或无响应的游戏体验会让用户感到不满。

祝您编程愉快,
Tyler Meyer

来源

资料来源:
[1] https://www.geeksforgeeks.org/cpp-for-game-development/

[2] https://www.orientsoftware.com/blog/gaming-programming-language/

[3] https://www.geeksforgeeks.org/c-vs-c-sharp/

[4] https://www.c-sharpcorner.com/UploadFile/f0b2ed/understanding-unsafe-code-in-C-Sharp/

[5] https://www.geeksforgeeks.org/cpp-vs-java/

[6] https://www.geeksforgeeks.org/cpp-pointers/

[7] https://cplusplus.com/doc/tutorial/dynamic/

[8] https://www.geeksforgeeks.org/memory-leak-in-c-and-how-to-avoid-it/

文章来源:https://dev.to/tymey/why-are-people-choosing-c-for-game-development-29fp