Chinaunix首页 | 论坛 | 博客
  • 博客访问: 619345
  • 博文数量: 69
  • 博客积分: 1891
  • 博客等级: 上尉
  • 技术积分: 1359
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-20 23:38
文章分类

全部博文(69)

文章存档

2012年(46)

2011年(23)

分类: C/C++

2011-05-24 01:04:38

翻译自 

原文以及源代码的版权属于Tim Jones和他的网站。

本译文版权所有,转载请指明出处。

 

 

  这个教程是写给那些有C 经验,或者有其他语言编程经验的人的。如果你对于代码中的语言细节,而非游戏有关的概念问题比较纠结的话,那么我强烈要求你先阅读我们的C 教程。我并不要求你掌握C 的的所有细节,只要一点点基本的就够用了。

      在这些教程中,我们将选CodeBlocks作为IDE,用gccmingw作为编译工具。但是你也可以自由选择其他的IDE,但是如果你对于如何连接第三方库不是很了解的话,那么对于你实践本教程中的例子会有一些障碍的。如果你愿意的话,你可以在下载CodeBlocks(下载包含mingw包的版本)。我们建议你用稳定版本,除非你想多花点时间来使用每晚更新的测试版本。

    这个教程将以SDL(Simple DirectMedia Layer),一个2D的跨平台图形库为中心。这个库将能帮助我们在屏幕上画出有趣的图形,也能帮助我们完成制作有趣游戏所需要的所有功能。你需要在下载这个库。在“Development Libraries”和"Win32"目录下下载Mingw32 tar包,在"Runtime Libraries""Win32"目录下下载zip包。如果你用Visual Studio的话,那么就下载Visual Studio对应的文件。我们建议你把zip包中的dll文件放到system32目录下。这样的话,SDL程序运行的时候就能自动加载对应的dll文件了。

       打开所下载的tar包,解压出来到一个文件夹里,例如C:\SDL。现在启动CodeBlocks来进行设置。点击菜单栏中的Settings(8.02以后的版本还要点Compiler and debugger),然后点击Search Directories选项卡。你需要把D:\SDL\include加入到Compiler选项卡里,把D:\SDL\lib加入到Linker选项卡里。

    新建空白项目,随便命名,保存一下。点击菜单上的Project,选择Properties,在弹出的对话框里点击"Projects build options"按钮。点击"Linker Settings"选项卡,然后把下面的库加入:

mingw32

SDLmain

SDL

记住,加入的顺序很重要,可以用旁边的箭头调整顺序,一定要和上面保持一致。如果你不明白这一步是在干什么的话,我可以简单解释一下:我们这是在简单地把代码“连接”在一起,或者,换句话说,我们是在把SDL的代码和我们的代码结合在一起,我们用include文件来编译,用lib文件来连接。一旦连接成功,SDL的代码便和我们的代码一起构成了一个应用程序。

      确定两次,那么所有的设定都完成了。

      现在创建两个文件CApp.hCApp.cpp;这个做为我们程序的核心。首先,打开CApp.h把下面的代码加入,这将是我们教程的开始:



  1. #ifndef _CAPP_H_

  2. #define _CAPP_H_

  3. #include <SDL.h>

  4. class CApp {

  5.     public:

  6.         CApp();

  7.         int OnExecute();

  8. };

  9. #endif

现在,打开CApp.c加入以下代码:


  1. #include "CApp.h"

  2. CApp::CApp() {

  3. }

  4. int CApp::OnExecute() {

  5.     return 0;

  6. }

  7. int main(int argc, char* argv[]) {

  8.     CApp theApp;

  9.     return theApp.OnExecute();

  10. }

CApp负责设置整个程序的状态。现在请允许我岔开话题,重点讲述一下一个典型程序的框架。大部分的游戏包含5个用来处理游戏该如何进展的函数。这5个函数基本如下:

 

Initialize

这个函数负责装载所有数据,无论是纹理,地图,NPC还是其他的什么。

 

Event

这个函数处理来自鼠标,键盘,游戏杆或者其他设备的事件。

 

Loop

这个函数负责处理所有的数据更新事件,例如NPC在屏幕上移动,你的血量减少,或者其他的。

 

Render

这个函数负责渲染所有显示在屏幕上的东西。它负责处理数据,数据是由Loop函数进行处理的。

 

Cleanup

这个函数负责清理所有加载的资源,保证游戏正常退出。

 

     要理解游戏是一个很大的循环这个概念,在这个循环中,我们接收事件,更新数据,然后渲染图片。因此,游戏的基本框架可以任务是如下代码所表示的:


  1. Initialize();

  2. while(true){

  3. Events();

  4. Loop();

  5. Render();

  6. }

 

