支持HW团队,就支付宝领取下面的红包吧! (打开支付宝就能领取!er1OEj73Uj), (打开支付宝收索“516503473”), 你领取消费,HW有奖励。红包使用无条件限制,有条件请注意是不是有病毒。

Login or Sign up | Validate
| Search

博主:初学MPEG

初学MPEG 本博客-采用Python的web框架Django与Mysql数据库,致力于对Python、Django的了解 与研究
Django技术QQ群:XXXXXXX
Python技术QQ群:XXXXXXX

Category

Keywords

本站最新博文

友情链接  

[转]SDL screen surface类

类别:游戏 状态:游客可见,可回,会员可关联(良好) 阅读:4691 评论:0 时间:May 11, 2013, 5:24 p.m.
关键字:SDL

 来源:
http://www.cppblog.com/lf426/archive/2008/02/21/43042.html
http://www.cppblog.com/lf426/archive/2008/02/21/43043.html
http://www.cppblog.com/lf426/archive/2008/02/21/43047.html
http://www.cppblog.com/lf426/archive/2008/02/21/43050.html
http://www.cppblog.com/lf426/archive/2008/02/21/43051.html
作者:龙飞

1、构建SDL screen surface类
今天是元宵节,新年过去,又要开始抓紧时间奋斗了。祝大家都能继续追逐自己的梦想。BS的书上引过一句话,“一个人要是不耕作,就必须写作”,所以,不耕作的我不能停止写作,time is money, my friend!

1.1:整理两种SDL_Surface的关系。

前面一直在用面向过程的思想写程序,因为OOP细分到每一个具体的方法,还是过程。OOP的难点其实在于理清楚不同类之间的关系。说实话,我只是为了自己的理想,为了实现我计划的项目,刚刚开始学习C++的菜鸟。两个多月的C++能有什么水平,希望前辈们不要见笑,我会继续努力的。而且,可能因为对于C++的偏爱,再加上我目前能找到的SDL相关教程都是C风格的,所以我充满了用C++来写SDL教程挑战的热情。
SDL_Surface是SDL的一个结构。在我们前面的知识中,学习了构建这个结构的两种方法:一种是通过SDL_SetVideoMode();一种是SDL_LoadBMP()。其实,通过SDL_SetVideoMode()构建的SDL_Surface是一种特殊的surface,因为,实际上,其他的surface都是通过blit到这个surface上,最终通过flip这个surface,才能显示出来。所以,这个surface可以看成是SDL库中,数据形式的surface(储存在电脑中)与实体形式的surface(通过屏幕显示出来)的唯一接口。另外,因为构建这两种surface需要的数据成员小同大异,类方法也不尽相同。所以,虽然我也考虑过建立一个基类把两种surface作为派生类,但是我最终选择了建立两个类。

1.2:构建SDL screen surface类。

class ScreenSurface
{
private:
        static int screenNum;
        int width;
        int height;
        int bpp;
        Uint32 flags;
        SDL_Surface* pScreen;
public:
        ScreenSurface();
        ScreenSurface(int w, int h, int b = 0, Uint32 f = 0);
        ~ScreenSurface();
        SDL_Surface* point() const;
        bool flip() const;
};

我先设定了一个静态int作为计数器。我的考虑是,screen surface实际上只需要建立一个。并且,其他的surface实际上都是“依赖”于这个surface才能显示出来的。在SDL中,多次SDL_SetVideoMode()的效果实际上是保留了最后一次的surface作为了screen surface,所以,多次SDL_SetVideoMode()其实是没有实际意义的。计数器screenNum用于保证只创建一次screen surface,多次创建我倾向让程序抛出异常。
构建函数除了调用SDL_SetVideoMode()函数,还作为SDL_Init()的启动载入。所以,我专门定义析构函数的目的,是因为C++会在类对象消亡的时候自动调用析构函数。因为保证了只建立一个screen surface,并且在创建对象的时候载入了SDL_Init(),所以,如果析构函数中使用SDL_Quit(),则可以在手动调用析构函数或者程序结束的时候调用SDL_Quit()了。
方法point()返回对象中的pScreen,其实就是SDL_Surface结构的指针。因为SDL库是C风格的,所以,直接使用指针的函数很多。
方法flip()用于把screen surface最终显示出来。

1.3:screen surface的类方法。

int ScreenSurface::screenNum = 0;

首先为静态变量附初值。

ScreenSurface::ScreenSurface()
        : width(640), height(480), bpp(32), flags(0)
{
        if ( screenNum > 0 )
                throw "DONOT create more than ONE screen!";
        if ( SDL_Init(SDL_INIT_VIDEO < 0 ) )
                throw SDL_GetError();
        pScreen = SDL_SetVideoMode(width, height, bpp, flags);
        screenNum++;
}

