有人会sdl游戏开发的吗

您的举报已经提交成功,我们将尽快处理,谢谢!
不推荐你那么打,既想拿剑又想拿匕首,那你是分剑贼的天赋还是匕首贼的天赋?而且换武器有1秒公共冷却时间的.剑贼和匕首贼,选一一个吧,不要两边都弄不好
大家还关注1415人阅读
前面的章节介绍了一个简单窗口的开发,这节将介绍如何把前面用到的东西用C++封装起来。
&&& 为什么用C++封装起来:
1、C语言没有异常机制,每次调用一个函数都需要通过检查返回值来判断是否成功,比较麻烦。
2、对我个人而言,开发效率上C++要优于C语言,并且C++的代码要容易组织管理,写出来的代码也更容易理解。
&&& 封装的主要部分:
1、将所有的SDL函数都用类包装起来,对于需要做返回值判断的函数,在包装的地方进行判断,然后决定是否抛出异常。这样对于调用者来说就不需要再做返回值判断了。
2、新建一个SDL类。该类用到了单例模式和工厂模式, 提供了访问所有SDL函数的入口。
3、将创建窗口和消息循环这部分代码封装成一个框架类,以后写代码时只要继承这个框架就可以了。
4、包装SDL_Surface结构体,使它可以像普通对象一样使用。因为通过SDL API获取一个SDL_Surface后需要要手动释放,否则会造成内存泄漏。
&&& 封装之后的main函数:
#include&&string&&&
#include&&lessons/Lesson01.h&&&
int&main(&int&argc,&char*&args[]&)&&
&&&&Lesson01&&&
&&&&frame.setSize(800,&600);&&&&&&&&&&
&&&&frame.setTitle(&Lesson01&);&&&&&&&
&&&&frame.open();&&&&&&&&&&&&&&&&&&&&&
&&&&return&0;&&
&&&& 封装成这样子之后,我们可以将不同课程中的例子代码写在不同的类中,完全隔离开,到时候想运行哪一课的例子修改一下main中的Lesson01就可以了。
&&& 下面根据这份代码逐步介绍封装过程。上面main函数中用到了Lesson01类,下面先看看Lesson01里面有些什么东西。
&&& Lesson01.h
#ifndef&LESSON01_H_&&
#define&LESSON01_H_&&
#include&&../SDLFrame.h&&&
class&Lesson01&:&public&SDLFrame&&
&&&&Lesson01();&&
&&&&virtual&~Lesson01();&&
protected:&&
&&&&void&onRender();&&&&&&
&&&&void&onInit();&&&&&&&&
&&&&SDLSurfacePtr&&&&&
#endif&/*&LESSON01_H_&*/&&
& & Lesson01.cpp
#include&&Lesson01.h&&&
Lesson01::Lesson01()&&
Lesson01::~Lesson01()&&
void&Lesson01::onRender()&&
&&&&SDL::video()-&BlitSurface(message,&NULL,&screen,&NULL);&&
void&Lesson01::onInit()&&
&&&&SDLSurfacePtr&loadedImage&=&SDL::video()-&LoadBMP(&E:\\code_picture\\javaeye.bmp&);&&
&&&&message&=&SDL::video()-&DisplayFormat(loadedImage);&&
&&& 这里可以看出Lesson01是继承自SDLFrame,而它本身只有两个函数,OnInit负责一些初始化工作,OnRender负责将要显示的内容填充到screen中去。
&&& Lesson01.cpp中用到了SDL::video(),这就是前面提到的SDL类,该类提供了所有SDL函数的入口,这里的SDL::video()-&BlitSurface等于SDL_BlitSurface,只是包装了一下而以。
&&& 下面看SDL类
&&& 头文件
#ifndef&SDLCORE_H_&&
#define&SDLCORE_H_&&
#include&&SDLException.h&&&
#include&&SDLVideo.h&&&
#include&&SDLWindow.h&&&
#include&&SDLEvent.h&&&
class&SDL&&
&&&&SDL();&&
&&&&virtual&~SDL();&&
&&&&static&void&Init(Uint32&flags);&&&&&&&
&&&&static&void&Quit();&&&&&&&&&&&&&&&&&&&
&&&&static&SDLVideo&*&video();&&&&&&&&&&&&
&&&&static&SDLWindow&*&window();&&&&&&&&&&
&&&&static&SDLEvent&*&event();&&&&&&&&&&&&
#endif&/*&SDLCORE_H_&*/&&
&& CPP文件
#include&&SDLCore.h&&&
SDL::SDL()&&
SDL::~SDL()&&
void&SDL::Init(Uint32&flags)&&
&&&&int&ret&=&&SDL_Init(flags);&&
&&&&if(ret&==&-1)&&
&&&&&&&&throw&SDLException(std::string(&初始化SDL错误:&)&+&SDL_GetError());&&
void&SDL::Quit()&&
&&&&SDL_Quit();&&
SDLVideo&*&SDL::video()&&
&&&&static&SDLVideo&&&
&&&&return&&&&
SDLWindow&*&SDL::window()&&
&&&&static&SDLWindow&&&
&&&&return&&&&
SDLEvent&*&SDL::event()&&
&&&&static&SDLEvent&&&
&&&&return&&&&
&& 从上面的代码可以看出,SDL类只负责初始化和退出SDL环境,同时创建SDL相关的封装类对象,这里用到了C++静态成员变量的特性:全局生命周期且只被初始化一次。从而保证SDLVideo、SDLWindow、SDLEvent的对象全局唯一。
&& SDLException是程序定义的一个异常类,由于很普通,所以在这里不再进行解释。
&& SDLVideo、SDLWindow、SDLEvent都是SDL API函数的封装类,原理几乎是一样的,这里取其中的一个进行分析。
SDLVideo.h
#ifndef&SDLVIDEO_H_&&
#define&SDLVIDEO_H_&&
#include&&SDLException.h&&&
#include&&SDL/SDL.h&&&
#include&&SDLSurface.h&&&
class&SDLVideo&&
&&&&friend&class&SDL;&&
private:&&
&&&&SDLVideo();&&
&&&&virtual&~SDLVideo();&&
&&&&SDLSurfacePtr&SetVideoMode(int&width,&int&height,&int&bpp,&Uint32&flags);&&
&&&&void&Flip(SDLSurfacePtr&screen);&&
&&&&SDLSurfacePtr&DisplayFormat(SDLSurfacePtr&surface);&&
&&&&SDLSurfacePtr&&&LoadBMP(std::string&file);&&
&&&&void&BlitSurface(SDLSurfacePtr&src,&SDL_Rect&*srcrect,&SDLSurfacePtr&dst,&SDL_Rect&*dstrect);&&
#endif&/*&SDLVIDEO_H_&*/&&
&SDLVideo.cpp
#include&&SDLVideo.h&&&
SDLVideo::SDLVideo()&{&&
SDLVideo::~SDLVideo()&{&&
SDLSurfacePtr&SDLVideo::SetVideoMode(int&width,&int&height,&int&bpp,&Uint32&flags)&&
&&&&SDL_Surface&*&surface&=&SDL_SetVideoMode(width,&height,&bpp,&flags);&&
&&&&if(NULL&==&surface)&&
&&&&&&&&throw&SDLException(std::string(&SDL_SetVideoMode初始化视频模式时发生错误:&)&+&SDL_GetError());&&
&&&&return&SDLSurfacePtr(new&SDLSurface(surface));&&
void&SDLVideo::Flip(SDLSurfacePtr&screen)&&
&&&&int&ret&=&SDL_Flip(screen-&value());&&
&&&&if(ret&==&-1)&&
&&&&&&&&throw&SDLException(std::string(&SDL_Flip内存内容显示到屏幕时发生错误:&)&+&SDL_GetError());&&
SDLSurfacePtr&SDLVideo::DisplayFormat(SDLSurfacePtr&surface)&&
&&&&SDL_Surface&*newSurface&=&SDL_DisplayFormat(surface-&value());&&
&&&&if(NULL&==&newSurface)&&
&&&&&&&&throw&SDLException(std::string(&SDL_DisplayFormat转换图片格式为程序格式时发生错误:&)&+&SDL_GetError());&&
&&&&return&SDLSurfacePtr(new&SDLSurface(newSurface));&&
SDLSurfacePtr&&&SDLVideo::LoadBMP(std::string&file)&&
&&&&SDL_Surface&*surface&=&SDL_LoadBMP(file.c_str());&&
&&&&if(NULL&==&surface)&&
&&&&&&&&throw&SDLException(std::string(&SDL_LoadBMP加载BMP图片时发生错误:&)&+&SDL_GetError());&&
&&&&return&SDLSurfacePtr(new&SDLSurface(surface));&&
void&SDLVideo::BlitSurface(SDLSurfacePtr&src,&SDL_Rect&*srcrect,&SDLSurfacePtr&dst,&SDL_Rect&*dstrect)&&
&&&&int&ret&=&SDL_BlitSurface(src-&value(),&srcrect,&dst-&value(),&dstrect);&&
&&&&if(ret&==&-1)&&
&&&&&&&&throw&SDLException(std::string(&SDL_BlitSurface重叠图片时发生错误:&)&+&SDL_GetError());&&
&&&& 从上面的代码可以看出SDLVideo只是简单的将SDL中video相关的函数做一下包装,检查SDL函数的返回值,如果有错误就抛出异常。在头文件中,将SDL类声明成友元类并且将构造函数设置为private是为了避免在除SDL类以外的地方实例化该类的对象。
&&& 这里用到了SDLSurfacePtr和SDLSurface。SDLSurfacePtr定义:
typedef&boost::shared_ptr&SDLSurface&&SDLSurfaceP&&
&&& 构造SDLSurfacePtr的代码为:
SDLSurfacePtr(new&SDLSurface(surface));&&
&&& 可以看出,SDLSurfacePtr中有SDLSurface,SDLSurface中有SDL_Surface*。
&&& boost库的shared_ptr是一种带引用计数的智能指针,当shared_ptr对象的引用计数变成0的时候,会自动delete它里面保存的对象,所以当最后一个SDLSurfacePtr对象析构的时候,SDLSurfacePtr会调用delete SDLSurface。关于shared_ptr的详细介绍,可以通过GOOGLE搜到很多资料。
&&& SDLSurface的析构函数如下:
SDLSurface::~SDLSurface()&&
&&&&if(surface&!=&NULL)&&
&&&&&&&&SDL_FreeSurface(surface);&&
&&& 由于SDLSurface的析构函数中会调用SDL_Surface*的释放操作。从而使得内存中的SDL_Surface*被自动释放。这样就省去了手动释放SDL_Surface的麻烦。
&&& 最后来看看SDLFrame类
#ifndef&SDLFRAME_H_&&
#define&SDLFRAME_H_&&
#include&&SDL/SDLCore.h&&&
class&SDLFrame&&
&&&&static&const&std::string&DEFAULT_TITLE;&&&&&&&&&&&
&&&&static&const&int&DEFAULT_SCREEN_WIDTH&=&800;&&&&&&
&&&&static&const&int&DEFAULT_SCREEN_HEIGHT&=&600;&&&&&
&&&&SDLFrame();&&
&&&&virtual&~SDLFrame();&&
&&&&void&&&&open(Uint32&flags&=&SDL_HWSURFACE&|&SDL_DOUBLEBUF);&&
&&&&void&&&&setTitle(std::string&title);&&
&&&&void&&&&setSize(int&width,&int&heigth);&&
protected:&&
&&&&virtual&bool&onEvent(const&SDL_Event&*event);&&
&&&&virtual&void&onRender();&&
&&&&virtual&void&onInit();&&
protected:&&
&&&&SDLSurfacePtr&&&
&&&&std::string&&&
&&&&int&&&
&&&&int&&&
#endif&/*&SDLFRAME_H_&*/&&
#include&&SDLFrame.h&&&
const&std::string&SDLFrame::DEFAULT_TITLE&&=&&SDL&Tutorial&;&&
SDLFrame::SDLFrame()&&
&&&&title&=&DEFAULT_TITLE;&&
&&&&width&=&DEFAULT_SCREEN_WIDTH;&&
&&&&height&=&DEFAULT_SCREEN_HEIGHT;&&
SDLFrame::~SDLFrame()&&
void&&&&SDLFrame::open(Uint32&flags)&&
&&&&SDL::Init(SDL_INIT_EVERYTHING);&&
&&&&screen&=&SDL::video()-&SetVideoMode(width,&height,&32,&flags);&&
&&&&SDL::window()-&SetCaption(title);&&
&&&&onInit();&&
&&&&SDL_Event&&&
&&&&bool&bQuit&=&false;&&
&&&&while(!bQuit)&&
&&&&&&&&while(&SDL::event()-&PollEvent(&&event&)&)&&
&&&&&&&&{&&
&&&&&&&&&&&&if(!onEvent(&event))&&
&&&&&&&&&&&&{&&
&&&&&&&&&&&&&&&&bQuit&=&true;&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&&&
&&&&&&&&onRender();&&
&&&&&&&&&&
&&&&&&&&SDL::video()-&Flip(screen);&&
&&&&SDL::Quit();&&
void&&&&SDLFrame::setTitle(std::string&title)&&
&&&&this-&title&=&&&
void&&&&SDLFrame::setSize(int&width,&int&heigth)&&
&&&&this-&width&=&&&
&&&&this-&height&=&&&
bool&SDLFrame::onEvent(const&SDL_Event&*event)&&
&&&&switch(event-&type)&&
&&&&case&SDL_KEYDOWN:&&
&&&&&&&&if(event-&key.keysym.sym&==&SDLK_ESCAPE)&&
&&&&&&&&{&&
&&&&&&&&&&&&return&false;&&
&&&&&&&&}&&
&&&&&&&&break;&&
&&&&case&SDL_QUIT:&&
&&&&&&&&return&false;&&
&&&&&&&&break;&&
&&&&default:&&
&&&&&&&&break;&&
&&&&return&true;&&
void&SDLFrame::onRender()&&
void&SDLFrame::onInit()&&
&&&& SDLFrame类封装了消息循环,通过在循环中调用成员函数的方式将消息循环中公共的部分与特殊的部分分离开,从而可以在基类中重载这些成员函数使不同的基类表现出不通的特性。其中onEvent负责处理用户输入,onInit负责窗口创建后的初始化,onRender负责窗口的绘制。
&&&& 这里onEvent只处理了窗口关闭和ESC键按下两个消息,子类可以通过重载来覆盖默认实现。onInit和onRender都是空实现。需要在子类中去实现具体的操作。
&&&& 结合消息循环,现在再回过头去看Lesson01的代码,就会发现只要程序一有空闲,就会调用onRender函数,而Lesson1的onRender函数中只有一行代码:SDL::video()-&BlitSurface(message, NULL, screen, NULL);,并且这行代码中用到的message和screen永远不会变,你可能会想老这样调用同样的代码是不是很浪费资源,在这里确实是浪费资源,其实只要将这行代码放到onInit函数的末尾就可以了。这里可以这样做的原因是因为程序初始化好了之后内存中的内容不再会发生变化,所以每次调用SDL::video()-&Flip(screen)都不会改变屏幕显示的内容。说的通俗点,就是这个程序太简单了,用不着定时去更新窗口。后面的章节中将会看到定时更新窗口的用处。
&&&& 这节的内容就介绍到这里,在以后的章节中,都将采用同Lesson01一样的方式来编写代码。附件中是本节内容的完整源代码。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:90819次
积分:1108
积分:1108
排名:千里之外
转载:87篇
(1)(3)(2)(2)(3)(5)(17)(1)(6)(1)(5)(7)(4)(27)(7)人人网 - 抱歉
哦,抱歉,好像看不到了
现在你可以:
看看其它好友写了什么
北京千橡网景科技发展有限公司:
文网文[号??京公网安备号?甲测资字
文化部监督电子邮箱:wlwh@??
文明办网文明上网举报***: 举报邮箱:&&&&&&&&&&&&

参考资料

 

随机推荐