Cleanup();


     每次循环,我们都对数据进行一些处理,并且根据数据进行渲染。事件做为用户操作数据的一种方式是例外,在这个意义上,事件在游戏中不是一定需要的,但是在与用户交互(例如把一个NPC移到左边去)。

     让 我来用个例子来说明这一切。假设我们有个骑士,他是这个游戏的英雄。我们所要做的只是让他能够移动。如果我按下左箭头,他就往左走。那么我们就要弄弄白在 我们游戏的循环中怎么来进行处理。首先,我们需要检查事件(这里是键盘事件)。因为事不同的事件都将意味着我们要对数据进行处理,那么我们将需要一些变量 来保存数据,并根据事件来进行修改。然后我们就可以根据这些变量来在屏幕上合适的位子画出我们的骑士了。那么就有了下面的代码:


  1. if(Key == LEFT) X–;

  2. if(Key == RIGHT) X ;

  3. if(Key == UP) Y–;

  4. if(Key == DOWN) Y ;//… somewhere else in our code …

  5. RenderImage(KnightImage, X, Y);

      按照上面的代码就可以实现我们需要的功能了,因为每次循环都会检查是哪个按键按下了,然后根据按键修改对应的变量值。因此,如果我们的游戏运行速度是30/秒并且我们按下左箭头的话,我们的英雄每秒就会移动30个像素。如果你还是不很明白游戏的循环的话,在后面你也会慢慢明白的,因为我们的游戏需要靠循环来保证正常运行。

    

   现在回到我们之前的框架代码上去,即是那5个函数,现在我们可以添加以下几个文件到我们的项目中去。

 

CApp_OnInit.cpp

CApp_OnEvent.cpp

CApp_OnLoop.cpp

CApp_OnRender.cpp

CApp_OnCleanup.cpp

 

   再次打开CApp.h,把下面的函数和变量加入到源码中:


  1. #ifndef _CAPP_H_

  2. #define _CAPP_H_

  3. #include <SDL.h>

  4. class CApp {

  5.     private:

  6.         bool Running;

  7.     public:

  8.         CApp();

  9.         int OnExecute();

  10.     public:

  11.         bool OnInit();

  12.         void OnEvent(SDL_Event* Event);

  13.         void OnLoop();

  14.         void OnRender();

  15.         void OnCleanup();

  16. };

  17. #endif

 

    依次打开我们刚才创建的5个文件,把下面的函数加入:

  1. #include "CApp.h"

  2. bool CApp::OnInit() {

  3.     return true;

  4. }

  1. #include "CApp.h"

  2. void CApp::OnEvent(SDL_Event* Event) {

  3. }
  1. #include "CApp.h"

  2. void CApp::OnLoop() {

  3. }

  1. #include "CApp.h"

  2. void CApp::OnRender() {

  3. }

  1. #include "CApp.h"

  2. void CApp::OnCleanup() {

  3. }

 