ScreenSurface::ScreenSurface(int w, int h, int b, Uint32 f)
        : width(w), height(h), bpp(b), flags(f)
{
        if ( screenNum > 0 )
                throw "DONOT create more than ONE screen!";
        if ( SDL_Init(SDL_INIT_VIDEO < 0 ) )
                throw SDL_GetError();
        pScreen = SDL_SetVideoMode(width, height, bpp, flags);
        screenNum++;
}

构造函数。如果创建1个以上的screen surface,则会抛出异常。

ScreenSurface::~ScreenSurface()
{
        SDL_Quit();
}

析构函数。在对象消亡时,退出SDL系统。

SDL_Surface* ScreenSurface::point() const
{
        return pScreen;
}

返回screen surface中SDL_Surface结构的指针,主要提供给SDL的函数调用。

bool ScreenSurface::flip() const
{
        if ( SDL_Flip(pScreen) < 0 )
                return false;
        else
                return true;
}

显示(弹出flip)screen surface到屏幕上。
2、构建SDL surface类
2.1:构建普通的surface类。

在所有的surface里面,只有screen surface是最特殊的。因为第一,screen surface只有一个;第二,其他所有的普通surface都必须被blit到screen surface上,通过flip screen surface才能显示出来。所以,我们可以认为普通的surface是“依赖”于一个screen surface的。所以,考虑在构建surface的时候,除了需要装载的bmp文件,还需要指定其所依赖的screen surface。

class DisplaySurface
{
private:
        string fileName;
        SDL_Surface* pSurface;
        SDL_Surface* pScreen;
public:
        DisplaySurface(string file_name, const ScreenSurface& screen);
        ~DisplaySurface();
        SDL_Surface* point() const;
        bool blit() const;
};

2.2:surface的类方法。

DisplaySurface::DisplaySurface(std::string file_name, const ScreenSurface& screen)
        : fileName(file_name)
{
        pSurface = SDL_LoadBMP(file_name.c_str());
        if ( pSurface == 0 )
                throw SDL_GetError();
        pScreen = screen.point();
}

构造函数。我们指定一个bmp文件,和一个screen surface对象来构造surface。注意,这里我们用到了C++的string。我们前面说过,string的方法c_str()用于把C++的string对象转化为C风格字符串,而这正是SDL的函数所需要的。

DisplaySurface::~DisplaySurface()
{
        SDL_FreeSurface(pSurface);
}

虽然load的bmp可以在SDL_Quit()的时候自动释放,不过也许我们也会有需要手动释放的时候。比如,装载一张图片,进行某种处理和转换后,保留处理后的,就可以把原来用于处理的对象释放掉了。

SDL_Surface* DisplaySurface::point() const
{
        return pSurface;
}

返回对象中,bmp的SDL_Surface指针。同样为了用于SDL库的函数。

bool DisplaySurface::blit() const
{
        if ( SDL_BlitSurface(pSurface, 0, pScreen, 0) < 0 )
                return false;
        else
                return true;
}

把surface blit到screen surface上面,左上角重合。到这里,我们前面用到的对于surface的所有操作,都包含到类方法中了。

3、对SDL_BlitSurface()的进一步讨论
3.1:矩形区域SDL_Rect。

typedef struct{
        Sint16 x, y;
        Uint16 w, h;
} SDL_Rect;

因为显示器通常是矩形的,所以,矩形是计算机图形学中最基本的操作区域单元。这个结构很简单,x和y是矩形的左上角坐标。x从左到右增加;y从上到下增加。左上角的坐标就是(0,0)——SDL中就是这样的。w是矩形的宽,h是矩形的高。

3.2:进一步了解SDL_BlitSurface()。

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);

4个参数都是指针——2个SDL_Surface指针,2个SDL_Rect指针。src是源面,也就是被blit的面;dst是目的面,也就是源面被blit到的面。srcrect是源面上的一个矩形区域,实际上,正是这个矩形区域被blit,如果是空指针,则整个源面被blit;dstrect虽然是个矩形区域指针,但是实际上只用到了这个矩形左上角坐标的数据。所以,实际上,它是源面被blit到目的面上的坐标。如果是空指针,则被blit到目的面的左上角(0,0)。

3.3:为surface类增加新的方法。

class DisplaySurface
{
public:
        bool blit(int at_x, int at_y) const;
        bool blit(int at_x, int at_y, int from_x, int from_y, int w, int h, int delta_x = 2, int delta_y = 2) const;
};

我们重载了blit()方法。

bool DisplaySurface::blit(int at_x, int at_y) const
{
        SDL_Rect offset;
        offset.x = at_x;
        offset.y = at_y;

        if ( SDL_BlitSurface(pSurface, 0, pScreen, &offset) < 0 )
                return false;
        else
                return true;
}

