|
前言 目前这个引擎只是一个预览版, 很多功能仍在开发当中 硬件需求: 显卡支持ShaderModel3.0 作者Blog: http://flohofwoe.blogspot.com 用到的开源工程 为了避免版本之间的不兼容, 已经在SDK中包含 体系结构一览 - Nebula3 分成三层, 每一层都是建立在另一层之上的:
- 基础层: 最底层, 提供了一个图形和音频之下基本的平台抽象. 基础层可以用作任意类型的程序开发平台, 而不仅仅是实时3D程序.
- 渲染层: 这是中间层, 它在基础层之上另加了许多特性, 像3D渲染, 音频, 物理和场景管理等.
- 应用程序层: 这是最高的一层, 提供了一个完整的游戏框架, 这使得开发人员可以集中精力在游戏逻辑上, 而不用对关心各种细节实现.
- Nebula3 会跟Mangalore 合为一个整体, Mangalore的各种子系统会集成到Nebula3的适合它们的层中去.
- Nebula3 比 Nebula2更趋向于使用C++.
- Nebula3通过引用计数和智能指针实现了对象生命周期的管理.
- Nebula3的新对象模型使用一个4 byte的基类来代替Nebula2中70+ bytes的.
- RTTI更高效, 更易用.
- Nebula3仍然不使用C++异常, RTTI和STL(所有这些不是降低性能就是降低便携性).
- 根据类名来创建对象更快更易用.
- Nebula3 避免使用C Lib, 去除了附加的代码层.
- Nebula3 使用LUA 代替TCL作为标准的脚本语言(当然也可以增加其它脚本语言的支持)
基础层 - App 包含各种Application类
- Attr 属性注册
- Core Nebula3的对象模型
- Debug 程序内存转储和基于HTTP的调试信息(这个真是个好东西, 第一次听说)
- Http 实现了HTTP服务器和客户端, 这样可以在浏览器中实时查看调试信息
- IO 输入输出系统, 很方便
- Math 基于C++的数学库, 没什么特别的
- Memory 实现了定制的内存分配器
- Messaging 消息定义
- Net 最基本的基于TCP协议的C/S架构, 更多的功能会在高层的网络扩展中给出
- Scripting 脚本系统
- System 硬件平台和特定操作系统相关的一些特性
- Threading 多线程
- Timing 时间/定时器管理
- Util 工具库, 包括各种容器, 数据结构, 还有一个强大的string类(抛弃STL吧!)
渲染层 - CoreGraphics 图形库核心类
- Frame 基于帧的处理, 如RenderTarget, PostProcess等
- Graphics 一些实体类型定义, 如Camera之类
- Input 输入设备, 就是鼠标键盘手柄…..
- Lighting 光照处理, 包括阴影算法(竟然要SM3.0@_@)
- Models 骨骼模型&粒子系统
- RenderUtil 目前就只有一个Maya的摄像机…..
- Resources 资源管理器
应用程序层 application_win32工程就是一个简单的游戏, 框架, 但是已经包含了许多特性, 如果物理, 数据库等. 不过好像还缺少GUI系统. 插件 目前有三个, 基于ODE的物理引擎, 基于SQLite的本地数据库, 基于nebula2模型的骨骼模型系统 编译 没什么好说的, 打开.sln直接编译就成了, 没见过这么简单的开源工程^_^ 就以这个程序做为测试程序的模板吧 1: #include "stdneb.h"
2: #include "core/debug.h"
3: #include "core/coreserver.h"
4: #include "core/sysfunc.h"
5:
6: using namespace Core;
7:
8: void
9: __cdecl main()
10: {
11: Ptr<CoreServer> coreServer = CoreServer::Create();
12: coreServer->SetAppName("Nebula3 Hello World!");
13: coreServer->Open();
14:
15: n_printf("Hello World\n");
16: system("pause");
17:
18: coreServer->Close();
19: coreServer = NULL;
20:
21: Core::SysFunc::Exit(0);
22: }
最近拿到SpeedTree资料,开始学习,并用到项目里去. 1. 该插件的特点: api无关。它本身只是数据结构和逻辑架构,没有任何渲染语句子,因此为了把它应用到自己的引擎里,需要为之添加渲染相关的语句。而根据sdk的讲解,推荐用户为之搭建中间架构,用来联系SPEEDTREE与自己的引擎。这样做起码有两点好处,搭建的中间架构(也推荐别加任何api相关的语句),因此,即使你以后换了api(譬如从gl换成dx),中间架构还是可以继续沿用的。还有一个好处就是,当speedtree更新版本的时候,你也无须修改你的引擎,而只需要修改相对简单而且稳定的中间架构。 2. 该插件的具体特性: 注意,下面具体特性分析都是基于SDK里一个叫“DirectX9”的例子进行的,在这个例子里,它给出了最基本的使用方法,同时也向用户展示了它的基本特性。 A. 树的基本渲染 通过大场景的测试,DP的个数大致是树木棵数的两到三倍。详细分析下,发现 它一棵树分三部分绘制:树干和大树枝(branches),小树枝(fronds),树叶(leaves) Branches:使用模型来绘制 Fronds:使用两个十字交叉的面模拟小树枝,为了节省三角形。 Leaves:使用billboard方式绘制,这样就能产生视觉效果比较好的叶子了。 它这样划分是出于以下三方面的考虑:这几部分的渲染状态不一样,动画的状态不一样,做LOD的时候也不一样。具体看下面的介绍。 B. 树的阴影系统 它包括两方面的阴影。首先是树干上的阴影。其次是整棵树在地面的投影。 树干的自阴影(self shadow)是预先生成的,至于生成的算法,可能是可以根据可穿透的光线跟踪,也可能是结合shadow map的逐象素地生成光照贴图(把树干的面都展开后,在对应的地方画上阴影).有了该光照贴图,那渲染树干的时候就可以跟树干本身的纹理进行混合产生比较真实的效果。 而整棵树在地面的透影子,则是使用一个矩形画出来的,阴影贴图也是预先生成好。渲染的时候浮在地面。 C. 树的动画 树的三部分的动画状态都是不一样的。这对优化有极其重要的作用。风小的时候,或是树离眼睛比较远的时候,可以不动树枝,而只是动树叶。而具体他们是怎么动的: 树叶的动画:就是一个billboard的来回平移以及他本身绕视坐标系统Z轴的转动。 树枝的动画:通过它引擎本身计算出来的矩阵进行动画。 而至于它具体怎么渲染动画的,它提供了基于CPU和GPU的方法。 基于CPU的方法是:创建顶点缓冲的时候, 使用D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY标记(这种方法能提高CPU修改和更新该缓冲的速度),渲染的时候实时更新顶点位置。 基于GPU的方法是:通过自定义的顶点shader程序进行,更新动画的时候,向shader传递常量数组。 D. 树的光照 它可以打开和关闭实时光照,对于实时光照,树干部分又分两种情况,对于没有法线贴图的树干,使用per-vertex的光照。而对于有法线贴图的,则使用per-pixcel光照。至于给不给树干渲染法线贴图,则根据具体的程序决定。 而对于树叶的渲染,因为它是一个billboard,因此也无法通过其法线来计算光照。它其实是根据这个 billboard的位置来确定其亮度的。通过把整棵树当成一个球来分析,而每个billboard的位置就相当于是球上的一点,结合光的方向,计算出该点的亮度。 E. LOD的特点 其强大的LOD系统,为实现大规模的场景提供了有力的支持。这里的LOD分三方面:顶点的LOD,纹理的LOD,动画的LOD。 (1) 顶点的LOD:首先是针对树干,因为这里的树干是实实在在的模型。至于树干的建立,它里面是采用贝塞尔曲线来描述整个mesh的,贝塞尔曲线的描述方式无疑给即时高效率的LOD计算提供了可行性。同时这还针对树枝,远了之后,小树枝就不渲染了。到了一定的距离的时候,整棵树其实就变成一个billboard了。 (2) 纹理的LOD:树干上在最高精度的时候会有三套纹理:基本纹理,光照贴图,法线贴图。随着LOD的进行,可以依次减去法线贴图,光照贴图,最后是本身贴图,最后只为树干渲染一种颜色。 (3) 动画的LOD:现在有三种动画,大树枝(模型)的动画,小树枝(两个交叉面)的动画,以及树叶的动画。随着LOD的进行,依次去掉大树杆的动画,小树杆的动画,最后是树叶的动画。这也是符合视觉效果的。 F. 文件系统 用场景来分析的话,一个场景是.stf文件(Speed Tree Forest).该文件描述了每棵树的相关属性。而一棵树是通过一个.spt(Speed Tree)文件来描述的.用文本编辑器打开,就能看到里面记录了该树的所有信息。而该插件为此开发了配套了树木编辑器材。使用该编辑器,打开.spt文件之后,就可以对该树进行浏览以及编辑。 3.Speedtree使用实践 它提供给用户的一个最主要的类就是CSpeedTreeRT.这是一个speedtree对外界的接口,从SpeedTreeRT.h中可以看到,这个类其实是包括了该插件的核心类.因此,我们在使用该插件的时候,其实全都是通过这个接口。 譬如CSpeedTreeRT::SetCamera(eye, viewDir),通知它内部现在的摄像机的信息,然后它内部就根据这些信息计算出正确的billboard. 而如何加载一棵树呢?使用CSpeedTreeRT::LoadTree(const char *treefile);输入一个”.spt”文件,然后我们设置光照和风效果的方法如CSpeedTreeRT::SetBranchWindMethod,SetFrondWindMethod,SetBranchLightingMethod,SetLeafLightingMethod, SetLodLimits等,接着执行CSpeedTreeRT::Compute(),然后它里面就开始进行黑盒处理,最后我们就可以获取其几何数据(CspeedTreeRT::GetGeometry)进行渲染。获取之前还可以手动去设置LOD级别CSpeedTreeRT::SetLodLevel,然后你获取到的就是经过LOD处理的几何数据。 不过有一点需要要注意的是,speedtree里面用的是右手坐标系(尽管它说可以通过define Y_UP来改变坐标系统,但我没发现define改了之后有什么变化,很奇怪)。笔者开始的时候完全没注意到这点,发现搬到自己的架构后,树全都是横着的。当时死活发现不了问题,就去旋转每棵树。然后又发现那些树叶也无法正常地旋转成billboard,又查了很久。后来终于发现,是因为speedtree内部使用右手坐标系进行计算。而我的架构是使用左手,这样一来,连传给speedtree camera的数据都要修改了, CSpeedTreeRT::SetCamera(eye, viewDir),其中的eye,eyeDir,都得经过变换再传进去: float3 viewDir=pCamera->GetViewDir(); float3 eye=pCamera->GetEye(); float afDirection[3]; afDirection[0] = viewDir.x; afDirection[2] = viewDir.y; afDirection[1] = -viewDir.z; CSpeedTreeRT::SetCamera(eye, afDirection); 4.把speedtree加到自己的引擎中去 以上所说的CSpeedTreeRT接口,笔者在使用的时候都是让一个CSpeedTreeRT对象汇聚到自己设计的一个tree类里。通过这种方式来封装speedtree,搭建中间架构。CSpeedTreeRT这接口也许多静态函数,譬如SetCamera,参照它的DEMO, 直接“CSpeedTreeRT::SetCamera(eye, eyeDir);”但要实现完美地跟自己的引擎相结合,也并不是一件容易的事情。主要是,自己的引擎本来就有一套完整的渲染系统,LOD系统,动画系统,而且跟speedtree的方式也不一样。一个极端的做法就是,对于SpeedTreeRT,屏蔽其实时计算,而是根据自己引擎的系统计算,这样的话, 其实是只利用了SpeedTree的数据结果了。而另外一个极端就是,不管 speedtree和自己引擎的关系,只保留简单的耦合,各自使用各自的系统,只是让他们的渲染行为(LOD,光照效果等)保持一致性。至于更好的办法,笔者也是在研讨中,我非常希望能跟读者进行探讨,这也是笔者写本笔记的动机之一。
Game组成员超过Hack组,不过Game的大部分人都不认识,有的甚至没有联系过。 我的好友中还有部分Hack没有联系上,至少应该还有20名左右吧,实际上加起来应该还比Game组的多。 我的好友中大部分应该是搞过C++或WinSDK编程的,至少是学过编程。 怀念从2000年至今的编程学习生涯,这是第三个QQ了, 最早一个QQ上的人大部分现在都应该是大牛了吧,可惜偶还是小弟,悲哀之。 这么多年过去了,发现自己依旧是那么无知无畏。 人一生所寻找的也许就是脚下的路吧,这样的人生才会意义。 这样才过的踏实一点,去年跟一朋友来到福州,来这里做游戏, 等到实际工作中才发现游戏不比传统软件行业, 这里的人比传统软件行业时的人更有想法,但大部分都不切换实际,甚至弱智。 为一点所谓的利益搞来搞去,其实真正的利益在BOSS那里,因为BOSS付出了。 不付出是得不到回报的,搞来搞去只是在牺牲自己的脑细胞。 不坚持原则,不追求完美,不追求质量,不切合实际,不明确目标。 完美的程序员至少是个政治家,虽然我不完美,但我是政治家,不要和我玩政治。 这里没有失败,因为我们只是为BOSS服务的。 损害别人并不能得到什么,因为每个人都选择的权利,因为和谁玩牌都一样。 输掉的只是大家的时间。
The Release builds include optimizaed code, but with the NiMemory system, NiMetrics, and release mode logging enabled. The Shipping builds do not have these systems enabled. ship 和 release 工程设置基本相同,但没有NiMemory、NiMetrics、release mode logging.更像是非常稳定之后的版本.
不可否认 选择做程序员 源于真正意义上的兴趣和喜欢 在做程序员的这几年 我的生活也在一步步的提升 但同样 不可否认 在我个人眼里 就像选择其他种类的工作一样 只是一份职业 一种在社会上存身立足的手段 现在呢 又在想 自己以前想过 大家也时不时的都会想 的问题 那就是 程序员的人生 将如何规划 在校时 以及 工作后 都曾这么的想过: 做一个IT从业人员 做一个程序员 做上个一二年 然后 向上提升下 做一个项目经理 什么的 再然后 或许 开一家自己的小软件公司 或许 做一名软件顾问 再或者 开一个网站 开发一个自己的小软件 或者 真的不行的 利用做程序员这几年的积蓄 做一些小买卖 转行 另谋生计 总之 感觉做程序开发将是人生的一个过渡 可若真的按这种思路一过渡 就将是用我人生的青春年华大好时光的五六年 或者更多时间 遗憾的是 至今 对这种付出后的收获 我却没有把握 曾听有人言(这一定是国人说的): 一年管理成富翁 三年市场路路通 十年技术一场空 这话每每想来 心里都不免有些低落 低落的不是现在 而是在现在看来 自己一两年或几年后的生活 没有着落 没有依靠 而那时的自己 或许做了项目经理 薪资在才做程序员的后辈们看来 已很是不低 但却可能远远不够日常生活的进一步开支 因为我们每个人每时每刻都有让生活越来越好的念头和目标 我们不想自己上去了 又下去了 不想自己 由前几年刚毕业的 蓝领代码工人 好不容易辛苦努力才做到今天的 白领项目经理 接着却因年事问题 薪资待遇问题 而沦为 房奴 车奴 我们不回避 人生将由 1.幼年-童年-少年-青年 2.成年-壮年 3.老年 这三段的划分 也不忌讳自己真的有一点老不中用了 但我们不能容忍自己在 从三十岁到五十岁 这段时间里 碌碌无为 而现在 我们站在目前自己做程序员的角度 去看三五年后的而立之年 我们心里没底 我们站在而立之年的程序员的角度 去看自己30-50的人生成熟和收获的黄金时期 我们更多的可能看到的是灰暗和苦涩 那么 早知如此 何必当初 想问大家 也是在问自己 一句 程序员的人生 该将如何规划? ( 希望成功的前辈们 能多多赐教 也希望有同样思考的同辈们 能说说自己好的想法和规划 或许 这个问题 不单单是属于做程序员工作的同行们 也可能是属于所有现在 没有自己的事业 正在工作着的 各行各业的同志们 我思 故我在 没有对明天的思考 明天的我 也许就没有美好的未来 也许换种环境更好 比如出国 至少不会有职业歧视 至少技术与业务一视同仁 至少会认为每个人的工作同样重要 ) *注:转载,部分修改.
由于项目的原由最近接触了很多优秀的项目,其中包括HTML排版引擎,以下对其做下简单的介绍和比较. 现在浏览器的内核引擎,基本上是三分天下: - Trident: IE 以Trident 作为内核引擎。
- Gecko: Firefox 是基于 Gecko 开发。
- WebKit: Safari, Google Chrome 基于 Webkit 开发。
- Trident
Trident (又称为MSHTML),是微软的窗口操作系统(Windows)搭载的网页浏览器—Internet Explorer的排版引擎的名称,它的第一个版本随着1997年10月Internet Explorer第四版释出,之后不断的加入新的技术并随着新版本的Internet Explorer释出。在未来最新的Internet Explorer第七版中,微软将对Trident排版引擎做了的重大的变动,除了加入新的技术之外,并增加对网页标准的支持。尽管这些变动已经在相当大的程度上落后了其它的排版引擎,如Gecko、WebCore、KHTML及Presto。 Trident引擎被设计成一个软件组件(模块),使得其它软件开发人员很容易的将网页浏览的功能加到他们自行开发的应用程序里。微软提出了一个称为组件对象模型(COM)的软件接口架构。供其它支持的组件对象模型开发环境的应用程序(如:C++及.NET)存取及编辑网页。例如,由C++所撰写的程序可以加入浏览器控件里,并透过Trident引擎存取当前显示在浏览器上的网页内容及网页的各种元素的值,从浏览器控件触发的事件亦可被程序撷取并进行处理。Trident引擎所提供的所有函式库可以透过与 mshtml.dll这个档案的连结而达成撰写程序时所需要的功能。 除此之外,微软还有另一个网页浏览器排版引擎,称为Tasman,它是使用在「Internet Explorer for Mac」的排版引擎。相较于Trident,Tasman引擎对网页标准有较佳的支持。与普遍的看法相反的是,微软已经停止了麦金塔计算机版本的 Internet Explorer的开发,但Tasman的开发仍旧持续, 新版本的Tasman引擎仍被应用在一些微软产品上,如:麦金塔计算机版本的Microsoft Office。 使用Trident引擎的浏览器有很多,比如Maxthon,腾讯TT,MyIE等等,但Trident只能应用于Windows平台. - Gecko
Gecko是套开放源代码的、以C++编写的网页排版引擎。目前为Mozilla家族网页浏览器以及Netscape 6以后版本浏览器所使用。这软件原本是由网景通讯公司开发的,现在则由Mozilla基金会维护。 这套排版引擎提供了一个丰富的程序界面以供互联网相关的应用程式使用,例如网页浏览器、HTML编辑器、客户端/服务器等等。虽然最初的主要对象是Mozilla的衍生产品,如Netscape和Mozilla Firefox,现在已有很多其他软件现在利用这个排版引擎。Gecko是跨平台的,能在Microsoft Windows、Linux和Mac OS X等主要操作系统上运行。 Gecko是最流行的排版引擎之一,其流行程度仅次于Trident. 使用Gecko引擎的浏览器有Firefox, 网景(6至9), SeaMonkey, Camino, Mozilla, Flock, Galeon, K-Meleon, Minimo, Sleipnir, Songbird , XeroBank. -
WebKit WebKit是Mac OS X v10.3及以上版本所包含的软件框架(对v10.2.7及以上版本也可通过软件更新获取)。 同时,WebKit也是Mac OS X的Safari网页浏览器的基础。WebKit是一个开源项目,包含了来自KDE项目和苹果公司的一些组件。 目前使用WebKit 引擎的浏览器主要有:Safari(apple出品),Midori,chrome(google出品)等。 WebKit拥有清晰的源码结构、极快的渲染速度。
现代游戏已经不能没有声音,所以音频引擎成为游戏引擎中不可缺少的一部分.这是一篇介绍现代音频引擎的文章(http://hard.zol.com.cn/labs/2003/0520/60986.shtml).FMOD音频引擎(http://www.fmod.org)是一个非常不错的音频引擎,其使用也比较简单,下面做一些简单介绍: 一.基本准备 它是免费的,你可以从它们的主站上下载API等文件.之后,你需要添加头文件和库文件,如下(C/C++): fmodvc.lib 用于 Microsoft Visual C++ 和 Codewarrior fmodbc.lib 用于 Borland fmodwc.lib 用于 Watcom fmodcc.lib 用于 LCC-Win32 libfmod.a 用于 MingW and CygWin fmod-3-7.lib 用于 GCC (参考:http://www.gamedev.net/reference/articles/article2098.asp) 之后,只要添加fmod.h头文件后就可以使用了. 二.开始使用 1.初始化 开始播放声音前,需要进行初始化,很简单: FSOUND_Init (44100, 32, 0); 第一个参数是输出HZ,第二是最大软件信道数可以不管也不会增加CPU负担,第三个参数可以设置一些标志可以不设置则赋值为0. 2.基本常识 FMOD将音频分为声音(sound)和音乐(music)两种.前者如:.MOD, .S3M, .XM, .IT, .MID, .RMI, .SGT or .FSB 等,后者如: .WAV, .MP2, .MP3, .OGG or .RAW等.二者使用不同的函数处理.都可以通过采样后流的方式来处理.不过小文件一般通过采样方式,它可以多次播放但占用内存.大文件通过流方式,减少内存消耗. 3.播放音乐 首先定义一个FMUSIC_MODULE类型变量来作为文件句柄.然后就可以通过FMUSIC API来实现,如: 装入文件: handle=FMUSIC_LoadSong("YourFileName"); FMUSIC_PlaySong(handle); 音量控制:FMUSIC_SetMasterVolume (handle, 255);后面的参数在0~255之间,值越大声音越大. 暂停播放:FMUSIC_SetPaused (handle, true); 重开始:FMUSIC_SetPaused (handle, false); 循环播放:FMUSIC_SetLooping (handle, true); 停止播放:FMUSIC_StopSong (handle); 释放音频内存:FMUSIC_FreeSong (handle); 下面是一个命令模式下的例子: #include <conio.h> #include "inc/fmod.h" FMUSIC_MODULE* handle; int main () { // 初始化 FSOUND_Init (44100, 32, 0); // 装如 handle=FMUSIC_LoadSong ("canyon.mid"); // 只播放一次 // 播放midi文件时请关闭循环播放 FMUSIC_SetLooping (handle, false); //播放 FMUSIC_PlaySong (handle); // 按任一键结束 while (!_kbhit()) { } //释放 FMUSIC_FreeSong (handle); FSOUND_Close(); } 4.播放声音 4.1 采样(Sample)方式 先定义FSOUND_SAMPLE类型变量,然后就可以使用FSOUND系列函数来实现,如: 装如文件: handle=FSOUND_Sample_Load (0,"YourFileName",0,0,0); //除文件名外的参数用于多采样或其它等 FSOUND_PlaySound (0,handle); 设置音量:FSOUND_SetVolume (handle, 255); 暂听:FSOUND_SetPaused (handle, true); 重新开始:FSOUND_SetPaused (handle, false); 停止:FSOUND_StopSound (handle); 释放:FSOUND_Sample_Free (handle); 下面是一个简单的例子: #include <conio.h> #include "inc/fmod.h" FSOUND_SAMPLE* handle; int main () { // 初始化 FSOUND_Init (44100, 32, 0); // 装载和播放 handle=FSOUND_Sample_Load (0,"sample.mp3",0, 0, 0); FSOUND_PlaySound (0,handle); // 按任一键结束 while (!_kbhit()) { } // 释放 FSOUND_Sample_Free (handle); FSOUND_Close(); } 4.2 流(stream)方式 先定义一个FSOUND_STREAM 类型变量,然后: 装入文件: handle=FSOUND_Stream_Open("YourFileName",0, 0, 0); FSOUND_Stream_Play (0,handle); 提示:3.7版本之前的方式是不一样的. 停止:FSOUND_Stream_Stop (handle); 释放:FSOUND_Stream_Close(handle); 其它和前面是一样的.下面是一个简单的例子: #include <conio.h> #include "inc/fmod.h" FSOUND_STREAM* handle; void main () { //init FMOD sound system FSOUND_Init (44100, 32, 0); //load and play sample handle=FSOUND_Stream_Open("sample.mp3",0, 0, 0); FSOUND_Stream_Play (0,handle); //wait until the users hits a key to end the app while (!_kbhit()) { } //clean up FSOUND_Stream_Close(handle); FSOUND_Close(); } 5.关闭 FSOUND_Close (); 参考: A Quick Guide to FMOD by Joachim Rohde(http://www.gamedev.net/reference/articles/article2098.asp) FMOD wiki(http://www.devmaster.net/wiki/FMod)
看到Fox发表关于 成员函数的消息映射的文章,也忍不住发表的一点自己的观点,希望对大家有所帮助。 其实也就是COMMAND模式的简单实现,看代码吧。 1. XGUIEventHandlerPointer.h
namespace XGUI { class EventHandlerSlot { public: virtual ~EventHandlerSlot() {}; virtual void execute(const EventArgs& args) = 0; virtual void* getClass() = 0; };
template<typename T> class EventHandlerPointer : public EventHandlerSlot { public: typedef void (T::*EventHandler)(const EventArgs&); public: EventHandlerPointer() : d_undefined(true), d_object(NULL) {} EventHandlerPointer(EventHandler func, T* obj) : d_function(func), d_object(obj), d_undefined(false) {} virtual ~EventHandlerPointer() {}
void execute(const EventArgs& args) { if(!d_undefined) (d_object->*d_function)(args); }
void* getClass() { return static_cast<void*>(d_object); }
protected: EventHandler d_function; T* d_object; bool d_undefined; }; }
2. XGUIWidget.h
namespace XGUI { // class Widget { public: template<typename T> void addWidgetEventHandler(WidgetEvent EVENT, void (T::*function)(const EventArgs&), T* obj) { mWidgetEventHandlers[EVENT].push_back(new EventHandlerPointer<T>(function,obj)); } void addWidgetEventHandler(WidgetEvent EVENT, EventHandlerSlot* function) { mWidgetEventHandlers[EVENT].push_back(new EventHandlerPointer<T>(function,obj)); } bool Widget::fireWidgetEvent(WidgetEvent EVENT, EventArgs& args) { // If there are no User defined event handlers we are done. if(mWidgetEventHandlers[EVENT].empty()) return false; // Execute registered handlers std::vector<EventHandlerSlot*>* userEventHandlers = &(mWidgetEventHandlers[EVENT]); for(std::vector<EventHandlerSlot*>::iterator it = userEventHandlers->begin(); it != userEventHandlers->end(); ++it ) (*it)->execute(args); return true; } // . }; }
以上只为部分代码。
摘要: 作者:炎龙工作室 千里马肝 版本:v1.0 最后更新日期:2002-3-30 绪言 在游戏中,因为我们是中国人麻,通常都需要显示汉字,比方说交待剧情。而对于文字的显示,英文的显示要较其简单得多,因为只有26个字母,就算再加一些标点、符号什么的,用一张位图,就可以足以显示所有的单词了,而相关实现技巧,也比较轻松。 而中文的显示方法,要复杂得许多。记得原来在DOS下,汉字的显示都是读的UCDOS的点阵... 阅读全文
头文件, 处理憔悴, 编译器, 报错如风雪。 是谁混淆基本类, 惹变量是非。 虚基类, 构造轮回, 动态堆, 字符串唤不回。 纵然代码已经成灰, 内存不灭。 循环如三千东流水, 我嵌套一瓢爱了解, 只恋函数的递归。 你发如雪, 定义了离别, 我指针指向了谁, 邀明月, 让地址皎洁, 爱在数组里倾颓。 你发如雪, 初始化眼泪, 我联编继承了谁, 数据醉, 编程的岁月, 我用后悔, 刻电脑崩溃的碑。 啦儿啦 啦儿啦 啦儿啦儿啦 啦儿啦 啦儿啦 啦儿啦儿啦 去约会她斜扎儿马尾只因为学C++学到了崩溃 http://www.cppblog.com/Lee/archive/2008/09/17/62108.html
|