SDL游戏教程第二课 坐标与块复制

翻译声明:
    本系列教程来自
Dev Hub,一切解释权归原作者。我只是出自个人爱好,才翻译了本系列教程。因为本人也是个初学者,而且英语水平有限,错误难免,望各路高手指正。

本课原文地址:http://www.sdltutorials.com/sdl-coordinates-and-blitting/

有第一节课做基础,我们将深入研究SDL表面世界。正如我在上节课所述,SDL表面就是存储在内存中的图像。假想我们有一个空白的320*240像素的表面。SDL坐标系统如下图所示:


这里的坐标系统和你熟悉的可能不大一样。注意,Y坐标向下递增,X坐标向右递增。了解SDL的坐标系统对于正确地向屏幕绘制图像很重要。

既然我们已经搞定了主表面(Surf_Display),那就需要一种方式来在其上绘制图像了。这个过程就叫做"块复制"(译者注:英文是Blitting),由此我们可以把一个图像迁移到另一幅图像。但在此之前,我们需要一种方式来把图像加载到内存。SDL提供了一个简单的函数来实现,那就是SDL_LoadBMP()。一些伪代码可能是这样的:

SDL_Surface* Surf_Temp;

if((Surf_Temp = SDL_LoadBMP("mypicture.bmp")) == NULL) {
    //Error!
}


超简单,SDL_LoadBMP()只需要一个参数,就是你要加载的文件,并且返回一个表面。如果文件找不到,已损坏,或其他什么错误,此函数就会返回NULL。不幸的是,有时这种方法还不够。加载图片的像素格式往往和要显示的不一致。这样的话,当我们想显示器绘制图像的时候会导致性能损失、图像颜色失真、或是别的什么。但是,多亏了SDL,它还为此提供了一个快捷的解决方法,SDL_DisplayFormat()。此函数需要一个已经加载的表面,然后返回一个和显示器格式一致的新表面。

让我们完成这个过程,并把它放在一个可再用的类里。使用SDL课程1作为基础代码,然后添加以下两个新文件:CSurface.h,CSurface.cpp。打开CSurface.h,增加如下:
  1. #ifndef _CSURFACE_H_
  2. #define _CSURFACE_H_
  3. #include <SDL.h>
  4. class CSurface {
  5.     public:
  6.         CSurface();
  7.     public:
  8.         static SDL_Surface* OnLoad(char* File);
  9. };
  10. #endif

我们已经创建了一个简单的静态函数,OnLoad,它会为我们加载一个表面。现在打开CSurface.cpp:

#include "CSurface.h"
  1. CSurface::CSurface() {
  2. }
  3. SDL_Surface* CSurface::OnLoad(char* File) {
  4.     SDL_Surface* Surf_Temp = NULL;
  5.     SDL_Surface* Surf_Return = NULL;
  6.     if((Surf_Temp = SDL_LoadBMP(File)) == NULL) {
  7.         return NULL;
  8.     }
  9.     Surf_Return = SDL_DisplayFormat(Surf_Temp);
  10.     SDL_FreeSurface(Surf_Temp);
  11.     return Surf_Return;
  12. }

在此有两件事需要注意。首先,切记在创建一个指针的时候一定要把它设置为NULL或0。不然,稍后你会遇到很多的麻烦。其次,注意SDL_DisplayFormat()是如何返回一个新表面,但并不覆盖原有的表面。这个一定要牢记,因为它创建了一个新的表面,我们必须释放那个旧表面。否则,我们会有一个表面还留在内存里(译者注:如果不释放旧资源,容易造成内存泄漏)。

现在我们有了把表面加载到内存的方法,我们还需要一个把他们绘制到其他表面的方法。就像SDL提供了一个函数来加载图像,它也提供了一个函数来绘制(blit)图像:SDL_BlitSurface()。不幸的是,此函数并不像SDL_LoadBMP()那么简单,尽管如此,它还是很简单的。打开CSurface.h添加如下函数原型:
  1. #ifndef _CSURFACE_H_
  2. #define _CSURFACE_H_
  3. #include <SDL.h>
  4. class CSurface {
  5.     public:
  6.         CSurface();
  7.     public:
  8.         static SDL_Surface* OnLoad(char* File);
  9.         static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y);
  10. };
  11. #endif

现在打开CSurface.cpp,添加如下:
  1. #include "CSurface.h"
  2. CSurface::CSurface() {
  3. }
  4. SDL_Surface* CSurface::OnLoad(char* File) {
  5.     SDL_Surface* Surf_Temp = NULL;
  6.     SDL_Surface* Surf_Return = NULL;
  7.     if((Surf_Temp = SDL_LoadBMP(File)) == NULL) {
  8.         return NULL;
  9.     }
  10.     Surf_Return = SDL_DisplayFormat(Surf_Temp);
  11.     SDL_FreeSurface(Surf_Temp);
  12.     return Surf_Return;
  13. }
  14. bool CSurface::OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y) {
  15.     if(Surf_Dest == NULL || Surf_Src == NULL) {
  16.         return false;
  17.     }
  18.     SDL_Rect DestR;
  19.     DestR.x = X;
  20.     DestR.y = Y;
  21.     SDL_BlitSurface(Surf_Src, NULL, Surf_Dest, &DestR);
  22.     return true;
  23. }

首先,看看传递给OnDraw()的参数。两个表面和两个整型。第一个表面为目标表面,或是我们要在其上进行绘制的表面。第二个表面是源表面,或是我们要将其绘制到另一个表面的表面。基本上是这样,我们把Surf_Src放到Surf_Dest的上面。其中X,Y变量是在Surf_Dest上要绘制表面的位置。