这个方法,让整个源面被blit到目的面的(at_x,at_y)坐标上。这个函数,把源面上,一个左上角坐标是(from_x,from_y),宽为w,高为h的矩形区域,blit到目的面的(at_x,at_y)坐标上。

bool DisplaySurface::blit(int at_x, int at_y) const
{
        SDL_Rect offset;
        offset.x = at_x;
        offset.y = at_y;

        if ( SDL_BlitSurface(pSurface, 0, pScreen, &offset) < 0 )
                return false;
        else
                return true;
}

要说明delta_x和delta_y的作用,我们先思考一个问题:动画效果是如何实现的。
我们假设有个作为背景的surface,名为back,我们要让一个名为front的surface在back上动起来。显然,至少有两种方法:
1) 把front blit到back上,把back blit到screen上,flit screen,显示出原来的图像;
把back面“搽干净”;改变front的坐标,将改变坐标后的front blit到back上,把back blit到screen上,flit screen,显示出更新后的图像。
2) 把back blit到screen上,flit screen,首先显示出back图像;
先把back面“搽干净”;把front blit到screen上(back是一直保持在screen上的,而front会被blit到back的上层),flit screen,显示出更新后的图像。
因为,第二种方法把所有的surface都直接往screen上blit,思路更为简单,所以,我们先讨论这种方法。
而对于“搽干净”这个概念,又有两种思路:
1) 全部back更新;
2) 更新back上“被弄脏”的部分。
实际上,当前的电脑速度对在平面上的blit速度问题已经不再是问题了。但是,在不久之前,程序员们还在为了计算机图形的实现速度而绞尽脑汁。blit一部分应该是比blit全部图像要快的。所以,这个重载的blit()方法多用于对于back的blit。delta_x和delta_y是为了保证blit的back部分,比front大那么一点点。不然的话——实际上大家可以把delta_x和delta_y设置为0看看是什么效果。

4、让图片动起来!
4.1:再讨论简单的SDL event响应。

Uint8 *SDL_GetKeyState(int *numkeys);

要让图片动起来,最好是我们可以“操作”的动。按照一般思路,键盘的“上”“下”“左”“右”是不错的选择。在FPS游戏和模拟器中,我们可能更习惯wsad四个键,所以,让他们同时起作用吧。这个函数的一般用法,是把参数设置为空指针。我们还是先忽略细节。因为有了两个新的blit()重载方法的加入,我们设置要移动的图片在screen上的坐标是(xpos,ypos),则:

//key event for up, down, left and right.
Uint8* keys;
//moving image's coordinate.
int xpos = 0;
int ypos = 0;

控制图片移动的代码如下:

//key event to move image.
keys = SDL_GetKeyState(0);
if ( keys[SDLK_UP] || keys[SDLK_w] ){
        ypos -= 1;
}
if ( keys[SDLK_DOWN]|| keys[SDLK_s] ){
        ypos += 1;
}
if ( keys[SDLK_LEFT]|| keys[SDLK_a] ){
        xpos -= 1;
}
if ( keys[SDLK_RIGHT]|| keys[SDLK_d] ){
        xpos += 1;
}

代码一目了然,不用多解释。具体对应的按键可参考:
http://www.libsdl.org/cgi/docwiki.cgi/SDLKey

4.2:对于第二种方法的分析。

我们前面讨论了总是把surface blit到screen上的情况。如果我们的思路是把其他surface先blit到一个作为背景画布的surface上,最后把背景画布blit到screen上呢?
我们将遇到的第一个疑问是,surface能不能把自己blit到自己上面?试验的结果是否定的,而SDL并没有给出官方的异常信息,即SDL_GetError()没有起作用。所以,异常得我们自己来抛出。另外一个后果是,我们必须建立背景画布的拷贝,把拷贝blit到背景画布上才是可行的。

class DisplaySurface
{
public:
        bool blitToSurface(const DisplaySurface& dst_surface,
                int at_x = 0, int at_y = 0) const;
        bool blitToSurface(const DisplaySurface& dst_surface,
                int at_x, int at_y,
                int from_x, int from_y, int w, int h,
                int delta_x = 2, int delta_y = 2) const;
};

这和blit到screen是类似的,但是实际效果是完全不一样的。因为,没有被blit到screen上的surface,都不可能被flip显示出来。所以,虽然参数列表是不一样的,意味着我可以继续用blit()这个名字重载。但是为了表明他们的实际效果有区别,我重新用了方法名。

bool DisplaySurface::blitToSurface(const DisplaySurface& dst_surface, int at_x, int at_y) const
{
        SDL_Rect offset;
        offset.x = at_x;
        offset.y = at_y;

        if ( &dst_surface == this )
                throw "Cannot blit surface to itself!";

        if ( SDL_BlitSurface(pSurface, 0, dst_surface.point(), &offset) < 0 )
                return false;
        else return true;
}

