65.9K
CodeProject 正在变化。 阅读更多。
Home

SpaceWarrior - 2D DirectDraw 游戏 - 第一部分

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.64/5 (17投票s)

2007 年 1 月 3 日

5分钟阅读

viewsIcon

37645

一篇关于创建简单的 2D DirectDraw 游戏(入门)的文章。

The Book - Screenshot

引言

很久以前,我决定制作一款现代电脑游戏,于是立刻开始编码。但当时存在一个问题。要制作“哪种类型的游戏”?我的游戏“故事”是什么?在哪里可以找到我的游戏的“图形”?它将是“2D 还是 3D”游戏?最后,我“如何”才能完全创建游戏呢?

我的第一件作品是基于 **GDI** 的横版卷轴游戏。但是,真正的游戏开发者永远不会使用 Windows GDI 来编程电脑游戏。为什么?它并不是一个很难学习和使用的东西。任何人都可以使用键盘按键或鼠标让一个简单的精灵在屏幕上移动。那么问题出在哪里呢?

Windows GDI 对于游戏编程来说太慢了。但 BitBlt() 工作得很快,对吧?制作游戏还需要什么?

嗯,基本上,你可以只用 GDI 来制作游戏。但是当 GDI 必须访问你电脑上的显卡时,它会与 Windows DDI(设备驱动程序接口)一起工作。此外,使用 MCI 进行声音播放还会进一步降低系统的性能。但是,你仍然可以勉强制作一个简单的 2D 卷轴游戏。如果你想制作类似《帝国时代》的策略游戏呢?或者任何类型的 3D 游戏呢?用 GDI 就行不通了。你必须寻找另一个替代品。

然后,我意识到我需要 **DirectX** 及其组件。但是,我对它一无所知。每个人都说要自己学习它非常困难。我深知,微软的文档虽然涵盖了一切,但并不是最好的学习方式。

然后,我在 Google 上搜索“电脑游戏书籍”。出现了许多书籍,但其中一本与众不同。它的名字对全世界的电脑游戏开发者来说都非常熟悉——安德烈·拉莫特(Andre LaMothe)的《Tricks of the Windows Game Programming Gurus》(《Windows 游戏编程大师的技巧》)。

这本书大约有 1000 页,涵盖了游戏编程任务的每一个部分。而这个列表中并没有一个任务……所以,在这里我将为你简要回顾这本书:里面有什么,如何使用它,下一步去哪里……

本书

阅读本书将直接引你进入第一个游戏编程问题:“要创建哪种类型的游戏”?所以,你必须决定第一个游戏要创建的类型。它必须是一个简单的游戏,只是为了学习游戏开发过程的概念。好的,我说,它将是一个 2D 射击游戏。3D 游戏仍然超出了我的能力范围,所以我不会在这里浪费你的时间。

游戏的故事是什么?同样,要简单一些,比如你要穿越的小行星带,在消灭敌人并反击敌人的同时前进。

游戏图形?嗯,要么你窃取别人的作品,要么你自己制作。使用什么工具?一些 3D 建模软件和绘画程序就足够了。

游戏中的声音?尝试使用一些声音编辑软件来修改现有的声音或创建你自己的声音。

现在,我们已经有了将这些部分组合在一起以获得游戏所需的所有元素,对吧?差不多了,但我们仍然需要一个粘合剂将这些东西固定在一起,否则每个部分都会走向自己的毁灭之路。在这里,这本书开始混合出一些东西,将我们带出这种困境。

每个游戏是如何运作的,又是如何组织的?每个游戏都有开始、中间部分和结束。其中两部分可以为你准备好,因为它们涵盖了 DirectX 组件的加载和卸载。所以,这意味着我们的主要游戏内容在中间部分,对吧?绝对正确。那么,这个中间部分包含什么呢?这是必须发现的一个百万美元的秘密。让我们从这本书的一点帮助开始。

在开始构建游戏之前,我们需要知道什么?精通一门编程语言就足够了,我个人偏好 C++。我们还需要一些关于 Windows 系统如何组织的常识:窗口、控件、菜单、资源等。在我们的游戏中,我们只需要一个窗口——主游戏窗口,一些上面提到的游戏资源(声音和图形),以及一个游戏故事。我们不需要标准控件或自定义控件,不需要菜单,不需要其他任何东西,只需要一颗良好的意愿和一些额外的时间。

主游戏窗口

在这里,我们将创建我们非常需要的主游戏窗口。这里不会提到 MFC,所以如果你不喜欢 Win32 SDK,那么你可以寻找其他处理该主题的游戏文章。顺便说一句,你不需要 MFC 来制作游戏。让我们在 MS Visual Studio 中创建一个空的 **Win32 项目**,并添加一个名为 main.cpp 的新文件,内容如下:

// Some usual definitions and headers

#define WIN_32_LEAN_AND_MEAN

#include "windows.h"
#include "windowsx.h"


// Declarations we use
#define WINDOW_CLASS_NAME        "MyWindow_Class"

// Macros we use
#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)


// Some global variables that we need
HWND g_hMainWnd = NULL;
HINSTANCE g_hInstance = NULL;


// Some global functions we need also
LRESULT CALLBACK WindowProc(HWND hWnd, 
        UINT msg, WPARAM wParam, LPARAM lParam);
int Game_Init(void* pParms=NULL, int numParms=0);
int Game_Main(void* pParms=NULL, int numParms=0);
int Game_Shutdown(void* pParms=NULL, int numParms=0);


// Now comes the application entry point
int WINAPI WinMain(HINSTANCE hInstance, 
    HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    // Some local variables
     WNDCLASSEX winClass;
    HWND hWnd;
    MSG msg;

    // Fill window class structure which describes the main game window
    winClass.cbSize = sizeof(WNDCLASSEX);
    winClass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    winClass.lpfnWndProc = WindowProc;
    winClass.cbClsExtra = 0;
    winClass.cbWndExtra = 0;
    winClass.hInstance = hInstance;
    winClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    winClass.lpszMenuName = NULL;
    winClass.lpszClassName = WINDOW_CLASS_NAME;
    winClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    // The main game window will be black with default Windows icon and cursor

    // Save application instance handle
    g_hInstance = hInstance;

    // Register window class
    if (!RegisterClassEx(&winClass))
    {
        return (0);
    }

    // Create the main game window
    if (!(hWnd = CreateWindowEx(NULL, WINDOW_CLASS_NAME, "Main Game Window", 
        WS_POPUP | WS_VISIBLE, 0, 0, 400, 300, NULL, NULL, hInstance, NULL)))
    {
        return (0);
    }

    // Save main game window handle
    g_hMainWnd = hWnd;

    // Initialize game here
    if (!Game_Init())
    {
        return (0);
    }

    // Enter main event loop
    while (TRUE)
    {
        // Test for a window message
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // Test for quit message
            if (msg.message == WM_QUIT)
                break;

            // Translate any accelerator keys
            TranslateMessage(&msg);

            // Send message to window procedure
            DispatchMessage(&msg);
        }

        // Main game processing here
        if (!Game_Main())
        {
            break;
        }
    }

    // Shutdown game here
    if (!Game_Shutdown())
    {
        return (0);
    }

    // Return to Windows
    return (msg.message);
 }

所以,你在这里看到了我们游戏的所有三个部分。只有 Game_Main() 对我和你来说才有趣,因为其他两个(Game_Init()Game_Shutdown())将来可能会改变。我们将在这一单个函数调用中编写所有的游戏逻辑。为什么?因为我们不需要更多了。

消息循环

这部分很简单。上面我们提供了名为 WindowProc() 的主游戏窗口消息过滤器函数。它在这里:

LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
     // Check the message
    switch (msg)
    {
        case WM_DESTROY:
        {
            // Send quit message
            PostQuitMessage(0);
        }
        break;

        default:
        {
            // Default message processing
            return DefWindowProc(hWnd, msg, wParam, lParam);
        }
        break;
    }

    return (0);
}

我们可以在此函数的正文中处理用户的所有键盘或鼠标输入,我们可以销毁主游戏窗口,或者创建另一个窗口,暂停我们的游戏等。直到这一部分,所有 Win32 应用程序都是完全相同的。现在是游戏部分。

游戏部分

我们上面声明了三个游戏函数:Game_Init()Game_Main()Game_Shutdown()。它们在这里:

int Game_Init(void* pParms, int numParms)
{
     // We will init the DirectX sub-system here

    return (1);
}

int Game_Main(void* pParms, int numParms)
{
     // We will write our game logic here

    // Test for ESC key pressed
    if (KEYDOWN(VK_ESCAPE))
    {
        // Destroy main game window
        PostMessage(g_hMainWnd, WM_CLOSE, 0, 0);
    }

    return (1);
}

int Game_Shutdown(void* pParms, int numParms)
{
     // We will shutdown the DirectX sub-system here

    return (1);
}

这就是创建主游戏窗口所需要的一切。你只需要游戏元素……

结论

你可以构建本文提供的示例并运行它。你应该看到一个 400x300 像素的黑色窗口,按键盘上的 ESC 键可以关闭它。

© . All rights reserved.