现在,返回到CApp.h文件,把上面的所有函数连接起来:


  1. #include "CApp.h"

  2. CApp::CApp() {

  3.     Running = true;

  4. }

  5. int CApp::OnExecute() {

  6.     if(OnInit() == false) {

  7.         return -1;

  8.     }

  9.     SDL_Event Event;

  10.     while(Running) {

  11.         while(SDL_PollEvent(&Event)) {

  12.             OnEvent(&Event);

  13.         }

  14.         OnLoop();

  15.         OnRender();

  16.     }

  17.     OnCleanup();

  18.     return 0;

  19. }

  20. int main(int argc, char* argv[]) {

  21.     CApp theApp;

  22.     return theApp.OnExecute();

  23. }

 

    看看上面的代码你会发现多了很多新的变量,现在让我们来看看整个代码。首先,我们初始化我们的程序,如果初始化错误,那么就返回-1(错误代码),这将导致我们的程序关闭。如果初始化成功的话,程序就会继续循环。在循环中,我们用SDL_PollEvent来检查事件,然后把事件一次一个地传递给OnEvent。事件处理完后,就进入程序循环来操作数据等等,然后对我们的游戏进行渲染。我们的程序就是这样的一个无限循环。如果用户退出游戏的话,我们就进入到OnCleanup来进行资源清理。就这么简单!

     现在,我们再看看SDL_EventSDL_PollEventSDL_Event是用来封装事件信息的一个结构,而SDL_PollEvent是用来取得在事件列队中的事件。这个列队可以包含任意数目的事件,这就是我们为什么要不断的循环的原因了。例如,假设在OnRender函数执行期间用户按下A并且移动鼠标。SDL将会探测到这些,并且把这些时间放到时间列队中,一个是按键事件,一个是鼠标移动事件。我们可以用SDL_PollEvent从事件列队里取得事件,然后将事件交给OnEvent来进行相应的处理。一旦事件列队里没有任何事件了,SDL_PollEvent将会返回false,然后退出对事件列队的循环处理。

     Running 是我们添加的另外的变量,这个是让我们退出游戏循环的变量。当它被设置成false时,它将会使得程序终止。因此,当用户按下Esc键的时候,我们可以把这个变量设置成false,然后程序就退出了。

    现在整个程序应该能够完成编译了,但是运行的时候你会发现程序无法退出了。你可能要用任务管理器来终止程序了。

    现在所有的东西都已经配制好了,就让我们开始创建游戏运行的窗口。打开CApp.h,然后添加一个SDL surface 变量到代码中:


  1. #ifndef _CAPP_H_

  2.     #define _CAPP_H_

  3. #include <SDL.h>

  4. class CApp {

  5.     private:

  6.         bool Running;

  7.         SDL_Surface* Surf_Display;

  8.     public:

  9.         CApp();

  10.         int OnExecute();

  11.     public:

  12.         bool OnInit();

  13.         void OnEvent(SDL_Event* Event);

  14.         void OnLoop();

  15.         void OnRender();

  16.         void OnCleanup();

  17. };

  18. #endif

        我想,现在是时候来解释一下SDL Surface(由于没有专业的术语,SDL Surface 我没有翻译) 究竟是什么了。SDL Surface是一个可以作为画图目标的也可以作为画图的源的东西,即是可以作为画纸,也可以作为调色板。假设我们有一张白纸,一直铅笔,还有一些贴画;那么这张纸就可以作为我们的显示面板(display "surface")。我们可以在上面用笔画,也可以在上面贴图,也可以做其他的。贴图也是一种surface,我们也可以在贴图上进行回话,同样也可以在贴图上再贴上贴图。因此,调用Surf_Display只是简单地创建了一张“白纸”,我们将会在上面画出我们所需要的一切。

 

     现在,我们就在CApp_OnInit里来创建这个surface:


  1. #include "CApp.h"

  2. bool CApp::OnInit() {

  3.     if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {

  4.         return false;

  5.     }

  6.     if((Surf_Display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) {

  7.         return false;

  8.     }

  9.     return true;

  10. }

   我们要做的第一件事是启动SDL本身,这样我们才能使用SDL的函数。首先,我们通过SDL_INIT_EVERYTHINGSDL初始化它的所有资源,我们也可以用其他的参数来初始化,但是现在来了解他们不是很重要。下一个使用的函数是SDL_SetVideoMode。就是这个家伙帮助我们创建了窗口和surface。它需要4个参数:窗口的宽度,高度,颜色的深度(16位或者32位),最后一个是display flagdisplay flag有很多可选的值,但是上面所实用的对于我们这个程序已经足够了。第一个flag,SDL_HWSURFACE是用来告诉SDL实用硬件的内存来保存我们的图像等资源,第二个flagSDL_DOUBLEBUF告诉SDL要使用双缓冲(这个很重要,否则的话会有闪屏的)。还有一个标志你可能会感兴趣的是SDL_FULLSCREEN,这个是让程序全屏的标志。

   

    现在显示的部分也搞定了,让我们来做一些清理工作来保证所有的东西都是正常的。打开CApp_OnCleanup.cpp,加入以下代码:


  1. #include "CApp.h"

  2. void CApp::OnCleanup() {

  3.     SDL_FreeSurface(Surf_Display);

  4.     SDL_Quit();

  5. }

   我们第一件要做的就是释放我们所使用的surface,这样基本上会释放所有占用的内存。一旦这一步完成了,就可以退出SDL了。你需要注意的是,在这个函数里你需要同时释放其他的所有使用的surface(也许需要在Surf_Display之前进行)。这样就能够让你的代码专注于完成他们自己的工作了。

    为了保持一致,我们需要在这个类的构造函数里把Surf_Display设置成NULL。打开CApp.cpp文件,加入以下代码:


  1. CApp::CApp() {

  2.     Surf_Display = NULL;

  3.     Running = true;

  4. }

   现在试着编译你的代码,然后运行起来。你现在应该可以看到一个空白的窗口弹出来了。你会发现你现在还是不能关闭这个窗口,依旧需要用任务管理器来关闭它。

    现在我们已经建立起了一个窗口,但是我们还需要能够关闭它。打开CApp_OnEvent.cpp文件,加入以下代码:


  1. #include "CApp.h"

  2. void CApp::OnEvent(SDL_Event* Event) {

  3.     if(Event->type == SDL_QUIT) {

  4.         Running = false;

  5.     }

  6. }

    SDL事件被分成了不同的类别,它们包含从键盘按下到鼠标移动的各种类别,这里我们只是简单地做了类型检查。我们这里所需要的类型是关闭窗口的请求(例如,当用户按下窗口上的"X")。如果那个事件发生了,我们就把Running 设置成false,这样我们的程序就结束了。简单吧!在后面我们将会对事件进行深入讲解的。

    现在,你应该把整个工程都建立好了的,而且还有一个很好的框架供以后使用。如果你把这个工程在Codeblocks里做成一个"SDL template"那将更好的。我不会讲解这部怎么实现的,你可以google一下。

 

   如果你已经掌握了我们这节所将的内容,那么你可以进入下一节去了解更多的关于SDL surface的内容了。

 

 

====================================================

源码文件:

阅读(1867) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~