bool DisplaySurface::blitToSurface(const DisplaySurface& dst_surface,
        int at_x, int at_y,
        int from_x, int from_y, int w, int h,
        int delta_x, int delta_y) const
{
        SDL_Rect offset;
        offset.x = at_x - delta_x;
        offset.y = at_y - delta_y;

        SDL_Rect dest;
        dest.x = from_x - delta_x;
        dest.y = from_y - delta_y;
        dest.w = w + delta_x*2;
        dest.h = h + delta_y*2;

        if ( &dst_surface == this )
                throw "Cannot blit surface to itself!";

        if ( SDL_BlitSurface(pSurface, &dest, dst_surface.point(), &offset) < 0 )
                return false;
        else return true;
}

5、本章范例的完整源代码
5.1:准备工作。

一张640*480大小的bmp文件作为背景,命名为:bg.bmp;
一张128*128大小的bmp文件作为要在背景上移动的图片,命名为:image.bmp。

5.2:头文件SurfaceClass.h

//FileName: SurfaceClass.h

#ifndef SURFACE_CLASS_H
#define SURFACE_CLASS_H

#include <iostream>
#include <string>
#include "SDL/SDL.h"

using std::string;

class ScreenSurface
{
private:
        static int screenNum;
        int width;
        int height;
        int bpp;
        Uint32 flags;
        SDL_Surface* pScreen;
public:
        ScreenSurface();
        ScreenSurface(int w, int h, int b = 0, Uint32 f = 0);
        ~ScreenSurface();
        SDL_Surface* point() const;
        bool flip() const;
};

class DisplaySurface
{
private:
        string fileName;
        SDL_Surface* pSurface;
        SDL_Surface* pScreen;
public:
        DisplaySurface(string file_name, const ScreenSurface& screen);
        ~DisplaySurface();
        SDL_Surface* point() const;
        bool blit() const;
        bool blit(int at_x, int at_y) const;
        bool blit(int at_x, int at_y,
                int from_x, int from_y, int w, int h,
                int delta_x = 2, int delta_y = 2) const;
        bool blitToSurface(const DisplaySurface& dst_surface,
                int at_x = 0, int at_y = 0) const;
        bool blitToSurface(const DisplaySurface& dst_surface,
                int at_x, int at_y,
                int from_x, int from_y, int w, int h,
                int delta_x = 2, int delta_y = 2) const;
};

#endif

5.3:类方法的实现文件:SurfaceClass.cpp

#include "SurfaceClass.h"

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
//class ScreenSurface

int ScreenSurface::screenNum = 0;

ScreenSurface::ScreenSurface():
width(640), height(480), bpp(32), flags(0)
{
        if ( screenNum > 0 )
                throw "DONOT create more than ONE screen!";
        if ( SDL_Init(SDL_INIT_VIDEO ) < 0 )
                throw SDL_GetError();
        pScreen = SDL_SetVideoMode(width, height, bpp, flags);
        screenNum++;
}

ScreenSurface::ScreenSurface(int w, int h, int b, Uint32 f):
width(w), height(h), bpp(b), flags(f)
{
        if ( screenNum > 0 )
                throw "DONOT create more than ONE screen!";
        if ( SDL_Init(SDL_INIT_VIDEO < 0 ) )
                throw SDL_GetError();
        pScreen = SDL_SetVideoMode(width, height, bpp, flags);
        screenNum++;
}

ScreenSurface::~ScreenSurface()
{
        SDL_Quit();
}

SDL_Surface* ScreenSurface::point() const
{
        return pScreen;
}

bool ScreenSurface::flip() const
{
        if ( SDL_Flip(pScreen) < 0 )
                return false;
        else return true;
}

//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
//class DisplaySurface

DisplaySurface::DisplaySurface(std::string file_name, const ScreenSurface& screen):
fileName(file_name)
{
        pSurface = SDL_LoadBMP(file_name.c_str());
        if ( pSurface == 0 )
                throw SDL_GetError();
        pScreen = screen.point();
}

DisplaySurface::~DisplaySurface()
{
        SDL_FreeSurface(pSurface);
}

SDL_Surface* DisplaySurface::point() const
{
        return pSurface;
}

bool DisplaySurface::blit() const
{
        if ( SDL_BlitSurface(pSurface, 0, pScreen, 0) < 0 )
                return false;
        else return true;
}


bool DisplaySurface::blit(int at_x, int at_y) const
{
        SDL_Rect offset;
        offset.x = at_x;
        offset.y = at_y;

        if ( SDL_BlitSurface(pSurface, 0, pScreen, &offset) < 0 )
                return false;
        else return true;
}

