ClanLib开源游戏引擎探索(1):Basic2D
2010-05-21 14:52
423 查看
研究ClanLib已经有一个多月了,心中若有所得,感觉还是有必要写点东西。如果有研究ClanLib的同学,请不用客气的来指正吧!
虽然不是很炫,但是内容很丰富,一个游戏所需的2D元素都包含在这里边了。下面慢慢分解这个程序:
使用CL_ClanApplication时,我们只需要声明一个全局的对象就可以了,在声明时需要向构造函数传递一个指向型别为int fun(const std::vector<CL_string> &args)的函数指针。参照帮助说明中的,“To use this class, create a static main function in your application class, then make a single global instance of CL_ClanApplication。”并结合XNA的模式,我是如此设计程序结构的:
在app::main中首先实例化一个Game的对象,然后执行Game::run,这样我们就将程序的控制流程转移到Game类中。注意到,Game类是用Template Methods模式来设计的,它定义了一个游戏的基本框架,并提供一系列的Hack接入点。对于任一个特定的游戏,我们只需从Game类继承得到一个子类,然后扩展相应的接入点,就可以定制游戏的行为了。继承之后,注意更改这里的Game为你定义的类如myGame等。
之后,就进入游戏循环了。如win32中的消息循环一样,我们使用一个while——当while循环跳出的时候,游戏就结束了。在每次循环中,我们要更新游戏逻辑,绘制图像要素,处理输入等等,值得注意的是,在循环最好要调用CL_DisplayWindow::flip来交换前后缓冲区,是绘制的内容显示出来,然后调用 CL_KeepAlive::process来更新ClanLib状态。
下面的代码是我参照XNA的相应结构设计的Game类实现:
关于成员变量中的几个Setup的说明: 在使用相应的ClanLib功能前我们要先初始化相应的库,这只需实例化相应库的Setup类对象就可以了。CL_SetupCore——对系统库的支持,CL_SetupDisplay——对窗口和绘图CL_Draw的支持,CL_SetupGL——对OpenGl的支持。
1,渐变填充的背景 通过CL_Draw::gradient_fill完成,在参数中指定CL_Gradient(CL_Colorf::blue, CL_Colorf::greedyellow)就可以了
2,旋转的Sprite 使用一副图像来构造一个CL_Sprite对象,设置rotation_hotspot为origin_center即图像中点,在Update中设置旋转角度angle
3,旋转的半透明方块 用CL_Draw的fill和box可以画出带边框的方块,指定fill用的颜色为CL_Colorf(1.0f, 0.0f, 0.0f, 0.5f)就可以达到红色半透明的效果
4,显示文字 使用CL_Font创建一个字体,然后使用draw_text来绘制要显示的文字
5,移动的剪切区 通过push_cliprect可以添加一个剪切区,通过pop_cliprect来移除一个剪切区,在每次绘制前先push,然后开始绘制,绘制的内容会被剪切,然后pop就可以达到动态剪切区的效果了
Basic2D 基本的2D元素的绘制
程序运行之后的效果图如下:虽然不是很炫,但是内容很丰富,一个游戏所需的2D元素都包含在这里边了。下面慢慢分解这个程序:
第一步,让程序跑起来!
ClanLib是一个基于C++的游戏引擎,而一个C++程序要运行起来,就必须指定一个main入口点函数。而在ClanLib中我们除了可以用自定义的main函数——就像hello world那样,也可以使用CL_ClanApplication来获得跨平台的支持。使用CL_ClanApplication时,我们只需要声明一个全局的对象就可以了,在声明时需要向构造函数传递一个指向型别为int fun(const std::vector<CL_string> &args)的函数指针。参照帮助说明中的,“To use this class, create a static main function in your application class, then make a single global instance of CL_ClanApplication。”并结合XNA的模式,我是如此设计程序结构的:
#include <ClanLib/core.h> #include <ClanLib/application.h> #include "Game.h" class app { public: static int main(std::vector<CL_String> const&args) { Game game; return game.run(args); } }; CL_ClanApplication application(&app::main);
在app::main中首先实例化一个Game的对象,然后执行Game::run,这样我们就将程序的控制流程转移到Game类中。注意到,Game类是用Template Methods模式来设计的,它定义了一个游戏的基本框架,并提供一系列的Hack接入点。对于任一个特定的游戏,我们只需从Game类继承得到一个子类,然后扩展相应的接入点,就可以定制游戏的行为了。继承之后,注意更改这里的Game为你定义的类如myGame等。
第二步,搭建游戏架构!
游戏运行的最初任务是要创建一个游戏窗口,有了窗口之后才能加载游戏中的图片、Sprites、材质、贴图等asserts。在ClanLib中使用CL_DisplayWindow来创建和管理游戏窗口,并且能向外发出一些事件信号,以供用户监听相应。在实例化CL_DisplayWindow是我们往往向构造器传递一个CL_DisplayWindowDescription对象来个性化窗口,包括设置窗口的大小、title、是否可以缩放等。实例化完毕之后,窗口就创建完成并显示出来。之后,就进入游戏循环了。如win32中的消息循环一样,我们使用一个while——当while循环跳出的时候,游戏就结束了。在每次循环中,我们要更新游戏逻辑,绘制图像要素,处理输入等等,值得注意的是,在循环最好要调用CL_DisplayWindow::flip来交换前后缓冲区,是绘制的内容显示出来,然后调用 CL_KeepAlive::process来更新ClanLib状态。
下面的代码是我参照XNA的相应结构设计的Game类实现:
#include <ClanLib/core.h> #include <ClanLib/display.h> #include <ClanLib/gl.h> class Game { public: int run( const std::vector<CL_String> & args ) { try { desc = CL_DisplayWindowDescription(cl_text("游戏")); ModifyDisplayWindowDescription(); window = CL_DisplayWindow(desc); ModifyDisplayWindow(); CL_GraphicContext gc = window.get_gc(); LoadContent(gc); quit = false; while (!quit) { gc.clear(CL_Colorf::cornflowerblue); Update(); Draw(gc); window.flip(1); CL_KeepAlive::process(); } } catch (CL_Exception &e) { CL_ConsoleWindow console("Exceptions: ", 160, 80); CL_Console::write_line(e.message); console.display_close_message(); UnLoadContent(); return -1; } UnLoadContent(); return 0; } virtual void Update() {} virtual void Draw(CL_GraphicContext &gc) {} virtual void LoadContent(CL_GraphicContext &gc) {} virtual void UnLoadContent() {} virtual void ModifyDisplayWindow() {} virtual void ModifyDisplayWindowDescription() { desc.set_allow_resize(false); desc.set_size(CL_Size(800, 600), true); } protected: CL_SetupCore core; CL_SetupDisplay display; CL_SetupGL gl; CL_DisplayWindow window; CL_DisplayWindowDescription desc; bool quit; };
关于成员变量中的几个Setup的说明: 在使用相应的ClanLib功能前我们要先初始化相应的库,这只需实例化相应库的Setup类对象就可以了。CL_SetupCore——对系统库的支持,CL_SetupDisplay——对窗口和绘图CL_Draw的支持,CL_SetupGL——对OpenGl的支持。
第三步,让游戏要素登台!
对于图中的各个效果,这里简单说明一下,具体的内容还是直接参照代码吧!1,渐变填充的背景 通过CL_Draw::gradient_fill完成,在参数中指定CL_Gradient(CL_Colorf::blue, CL_Colorf::greedyellow)就可以了
2,旋转的Sprite 使用一副图像来构造一个CL_Sprite对象,设置rotation_hotspot为origin_center即图像中点,在Update中设置旋转角度angle
3,旋转的半透明方块 用CL_Draw的fill和box可以画出带边框的方块,指定fill用的颜色为CL_Colorf(1.0f, 0.0f, 0.0f, 0.5f)就可以达到红色半透明的效果
4,显示文字 使用CL_Font创建一个字体,然后使用draw_text来绘制要显示的文字
5,移动的剪切区 通过push_cliprect可以添加一个剪切区,通过pop_cliprect来移除一个剪切区,在每次绘制前先push,然后开始绘制,绘制的内容会被剪切,然后pop就可以达到动态剪切区的效果了
#include "game.h" class myGame : public Game { public: void Draw(CL_GraphicContext &gc) { CL_Draw::gradient_fill(gc, CL_Rectf(window.get_viewport()), CL_Gradient(CL_Colorf::blue, CL_Colorf::greenyellow)); CL_Draw::line(gc, clip_rect.left, clip_rect.top - 1, clip_rect.right, clip_rect.top - 1, CL_Colorf::navy); CL_Draw::line(gc, clip_rect.left, clip_rect.bottom, clip_rect.right, clip_rect.bottom, CL_Colorf::navy); gc.push_cliprect(clip_rect); spr_card.draw(gc, 400.0f - spr_card.get_width() / 2, 300.0f - spr_card.get_height() / 2); CL_Draw::fill(gc, box_rect1, CL_Colorf(1.0f, 0.0f, 0.0f, 0.6f)); CL_Draw::box(gc, box_rect1, CL_Colorf::black); CL_Draw::fill(gc, box_rect2, CL_Colorf(0.0f, 1.0f, 0.0f, 0.6f)); CL_Draw::box(gc, box_rect2, CL_Colorf::black); font.draw_text(gc, 50.0f, 300.0f + 8.0f, cl_text("ClanLib游戏演示1: Basic2D")); gc.pop_cliprect(); } void LoadContent(CL_GraphicContext &gc) { spr_card = CL_Sprite(gc, cl_text("EE1-JP169.jpg")); spr_card.set_rotation_hotspot(origin_center); rotation_angle = 0.0f; clip_rect = CL_Rect(0, 0, CL_Size(800, 300)); speed = 3; font = CL_Font(gc, cl_text("宋体"), 18.0f); } void Update() { rotation_angle += 1.0f; spr_card.set_angle(CL_Angle(rotation_angle, cl_degrees)); if (clip_rect.top < window.get_viewport().top || clip_rect.bottom > window.get_viewport().bottom) { speed *= -1; } clip_rect.translate(0, speed); float radioX = sin(rotation_angle / 15) * 100.0f; float radioY = cos(rotation_angle / 15) * 100.0f; box_rect1 = CL_Rectf(400.0f - radioX, 300.0f - 12.5f - radioY, CL_Sizef(25, 25)); box_rect2 = CL_Rectf(400.0f + radioX, 300.0f - 12.5f + radioY, CL_Sizef(25, 25)); } void ModifyDisplayWindow() { slot_window_close = window.sig_window_close().connect(this, &myGame::onWndClose); CL_InputDevice keyboard = window.get_ic().get_keyboard(); slot_keydown = keyboard.sig_key_down().connect(this, &myGame::onKeyDown); } void onKeyDown(const CL_InputEvent &input, const CL_InputState &state) { if (input.id == CL_KEY_ESCAPE) { quit = true; } } void onWndClose() { quit = true; } private: CL_Slot slot_window_close; CL_Slot slot_keydown; CL_Sprite spr_card; float rotation_angle; CL_Rect clip_rect; int speed; CL_Rectf box_rect1; CL_Rectf box_rect2; CL_Font font; };
总结
这个例子并不复杂,关键的东西就是CL_Draw。例子就说到这里,其他有趣的东西如signal—slot模型(模拟C#中event的东西)等以后有机会再说吧!相关文章推荐
- ClanLib开源游戏引擎探索(2):Canvas
- ClanLib开源游戏引擎探索(3):CanvasAdvanced
- 支持Box2D,开源HTML5 2D游戏引擎FLAG
- Youzi2D推出开源HTML5游戏加速引擎
- 开源项目之开源的2D游戏引擎 HGE
- Phaser开源2d引擎 html5游戏框架中文简介
- 开源、免费、跨平台的2D手机游戏引擎-----Cocos2d-x
- Cocos2D-HTML5开源2D游戏引擎
- hge(开源的2D游戏引擎)
- 开源2D游戏引擎(Java&Android),LGame-0.3.2版正式发布
- 支持Box2D,开源HTML5 2D游戏引擎FLAG
- XNA 的 3D 和 2D 游戏引擎(商业与开源)
- Cocos2D-HTML5开源2D游戏引擎
- Phaser开源2d引擎 javascript/html5游戏框架
- 开源跨平台2D游戏引擎Love2D介绍
- 开源2D游戏引擎(Java&Android),LGame-0.3.2版正式发布
- Youzi2D推出开源HTML5游戏加速引擎
- Cocos2D-HTML5开源2D游戏引擎
- Cocos2D-HTML5开源2D游戏引擎www.maiziedu.com
- 十五开源的Android(2D或3D)Android开发游戏引擎