函数的开始确保我们得到的是有效的表面,否则,返回false。然后,就是SDL_Rect。他是一个SDL结构体,基本包含4个成员:x,y,w,h。这个,当然是创建一个特定规格的矩形了。我们仅关心要在哪画,并不是绘制的大小。所以,我们把X,Y坐标分配到目标表面上。如果你想知道SDL_BlitSurface里的那个NULL是什么意思,它也是一个SDL_Rect的参数。本课后面还会在提到。

最后,我们可以调用这个函数来绘制图像,并返回true。

现在,为了确保所有这些可以运行,就让我们创建一个测试表面。打开CApp.h,然后创建一个新的表面,并包含进新创建的CSurface.h:
  1. #ifndef _CAPP_H_
  2. #define _CAPP_H_
  3. #include <SDL.h>
  4. #include "CSurface.h"
  5. class CApp {
  6.     private:
  7.         bool            Running;
  8.         SDL_Surface*    Surf_Display;
  9.         SDL_Surface*    Surf_Test;
  10.     public:
  11.         CApp();
  12.         int OnExecute();
  13.     public:
  14.         bool OnInit();
  15.         void OnEvent(SDL_Event* Event);
  16.         void OnLoop();
  17.         void OnRender();
  18.         void OnCleanup();
  19. };
  20. #endif

同样的,在构造函数里初始化这个表明为NULL:

CApp::CApp() {
    Surf_Test = NULL;
    Surf_Display = NULL;

    Running = true;
}


并且,还要记得清除:
  1. #include "CApp.h"
  2. void CApp::OnCleanup() {
  3.     SDL_FreeSurface(Surf_Test);
  4.     SDL_FreeSurface(Surf_Display);
  5.     SDL_Quit();
  6. }

现在,让我们实际加载一幅图像。打开CApp_OnInit.cpp,然后添加代码来加载一个表面:
  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.     if((Surf_Test = CSurface::OnLoad("myimage.bmp")) == NULL) {
  10.         return false;
  11.     }
  12.     return true;
  13. }

确保要把"myimage.bmp"改成一幅实际存在的位图文件。如果你没,那就打开画图,随便画点什么东西上去,保存到和可执行文件同目录下。

现在我们已经加载了图片,让我们绘制它。打开CApp_OnRender.cpp,添加如下:
  1. #include "CApp.h"
  2. void CApp::OnRender() {
  3.     CSurface::OnDraw(Surf_Display, Surf_Test, 0, 0);
  4.     SDL_Flip(Surf_Display);
  5. }

注意这里的一个新函数SDL_Flip()。它只是更新缓冲区,然后显示Surf_Display到屏幕。这就叫做双缓存。整个过程就是把所有东西绘制到内存,然后把所有东西最终绘制到屏幕。若不这么做,我们会看到图像在屏幕上闪烁。还记得SDL_DOUBLEFUL标志位吗?这就是我们为什么要打开双缓存的原因。

编译代码,然后确保一切运行正常。你应该可以在屏幕左上角可以看到你的图像。若是这样,祝贺你,你离一个真实的游戏更近了一步。若不然,确保在你的可执行文件夹里有myimage.bmp存在。也要保证它是一个有效的位图文件。



现在让我们把这个过程进行的更深远点。尽管它可以很好并足够把一幅图像绘制到屏幕上了,通常我们仅需要绘制一幅图像的一部分。例如,一个贴图集合:



尽管这只是一单幅图像,我们只想绘制其中一部分。打开CSurface.h,添加如下代码:
  1. #ifndef _CSURFACE_H_
  2. #define _CSURFACE_H_
  3. #include <SDL.h>
  4. class CSurface {
  5.     public:
  6.         CSurface();
  7.     public:
  8.         static SDL_Surface* OnLoad(char* File);
  9.         static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y);
  10.         static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y, int X2, int Y2, int W, int H);
  11. };
  12. #endif

打开CSurface.cpp,添加如下函数:

bool CSurface::OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y, int X2, int Y2, int W, int H) {
    if(Surf_Dest == NULL || Surf_Src == NULL) {
        return false;
    }

    SDL_Rect DestR;

    DestR.x = X;
    DestR.y = Y;

    SDL_Rect SrcR;

    SrcR.x = X2;
    SrcR.y = Y2;
    SrcR.w = W;
    SrcR.h = H;

    SDL_BlitSurface(Surf_Src, &SrcR, Surf_Dest, &DestR);

    return true;
}


注意下,它基本上和前一个函数一样,只不过我们增加了另一个SDL_Rect。我们通过这个源矩形指定从源中要拷贝那些像素到目标。如果我们指定了0,0,50,50作为X2,Y2,W,H的参数,那么只有源的左上角(一个50*50的方形)被绘制。



我们同样试试这个新函数,打开CApp_OnRender.cpp,然后添加:
  1. #include "CApp.h"
  2. void CApp::OnRender() {
  3.     CSurface::OnDraw(Surf_Display, Surf_Test, 0, 0);
  4.     CSurface::OnDraw(Surf_Display, Surf_Test, 100, 100, 0, 0, 50, 50);
  5.     SDL_Flip(Surf_Display);
  6. }

你应该能看到你的图片会被在100,100处绘制,且仅显示了一部分。你应该搞清楚这一切是怎么运行的,以及SDL的坐标系统是如何建立的,这将会很有用。

跳到下一课,在那我们会看到更多的SDL消息,以及如何使那个过程更简单。

SDL 坐标和块复制 —— 课程文件:
Win32: ZipRar
Linux: Tar(感谢Gaten)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值