bool DisplaySurface::blit(int at_x, int at_y,
        int from_x, int from_y, int w, int h,
        int delta_x, int delta_y) const
{
        SDL_Rect offset;
        offset.x = at_x - delta_x;
        offset.y = at_y - delta_y;

        SDL_Rect dest;
        dest.x = from_x - delta_x;
        dest.y = from_y - delta_y;
        dest.w = w + delta_x*2;
        dest.h = h + delta_y*2;

        if ( SDL_BlitSurface(pSurface, &dest, pScreen, &offset) < 0 )
                return false;
        else return true;
}

bool DisplaySurface::blitToSurface(const DisplaySurface& dst_surface, int at_x, int at_y) const
{
        SDL_Rect offset;
        offset.x = at_x;
        offset.y = at_y;

        if ( &dst_surface == this )
                throw "Cannot blit surface to itself!";

        if ( SDL_BlitSurface(pSurface, 0, dst_surface.point(), &offset) < 0 )
                return false;
        else return true;
}

bool DisplaySurface::blitToSurface(const DisplaySurface& dst_surface,
        int at_x, int at_y,
        int from_x, int from_y, int w, int h,
        int delta_x, int delta_y) const
{
        SDL_Rect offset;
        offset.x = at_x - delta_x;
        offset.y = at_y - delta_y;

        SDL_Rect dest;
        dest.x = from_x - delta_x;
        dest.y = from_y - delta_y;
        dest.w = w + delta_x*2;
        dest.h = h + delta_y*2;

        if ( &dst_surface == this )
                throw "Cannot blit surface to itself!";

        if ( SDL_BlitSurface(pSurface, &dest, dst_surface.point(), &offset) < 0 )
                return false;
        else return true;
}

//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

5.4:演示文件main.cpp

#ifdef WIN32
#ifdef _DEBUG
#pragma comment (lib, "SDLd")
#pragma comment (lib, "SDLmaind")
#else
#pragma comment (lib, "SDL")
#pragma comment (lib, "SDLmain")
#endif
#endif /* WIN32 */

#include "SurfaceClass.h"

void game();
int main(int argc ,char* argv[])
{
        try
        {
                game();
        }
        catch ( const char* s )
        {
                std::cerr << s << std::endl;
                return -1;
        }

        return 0;
}

void game()
{
        //Create a SDL screen.
        const int SCREEN_WIDTH = 640;
        const int SCREEN_HEIGHT = 480;
        ScreenSurface screen(SCREEN_WIDTH, SCREEN_HEIGHT);

        //Create 2 SDL surface for the screen that just created.
        DisplaySurface backGround("bg.bmp", screen);
        DisplaySurface frontImage("image.bmp", screen);
        //VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
        //way2: If use blitToSurface, must get a copy of backGround.
        /*
        DisplaySurface backGroundCopy("bg.bmp", screen);
        */
        //AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

        //Blit backGround surface to screen and flip the screen.
        if ( backGround.blit() == false )
                throw SDL_GetError();
        if ( screen.flip() == false )
                throw SDL_GetError();

        //variable for main loop.
        //key event for up, down, left and right.
        Uint8* keys;
        //moving image's coordinate.
        int xpos = 0;
        int ypos = 0;
        //moving image's size.
        const int IMG_WIDTH = 128;
        const int IMG_HEIGHT = 128;
        //main loop.
        bool gameOver = false;
        while( gameOver == false )
        {
                //press ESC or click X to quit.
                SDL_Event gameEvent;
                while ( SDL_PollEvent(&gameEvent) != 0 )
                {
                        if ( gameEvent.type == SDL_QUIT )
                        {
                                gameOver = true;
                        }
                        if ( gameEvent.type == SDL_KEYUP )
                        {
                                if ( gameEvent.key.keysym.sym == SDLK_ESCAPE )
                                {
                                        gameOver = true;
                                }
                        }
                }
                //key event to move image.
                keys = SDL_GetKeyState(0);
                if ( keys[SDLK_UP] || keys[SDLK_w] )
                {
                        ypos -= 1;
                }
                if ( keys[SDLK_DOWN]|| keys[SDLK_s] )
                {
                        ypos += 1;
                }
                if ( keys[SDLK_LEFT]|| keys[SDLK_a] )
                {
                        xpos -= 1;
                }
                if ( keys[SDLK_RIGHT]|| keys[SDLK_d] )
                {
                        xpos += 1;
                }

                //Hold moving image on the backGround area.
                if ( xpos <= 0 )
                        xpos = 0;
                if ( xpos >= SCREEN_WIDTH - IMG_WIDTH )
                        xpos = SCREEN_WIDTH - IMG_WIDTH;
                if ( ypos <= 0 )
                        ypos = 0;
                if ( ypos >= SCREEN_HEIGHT - IMG_HEIGHT )
                        ypos = SCREEN_HEIGHT - IMG_HEIGHT;

                //VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
                //way1: blit surface to screen
                //Blit a part of backGround ( a rectangular area ) to screen,
                //then the original blitted backGround can be "cleaned".
                if ( backGround.blit(xpos, ypos, xpos, ypos, IMG_WIDTH, IMG_HEIGHT) == false )
                        throw SDL_GetError();
                //Blit the image to screen.
                if ( frontImage.blit(xpos, ypos) == false )
                        throw SDL_GetError();
                //Flip the screen
                if ( screen.flip() == false )
                        throw SDL_GetError();
                //AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

                //VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
                //way2: blit surface to surface, then blit the last surface to screen
                /*
                if ( backGroundCopy.blitToSurface(backGround, xpos, ypos, xpos, ypos, IMG_WIDTH, IMG_HEIGHT) == false )
                throw SDL_GetError();
                if ( frontImage.blitToSurface(backGround, xpos, ypos) == false )
                throw SDL_GetError();
                if ( backGround.blit() == false )
                throw SDL_GetError();
                if ( screen.flip() == false )
                throw SDL_GetError();
                */
                //AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
        }
        return;
}

    下面的代码里图片大小和上面不一样

6、对C++异常机制的思考,代码重写
6.1:用bool作为命令是不是画蛇添足了?
不知道为什么,我总觉得总是用if结构来调用命令让人读起程序来很不连贯。所以,我决定重新修改下,并且异常抛出改为使用类对象,这样是不是更C++一点呢?:)

6.2:修改后的代码。

//FileName: SurfaceClass.h

#ifndef SURFACE_CLASS_H
#define SURFACE_CLASS_H

#include <iostream>
#include <string>
#include "SDL/SDL.h"

using std::string;

class ScreenSurface
{
private:
        static int screenNum;
        int width;
        int height;
        int bpp;
        Uint32 flags;
        SDL_Surface* pScreen;
public:
        ScreenSurface();
        ScreenSurface(int w, int h, int b = 0, Uint32 f = 0);
        ~ScreenSurface();
        SDL_Surface* point() const;
        void flip() const;
};

class DisplaySurface
{
private:
        string fileName;
        SDL_Surface* pSurface;
        SDL_Surface* pScreen;
public:
        DisplaySurface(string file_name, const ScreenSurface& screen);
        ~DisplaySurface();
        SDL_Surface* point() const;
        void blit() const;
        void blit(int at_x, int at_y) const;
        void blit(int at_x, int at_y,
                int from_x, int from_y, int w, int h,
                int delta_x = 2, int delta_y = 2) const;
        void blitToSurface(const DisplaySurface& dst_surface,
                int at_x = 0, int at_y = 0) const;
        void blitToSurface(const DisplaySurface& dst_surface,
                int at_x, int at_y,
                int from_x, int from_y, int w, int h,
                int delta_x = 2, int delta_y = 2) const;
};

class ErrorInfo
{
private:
        string info;
public:
        ErrorInfo():info("Unknown ERROR!")
        {}
        ErrorInfo(const char* c_str)
        {
                info = string(c_str);
        }
        ErrorInfo(const string& str):info(str)
        {}
        void show() const
        {
                std::cerr << info << std::endl;
        }
};

#endif
#include "SurfaceClass.h"

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
//class ScreenSurface

int ScreenSurface::screenNum = 0;

ScreenSurface::ScreenSurface()
        : width(640), height(480), bpp(32), flags(0)
{
        if ( screenNum > 0 )
                throw ErrorInfo("DONOT create more than ONE screen!");
        if ( SDL_Init(SDL_INIT_VIDEO < 0 ) )
                throw ErrorInfo(SDL_GetError());
        pScreen = SDL_SetVideoMode(width, height, bpp, flags);
        screenNum++;
}

ScreenSurface::ScreenSurface(int w, int h, int b, Uint32 f)
        : width(w), height(h), bpp(b), flags(f)
{
        if ( screenNum > 0 )
                throw ErrorInfo("DONOT create more than ONE screen!");
        if ( SDL_Init(SDL_INIT_VIDEO < 0 ) )
                throw ErrorInfo(SDL_GetError());
        pScreen = SDL_SetVideoMode(width, height, bpp, flags);
        screenNum++;
}

ScreenSurface::~ScreenSurface()
{
        SDL_Quit();
}

SDL_Surface* ScreenSurface::point() const
{
        return pScreen;
}

void ScreenSurface::flip() const
{
        if ( SDL_Flip(pScreen) < 0 )
                throw ErrorInfo(SDL_GetError());
}

//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

//VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
//class DisplaySurface

DisplaySurface::DisplaySurface(std::string file_name, const ScreenSurface& screen)
        : fileName(file_name)
{
        pSurface = SDL_LoadBMP(file_name.c_str());
        if ( pSurface == 0 )
                throw ErrorInfo(SDL_GetError());
        pScreen = screen.point();
}

DisplaySurface::~DisplaySurface()
{
        SDL_FreeSurface(pSurface);
}

SDL_Surface* DisplaySurface::point() const
{
        return pSurface;
}

void DisplaySurface::blit() const
{
        if ( SDL_BlitSurface(pSurface, 0, pScreen, 0) < 0 )
                throw ErrorInfo(SDL_GetError());
}


void DisplaySurface::blit(int at_x, int at_y) const
{
        SDL_Rect offset;
        offset.x = at_x;
        offset.y = at_y;

        if ( SDL_BlitSurface(pSurface, 0, pScreen, &offset) < 0 )
                throw ErrorInfo(SDL_GetError());
}

void DisplaySurface::blit(int at_x, int at_y,
        int from_x, int from_y, int w, int h,
        int delta_x, int delta_y) const
{
        SDL_Rect offset;
        offset.x = at_x - delta_x;
        offset.y = at_y - delta_y;

        SDL_Rect dest;
        dest.x = from_x - delta_x;
        dest.y = from_y - delta_y;
        dest.w = w + delta_x*2;
        dest.h = h + delta_y*2;

        if ( SDL_BlitSurface(pSurface, &dest, pScreen, &offset) < 0 )
                throw ErrorInfo(SDL_GetError());
}

void DisplaySurface::blitToSurface(const DisplaySurface& dst_surface, int at_x, int at_y) const
{
        SDL_Rect offset;
        offset.x = at_x;
        offset.y = at_y;

        if ( &dst_surface == this )
                throw ErrorInfo("Cannot blit surface to itself!");

        if ( SDL_BlitSurface(pSurface, 0, dst_surface.point(), &offset) < 0 )
                throw ErrorInfo(SDL_GetError());
}

void DisplaySurface::blitToSurface(const DisplaySurface& dst_surface,
        int at_x, int at_y,
        int from_x, int from_y, int w, int h,
        int delta_x, int delta_y) const
{
        SDL_Rect offset;
        offset.x = at_x - delta_x;
        offset.y = at_y - delta_y;

        SDL_Rect dest;
        dest.x = from_x - delta_x;
        dest.y = from_y - delta_y;
        dest.w = w + delta_x*2;
        dest.h = h + delta_y*2;

        if ( &dst_surface == this )
                throw ErrorInfo("Cannot blit surface to itself!");

        if ( SDL_BlitSurface(pSurface, &dest, dst_surface.point(), &offset) < 0 )
                throw ErrorInfo(SDL_GetError());
}

//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
#ifdef WIN32
#ifdef _DEBUG
#pragma comment (lib, "SDLd")
#pragma comment (lib, "SDLmaind")
#else
#pragma comment (lib, "SDL")
#pragma comment (lib, "SDLmain")
#endif
#endif /* WIN32 */

#include "SurfaceClass.h"

int game(int argc, char* argv[]);
int main(int argc ,char* argv[])
{
        int mainRtn = 0;
        try
        {
                mainRtn = game(argc, argv);
        }
        catch ( const ErrorInfo& info )
        {
                info.show();
                return -1;
        }

        return mainRtn;
}

int game(int argc ,char* argv[])
{
        //Create a SDL screen.
        const int SCREEN_WIDTH = 640;
        const int SCREEN_HEIGHT = 480;
        ScreenSurface screen(SCREEN_WIDTH, SCREEN_HEIGHT);

        //Create 2 SDL surface for the screen that just created.
        DisplaySurface backGround("bg.bmp", screen);
        DisplaySurface frontImage("image.bmp", screen);
        //VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
        //way2: If use blitToSurface, must get a copy of backGround.
        /*
        DisplaySurface backGroundCopy("bg.bmp", screen);
        */
        //AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

        //Blit backGround surface to screen and flip the screen.
        backGround.blit();
        screen.flip();

        //variable for main loop.
        //key event for up, down, left and right.
        Uint8* keys;
        //moving image's coordinate.
        int xpos = 0;
        int ypos = 0;
        //moving image's size.
        const int IMG_WIDTH = 128;
        const int IMG_HEIGHT = 128;
        //main loop.
        bool gameOver = false;
        while( gameOver == false )
        {
                //press ESC or click X to quit.
                SDL_Event gameEvent;
                while ( SDL_PollEvent(&gameEvent) != 0 )
                {
                        if ( gameEvent.type == SDL_QUIT )
                        {
                                gameOver = true;
                        }
                        if ( gameEvent.type == SDL_KEYUP )
                        {
                                if ( gameEvent.key.keysym.sym == SDLK_ESCAPE )
                                {
                                        gameOver = true;
                                }
                        }
                }
                //key event to move image.
                keys = SDL_GetKeyState(0);
                if ( keys[SDLK_UP] || keys[SDLK_w] ){
                        ypos -= 1;
                }
                if ( keys[SDLK_DOWN]|| keys[SDLK_s] ){
                        ypos += 1;
                }
                if ( keys[SDLK_LEFT]|| keys[SDLK_a] ){
                        xpos -= 1;
                }
                if ( keys[SDLK_RIGHT]|| keys[SDLK_d] ){
                        xpos += 1;
                }

                //Hold moving image on the backGround area.
                if ( xpos <= 0 )
                        xpos = 0;
                if ( xpos >= SCREEN_WIDTH - IMG_WIDTH )
                        xpos = SCREEN_WIDTH - IMG_WIDTH;
                if ( ypos <= 0 )
                        ypos = 0;
                if ( ypos >= SCREEN_HEIGHT - IMG_HEIGHT )
                        ypos = SCREEN_HEIGHT - IMG_HEIGHT;

                //VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
                //way1: blit surface to screen
                //Blit a part of backGround ( a rectangular area ) to screen,
                //then the original blitted backGround can be "cleaned".
                backGround.blit(xpos, ypos, xpos, ypos, IMG_WIDTH, IMG_HEIGHT);
                //Blit the image to screen.
                frontImage.blit(xpos, ypos);
                //Flip the screen
                screen.flip();
                //AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

                //VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
                //way2: blit surface to surface, then blit the last surface to screen
                /*
                backGroundCopy.blitToSurface(backGround, xpos, ypos, xpos, ypos, IMG_WIDTH, IMG_HEIGHT);
                frontImage.blitToSurface(backGround, xpos, ypos);
                backGround.blit();
                screen.flip();
                */
                //AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
        }

        return 0;
}

7、鼠标事件演示,代码重用
7.1:演示程序源代码
今天因为一个网上的朋友的请求,做个一个关于鼠标事件的演示程序。实际上,可以完全用到前面我们构造的类和类方法,这里送上主程序,供大家参考。其他两个文件和图片文件均不需要任何改变。

#ifdef WIN32
#ifdef _DEBUG
#pragma comment (lib, "SDLd")
#pragma comment (lib, "SDLmaind")
#else
#pragma comment (lib, "SDL")
#pragma comment (lib, "SDLmain")
#endif
#endif /* WIN32 */
 
#include "SurfaceClass.h"

int game(int argc, char* argv[]);
int main(int argc ,char* argv[])
{
        int mainRtn = 0;
        try {
                mainRtn = game(argc, argv);
        }
        catch ( const ErrorInfo& info ) {
                info.show();
                return -1;
        }

        return mainRtn;
}

int game(int argc ,char* argv[])
{
        //Create a SDL screen.
        const int SCREEN_WIDTH = 640;
        const int SCREEN_HEIGHT = 480;
        ScreenSurface screen(SCREEN_WIDTH, SCREEN_HEIGHT);

        //Create 2 SDL surface for the screen that just created.
        DisplaySurface backGround("bg.bmp", screen);
        DisplaySurface frontImage("image.bmp", screen);

        //Blit backGround surface to screen and flip the screen.
        backGround.blit();
        screen.flip();

        //moving image's coordinate.
        int xpos = 0;
        int ypos = 0;
        //mouse's coordinate.
        int px = 0;
        int py = 0;
        //moving image's size.
        const int IMG_WIDTH = 128;
        const int IMG_HEIGHT = 128;
        //main loop.
        bool gameOver = false;
        while( gameOver == false ){
                //press ESC or click X to quit.
                SDL_Event gameEvent;
                while ( SDL_PollEvent(&gameEvent) != 0 ){
                        if ( gameEvent.type == SDL_QUIT ){
                                gameOver = true;
                        }
                        if ( gameEvent.type == SDL_KEYUP ){
                                if ( gameEvent.key.keysym.sym == SDLK_ESCAPE ){
                                        gameOver = true;
                                }
                        }
                        //mouse event
                        if ( gameEvent.type == SDL_MOUSEMOTION ) {
                                px = gameEvent.motion.x;
                                py = gameEvent.motion.y;
                        }
                }

                if ( xpos < px )
                        xpos++;
                if ( xpos > px )
                        xpos--;
                if ( ypos < py )
                        ypos++;
                if ( ypos > py )
                        ypos--;

                backGround.blit(xpos, ypos, xpos, ypos, IMG_WIDTH, IMG_HEIGHT);
                frontImage.blit(xpos, ypos);
                screen.flip();
        }

        return 0;
}

 

文件名 大小 时间 会员 费用 操作
VS2010工程.rar(精华) 34K791B May 11, 2013, 11:06 p.m. 初学MPEG 积分:0
荣誉:0
会币:0
下载
VS2010工程6.rar(精华) 35K97B May 11, 2013, 11:37 p.m. 初学MPEG 积分:0
荣誉:0
会币:0
下载
VS2010工程7.rar(精华) 34K734B May 11, 2013, 11:47 p.m. 初学MPEG 积分:0
荣誉:0
会币:0
下载
 
 
操作:

Please Login (or Sign Up) to leave a comment