|
|
2006年02月22日
摘要: 前面已经说过程序就是方法的描述,而方法的描述无外乎就是动作加动作的宾语,而这里的动作在C++中就是通过语句来表现的,而动作的宾语,也就是能够被操作的资源,但非常可惜地C++语言本身只支持一种资源——内存。由于电脑实际可以操作不止内存这一种资源,导致C++语言实际并不能作为底层硬件程序的编写语言(即使是C语言也不能),不过各编译器厂商都提供了自己的嵌入式汇编语句功能(也可能没提供或提供其它的附加语法以使得可以操作硬件),对于VC,通过使用__asm语句即可实现在C++代码中加入汇编代码来操作其他类型的硬件资源。对于此语句,本系列不做说明。 语句就是动作,C++中共有两种语句:单句和复合语句。复合语句是用一对大括号括起来,以在需要的地方同时放入多条单句,如:{ long a = 10; a += 34; }。而单句都是以“;”结尾的,但也可能由于在末尾要插入单句的地方用复合语句代替了而用“}”结尾,如:if( a ) { a--; a++; }。应注意大括号后就不用再写“;&rdq (全文共19737字)——点击 此处阅读全文 你是否梦想写一部格斗游戏但却无从着手呢?是否你只因游戏开发好玩而对之感兴趣?本文我们将分析一个通用的跨平台游戏引擎,每个游戏开发新手都可以自由地使用它。 1. 3D游戏引擎的简短历史 在游戏开发中,从一开始就确定正确的开发平台是很重要的。是否你的游戏支持Windows,Linux和OS X?是否你的游戏开发只使用OpenGL就足够了?OpenGL是十九世纪九十年代初期设计的,起初只运行于价值约$25,000的Unix CAD工作站上,后来移植到Windows和其它一些低端平台上。与此同时,随着游戏工业的发展,图形加速器价格从$2,000剧跌到你今天看到的价值约$150的大众市场价格。 确实,许多人都会援引在1996年用OpenGL开发成功的革命性的游戏Quake,作为以上急速发展现象的直接的原因。然而,成功的Quake级的游戏开发标准要求更多:世界级音频支持,网络连接,用户输入设备支持,以及实时的管理能力等。既需要实现跨平台支持又能使游戏效果激动人心,要实现这样的解决方案最好建立一个体面的游戏开发站台。 2. 用于C++,Java和其它开发语言的简单DirectMedia层 对,历史就是这样有趣,但并不是每一部游戏都要做成Quake的克隆品。一直被业界许多人吹捧有着许多优点的选择是简单DirectMedia层(SDML)。这是一套跨平台的多 媒体库,它提供对于音频,键盘,鼠标,游戏杆,OpenGL和2D视频帧缓冲的低级存取。SDML支持几乎我能想像出的每一个平台,包括Linux,Windows,所有的MacOS变异物,WinCE,Dreamcast还有另外一些操作系统。它被广泛应用于开发MPEG播放器,硬件仿真器,和许多流行的游戏,包括获奖的运行于Linux平台的Civilization:Call to Power。 SDML用C写成,但生来就与C++一起工作,已经绑定到了另外许多语言,包括Ada,Eiffel,Java,Lua,ML,Perl,PHP,Pike,Python和Ruby。SDML的应用环境简直就没有什么限制,而且它碰巧是我最喜爱的开源飞行模拟器GL- 117(见图1)的开发引擎。事实上,513游戏的当前开发已经基于SDML引擎而且被注册到了SDML的主页。  图1.GL-117中的一个视图 |
3. 通道视觉效果演示程序 研究游戏引擎的最好方法是看一些示例程序代码。简单地看一下图2中用SDML实现的2D通道类型演示图,你就能发现你仅用几行代码所能完成的工作。你可以使用该实例作为一个保护屏程序,音乐可视化动画效果,等等。篇幅所限,我已经整理了实际的绘制代码。请跟随我的注释分析下面对SDML的工作原理的描述: #include "Tunnel.h" // SDL 相关变量定义 SDL_Surface *screen,*bBuffer,*Image; SDL_Rect rScreen,rBuffer; int main (int argc, char **argv) { int flag = SDL_SWSURFACE;// 请求一个软件表面. //软件表面处于系统内存中, // 一般不如硬件表面速度快 #ifdef WIN32 int fullscreen = MessageBox(NULL, "Show Full Screen (y/n):","Screen Setting", MB_YESNO); if (fullscreen==IDYES) { flag |= SDL_FULLSCREEN; // 如果用户需要,接管整个屏幕 } #endif Tunnel_Timer(); // 读取起始的系统时钟值 SDL_Init( SDL_INIT_VIDEO ); // 初始化视频子系统 //把屏幕设置到 320x240,32位颜色 screen = SDL_SetVideoMode( 320, 240, 32, flag); // 如果可用的话,为屏幕表面请求硬件缓冲 bBuffer = SDL_CreateRGBSurface( SDL_HWSURFACE, screen->w,screen->h,screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask); // 这是种子图像,一旦开始它就会盘旋起来 Image = SDL_LoadBMP( "tunnel_map.bmp" ); Image = SDL_ConvertSurface(Image, screen->format, SDL_HWSURFACE); rBuffer.x = 0; rBuffer.y = 0; rBuffer.w = bBuffer->w; rBuffer.h = bBuffer->h; // 忽视大多数事件, 包括 鼠标动作, 并取消光标 SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE); SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); SDL_ShowCursor( SDL_DISABLE ); Tunnel.Set( 320, 240 ); // 通道将填充整个的缓冲区 Tunnel.Precalc( 16 ); //内部的圆圈直径 while (SDL_PollEvent(NULL)==0) { float fTime = Tunnel_GetTime(); //在修改前,必须锁定表面,特别当缓冲区处于图形硬件内存中时 SDL_LockSurface(bBuffer); SDL_LockSurface(Image); Tunnel.Draw(bBuffer, Image, 180*sin(fTime), fTime*100); SDL_UnlockSurface(bBuffer); // 在更新以后你可以开锁 SDL_UnlockSurface(Image); // 把缓冲区中的数据输出到屏幕绘图区域并强迫进行重画 SDL_BlitSurface( bBuffer, NULL, screen, &rBuffer ); SDL_UpdateRect( screen, 0, 0, 0, 0 ); } Tunnel.Free(); } |
 图 2. 演示旋转和扭曲的2D通道 |
4. 对另外一些游戏引擎的探索 让我们看一下另外一些开源的游戏引擎。 a) ALLEGRO(Allegro低级游戏开发例程) Allegro是一个开源的可移植的库,主要针对视频游戏和多媒体编程。Allegro由Shawn Hargreaves(近来称为Climax)创建,现在成长为一个能够跨越许多操作系统如Linux,Windows,MacOS,MS-DOS和许多另外的流行平台等的游戏系统。 除了具有一个高级的2D图形库,它能容易地存取鼠标,键盘,游戏杆和高精度定时器中断。Allegro并没有包装或替换OpenGL,但是通过参观他们广阔的开发站点(http://www.allegro.cc/),你能学习怎样把OpenGL集成到Allegro游戏程序中。 大约有700种不同的游戏工程,与Allegro一起发行,其中最为杰出的两类是街机游戏和谜题游戏。我特别地喜欢经典的街机游戏Zaxxon(见图3)的重制品。  图3.酷毙的Zaxxon的重制品 |
b) Irrlicht:点燃快速实时的3D引擎 这个Irrlicht 引擎是一个跨平台,高性能实时引擎,用C++写成。你可以选择 Direct3D,OpenGL或基于软件的着色技术。高端特点包括动态阴影,粒子系统,人物动画,进门和出门技术和碰撞检测(见图4)。Irrlicht支持Windows和Linux并提供到语言Java,Perl,Ruby等的绑定。业界先驱Nikolaus Gebhardt在他的朋友的少部分帮助下完成的这个引擎工。  图4.在Irrlicht中的一个十分逼真的场景 |
c) ClanLib:为多玩家游戏设计的引擎 ClanLib提供了一个平台独立的接口来书写游戏-它们有一个共同的到低级库如DirectX和OpenGL的接口。借助于ClanLib,你只需编写少量代码即可在Windows,Linux和OSX系统上开发游戏程序。ClanLib包括一个广泛的声音库,2D碰撞检测,动画, GUI框架和网络库。图5显示了游戏XenoHammer中的一个场景。  图5.XenoHammer屏幕快照 |
要:本文重点讨论开源游戏开发库Allegro(Allegro低级游戏例程),同时涉及到一些深度技术并提供了一个简单的示例程序,帮你进一步确定它是否是适合你的开发平台。 一、 一个适于多环境的引擎 Allegro最开始被研发于八十年代后期古老的Atari ST平台上,随后被快速地移植到流行的DJGPP环境(一个在九十年代早期流行的32位的 MS-DOS扩展程序)。此后,Allegro被移植到最为流行的Windows C++开发环境中,包括VS,MinGW,Cygwin和Borland C++。另外的支持它的平台包括Linux,BeOS,QNX,Mac OSX以及几乎任何其它带有X11库的Unix平台上。 Allegro能着色到各种类型的位图和硬件加速的环境中,例如DirectX,XWindows,SVGAlib,FreeBE/AF,CGDirectDisplay,QuickDraw,等等。Allegro并不想提供它自己的3D环境或模拟器,但是OpenGL可以被容易地集成,这是通过使用AllegroGL库-它提供了一个类似于GLUT的接口(包括扩展管理)-实现的。 二、 性能概要 在进一步使用API开发前,让我们看一下Allegro提供的总体功能: ·具体到像素级的绘图函数,包括平坦阴影,gouraud阴影,纹理贴图,z缓冲的多边形和圆绘制,填充, 贝塞尔样条曲线,图案填充,精灵,blitting(位图复制),位图计算缩放和旋转,半透明/光效果以及比例字体支持的文本输出 ·FLI/FLC(在计算机生成的动画方面,这种格式比MPEG有更高的压缩性能)动画播放器 ·播放后台MIDI音乐,可达64种同时的声音效果,并能录制样本 波形和MIDI输入(声音平台支持,包括WaveOut,DirectSound,OSS,ESD,CoreAudio和QuickTime,等等) ·容易地存取鼠标,键盘,游戏杆等设备,还支持高分辨率定时器中断,包括一个DOS版本的垂直折回中断模拟器 ·读/写LZSS压缩文件的例程 ·数学函数,包括定点算术,表查找和3D矢量/矩阵/四元数操作 ·GUI对话框管理器和文件选择器 ·内建地支持16位和 UTF-8格式的 Unicode字符 三、 使用引擎 使用Allegro进行开发,就象在许多其它游戏场合下一样,游戏的总体结构都包括游戏开始前的初始化,游戏循环以及游戏完成后的清理。初始化意味着既包含Allegro启动代码也包含在开始的位置实现基本地装载或生成你的游戏级别。在创建你的初始化代码时,启动Allegro基本上没有什么代价付出(见图1). 如果你需要很多屏幕相关的真实性能,建议你首先礼貌地用get_gfx_mode_list()函数查询一下最大可用方式: #include <allegro.h> //必须放于系统头文件的引用之后 set_color_depth(32); // 缺省情况下使用8位颜色 if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0) != 0) { abort_on_error("Couldn’t set a 32 bit color resolution"); } |
set_gfx_mode()的最后两个参数用于指定虚拟缓冲区的大小-我们的图形屏幕存储于其中。这可以使创建一个卷边游戏-其中地形是连续移动的-变得容易。例如,你可能要使虚拟缓冲区,比方说,宽出20%以留出足够的空间来平滑卷动新的精灵和地形。
四、 一个完整的Allegro实例
本教程将使用Kee-Yip Chan的SnookerClone演示程序,它是基于James Lohr的另一个具有相同名字的演示程序。图1显示了演示程序的基本屏幕快照。
 图1.Kee-Yip Chan的"SnookerClone"演示程序 |
这个工程向你展示了许多不同的Allegro技术,包括动画,键盘输入和鼠标输入,碰撞和游戏物理知识(例如重力)。它利用了三个主要的元素:一个有8个扶手的旋转的车轮,一个用箭头键来控制的大红球,还有一些从顶部往下坠落的蓝球。车轮以接触方式推动红球,而当红球碰上蓝球时,它们之间相互影响。 下列是完整的Allegro演示程序的代码: 1 #include <allegro.h> 2 vector<Point> g_points; //aka球上点的列表 3 vector<Joint> g_joints; //物理对象列表,如车轮和缓冲器 4 kVec g_accControl; 6 int main(void) 7 { 8 allegro_init(); // 初始化allegro. 9 install_keyboard(); // 启动键盘. 10 install_mouse(); // 启动鼠标. 11 install_timer(); //过程show_mouse()所需要; 13 // 创建一个800x600的非全屏窗口. 14 set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0); 16 set_window_title("Kee-Yip Chan’s Snooker Clone"); 17 text_mode(-1); // 文本将被画在透明的背景之上 19 BITMAP* buffer = create_bitmap(SCREEN_W, SCREEN_H); //创建一张位图用于双缓冲. 21 // 初始化数据. 22 create_joints(g_joints); //注册车轮、地板和缓冲器的硬编码的屏幕位置 25 // 创建顶点以组成aka球: 玩家所用球和三个蓝球 26 // 的位置, 速度, 大小和质量. 27 g_points.push_back(Point(kVec(100, 300),kVec(0, 0),16, 10)); // 玩家. 28 g_points.push_back(Point(kVec(50, 40), kVec(0, 0),12, 5)); // 中等的球. 29 g_points.push_back(Point(kVec(80, 40), kVec(0, 0) 12, 5)); //中等的球. 30 g_points.push_back(Point(kVec(110, 40),kVec(0, 0),6, 1)); // 小球. 32 //主循环,在按ESC键后退出 33 while(!key[KEY_ESC]) { //检查输入. 34 if(key[KEY_UP]) 35 g_accControl.y = -0.07; //Jet pack.向上加速 36 if(key[KEY_LEFT]) 37 g_accControl.x = -0.07; //左走.向左加速 38 if(key[KEY_RIGHT]) 39 g_accControl.x = 0.07; //右走.向右加速 41 static bool leftMousePressed = false, rightMousePressed = false; 42 if(mouse_b & 1) { //鼠标左键按下 43 if(!leftMousePressed){ 44 leftMousePressed = true; // 创建一个新球. 45 g_points.push_back(Point(kVec(mouse_x, mouse_y),kVec(0, 0), 12, 5)); 46 } 47 } 48 if(!(mouse_b & 1)) 49 //保证不重复鼠标按键 50 //否则,就会出现许多的新球 51 leftMousePressed = false; 52 if(mouse_b & 2) { //鼠标右键按下 53 if(!rightMousePressed){ 54 rightMousePressed = true; // 创建一个新球 55 g_points.push_back(Point(kVec(mouse_x, mouse_y),kVec(0, 0), 6, 1)); 56 } 57 } 58 if(!(mouse_b & 2)) 59 //保证不重复鼠标按键 60 //否则,就会出现许多的新球. 61 rightMousePressed = false; 63 doPhysics(); 65 // 着色:如果我们能再次使用缓冲区,则清除它; //否则,旧图像将滞留显示 66 //用白色进行清除. 67 clear_to_color(buffer, makecol(255, 255, 255)); 68 for(unsigned i = 0; i < g_points.size(); i++) { //画点. 69 //画一个实心球 70 circlefill(buffer, //画向缓冲区 71 g_points[i].position.x,g_points[i].position.y,// aka 球的中心点的位置 72 g_points[i].size, // 半径. 73 (i == 0) ? makecol(255, 0, 0) : makecol(0, 0, 255)); //红色如果是玩家;否则为蓝色 75 // 画一个轮廓球. 76 circle(buffer, //画向缓冲区 77 g_points[i].position.x,g_points[i].position.y, // aka 球的中心点的位置. 78 g_points[i].size, // 半径. 79 makecol(0, 0, 0)); //红色如果是玩家;否则为蓝色. 81 } 83 // 画接合点 84 for (unsigned i = 0; i < g_joints.size(); i++) 85 line(buffer, //画向缓冲区 86 g_joints[i].p1.x, g_joints[i].p1.y, // 点 1. 87 g_joints[i].p2.x, g_joints[i].p2.y, // 点 2. 88 makecol(0, 0, 0)); // 黑颜色. 89 ); 91 // 打印指令. 92 textout(buffer, font, "Left Mouse Button - new big ball Right Mouse Button - new small ball", 93 125, 1, makecol(0, 0, 0)); 95 textout(buffer, font, "Arrow Keys - move red ball", 96 300, 592, makecol(0, 0, 0)); 98 show_mouse(buffer); // 画鼠标光标. 100 draw_sprite(screen, buffer, 0, 0);// 把缓冲区中的数据画向屏幕. 101 } // while循环结束 103 return 0; 105 }END_OF_MAIN(); |
33-101行包括了典型的游戏编程循环模式。游戏继续进行直到玩家按下ESC键退出为止。34-39行支持同时进行的键盘输入,因为你可以按下向上和向左箭头键来获取粗略的斜向运动。 在41-61行,鼠标动作被捕获到全局变量mouse_b(用于按钮),mouse_x和mouse_y。如果你一直在使用一滚轮鼠标,你还可以使用变量mouse_z。我们对反向弹跳逻辑进行了一点硬编码以确保每次鼠标按下事件只有一个球下落。 第63行调用doPhysics(),其目的是旋转车轮的线段,更新球位置,检测球碰撞和适当地改变它们的方向矢量。这个模块(350行的数学代码)有点深入了些,但它确实是一个一流的实现,值得你深入研究。 余下的代码,65-101行,开始着色,在典型的示例程序中这属于常规实现部分。这里的着色用典型的双缓冲区技术,下一次屏幕变化被计算出来并进行脱屏绘制并在最的一毫秒进行缓冲交换(第100行)。这确保了视觉的连续性又减少了烦人的闪烁-对象看上去是随机地绘制的。在着色代码部分,对line()和circlefill()的调用是相当直接的:circlefill()以x,y,半径和填充颜色作为参数。 textout_ex()函数的功能稍强于textout()(示于92-96行),允许你指定前景和背景颜色。Allegro提供例程以直接从GRX格式.fnt文件,8x8或8x16 BIOS格式.fnt文件,位图图象以及数据文件格式中装入字体。作为选择,你能导入一种大范围的Unicode字体,这可以通过写一个.txt脚本-它为每一范围的字符指定相应的不同的源文件-来实现。如果你想要支持TrueType,那么你需要AllegroTTF或相同功能的插件。 最后,在第100行的draw_sprite()实现一个覆盖性复制新生成的位图到第14行创建的屏幕对象上。覆盖性复制意指只有非透明的颜色像素被复制。在本例中,我确信它已被退化成一个"blit"(块复制)转储。 五、 Allegro的音频 这个snooker演示程序只涉及到了一些最基本的图形和I/O函数,但是并没有用到Allegro的音频开发包。该包中的MIDI混频,音响效果和录音API,其效果达到或超过几乎每一个我所见过的专业的声音库。Allegro音频应用软件大量存在,包括WinCab-一个MP3和OGG Vorbis音乐唱片机,还有LouTronic Rhythm Box-一个鼓声生成合成器,它具有可全面融合到一起的snare鼓,低音鼓和hi hat的效果。下面我们简单地回顾Allegro音频API的一小部分。 每一个使用音频的程序都应该调用reserve_voices()来指定数字和MIDI声音驱动程序分别使用的声音的数目。接下去,你能控制这些音频轨道的混合. 你可以非常容易地象下面这样插入一个音轨: MIDI *midFile = load_midi("myfile.mid’); play_midi(midFile, TRUE);//连续循环 |
对于更复杂的需要,你可以安装三个钩子函数之一,它们可以使你拦截MIDI玩家事件。如果被设置为非NULL,这些例程将在每次MIDI消息,元事件和系统独占的数据块中被分别调用。 Allegro的数字音频系统被设计为从最基本的配置到可高度扩展的。你能容易安装读取器和写入器来处理新的或者不同的音频文件类型, 例如: | register_sample_file_type("mp3",load_mp3,NULL);//安装MP3读取器 |
当正播放数字音频时,你可以随时编辑它。下列代码改变将在播放一个样本参数时改变该样本(用于操作循环播放的声音): | void adjust_sample(const SAMPLE *spl, int vol, int pan, int freq, int loop); |
你能改变音量,平移音频数据并清除循环标志,在下次执行到循环末尾时,这将停止该样本。如果在播放相同样本的好几个副本,这会调整它遇到的第一个副本。如果该样本没有播放,对它没有任何影响。
Irrlicht引擎是一个用C++书写的高性能实时的3D引擎,可以应用于C++程序或者.NET语言中。通过使用 Direct3D(Windows平台),OpenGL 1.2或它自己的软件着色程序,可以实现该引擎的完全跨平台。尽管是开源的,该Irrlicht库提供了可以在商业级的3D引擎上具有的艺术特性,例如动态的阴影,粒子系统,角色动画,室内和室外技术以及碰撞检测等(见图1)。  图1.Irrlicht 3D引擎 |
Irrlicht是一个德国神话故事中的一种动物的名字,它能够发光和飞翔,可以在大部分的沼泽地附近发现它。单词"Irrlicht"是两个德国单词("irr"意思是疯狂的;而"Licht"意思是光)的组合。在英语中,它被译为"鬼火"。 Irrlicht十分幸运地为一个巨大的活跃的开发团队以大量的工程所支持。然而,因为Irrlicht主要由游戏名家Nikolaus Gebhardt所设计,所以该游戏在设计上十分连贯。你可以在网上到处发现有Irrlicht的增强程序,如可选用的地形生成器,入口生成器,输出器,world层生成器,相关教程和编辑器等。而且,它独立地创建了到Java,Perl,Ruby,BASIC,Python,LUA甚至更多种语言的绑定。而最为重要的是,它是完全自由的。 二、 Irrlicht特性 在深入分析API之前,请让我更具体地介绍一下Irrlicht提供给了3D游戏开发者哪些功能: ·一个可以运行于Linux以及Windows 98,ME,NT,2000和XP(MacOS在计划之中)等操作系统之上的引擎 ·针对Direct3D 8生成器或Direct3D 9生成器(可选)提供了Anti-aliasing支持 ·可换肤的GUI环境(包括一个很酷的具有金属质地的带阴影的皮肤),给一些老式的对话框加上漂亮的外观 ·场景管理系统,它允许无缝的室内/室外过渡 ·角色动画系统,带有骨骼和变形目标动画功能 ·一个特殊的效果系统,包括粒子效果(雨,烟,火,雪,等等),告示板,灯光贴图,环境,地图,模板缓冲区阴影,雾,纹理动画,视差贴图, 凹凸贴图,还有更多 ·内建的材质支持,包括支持Pixel and Vertex Shaders版本1.1到3.0,ARB Fragment and Vertex程序以及HLSL(GLSL正在计划中) ·.NET语言绑定,这使得引擎可用于所有的.NET语言例如C#,Visual Basic.NET以及Delphi.NET ·一内建的平台独立的软件生成器,特性有:z-缓冲,Gouraud阴影,alpha混合和透明性,还有快速的2D绘图(见图2) ·你久已期待的2D绘图功能,例如alpha混合,基于关键色的位图复制,字体绘制,以及混合3D与2D图形 ·能直接导入常见的建模文件格式:Maya,3DStudio Max,COLLADA,DeleD,Milkshape, Quake 3 levels,Quake2 models,DirectX,Pulsar,My3DTools,FSRad以及Cartography Shop ·能直接从BMP,PNG,Photoshop,JPEG, Targa和PCX导入纹理 ·快速而易用的碰撞检测与响应 ·为快速的3D运算和容器模板库进行了优化处理 ·直接读取档案(可能是压缩的,如.zip文件) ·集成了快速的XML分析器 ·为实现容易的本地化开发提供Unicode支持  图2:基于Irrlicht的游戏Yet Another Space Shooter(YASS),这里显示的是一个静态游戏帧中的令人吃惊的着色效果 | 三、 在Irrlicht中的特殊效果 在本文的例子中,我将向你展示怎样使用模板缓冲区影子技术,还有粒子系统,告示板,动态光以及水表面场景结点等技术。参见图3。  图3.结合动态的光和水进行的场景着色 |
Irrlicht引擎自动地检查是否你的硬件支持模板缓冲;而如果不支持,则不启动阴影。在这个演示程序中,在方法createDevice()中的’shadows’标志被置位,以产生从一个动画角色投下的动态影子。如果这个实例程序在你的PC上运行太慢,可以把这个标志设置为false或者干脆再买一块更好些的图形加速卡。 为能够使用Irrlicht.DLL文件,你需要链接到Irrlicht.lib库文件。你可以在工程设置对话框中设置这个选项;但是为了容易实现,你可以使用一个pragma预编译注释命令。方法createDevice()负责实例化根对象-它使用引擎完成一切事情。参数如下: ·deviceType:设备类型。当前你可选取Null设备以及软设备,如DirectX8,DirectX9或OpenGL。 ·windowSize:要创建的窗口的大小或全屏幕模式。这个例子中使用512x384。 ·bits:每像素位数(当在全屏幕情况时)。仅允许值为16或者32。 ·fullscreen:指定是否你想使设备运行于全屏幕方式。 ·stencilbuffer:指定是否你想使用模板缓冲区以用于绘制阴影。 ·vsync:指定是否你想启动vsync(仅在全屏幕情况),可选。 ·eventReceiver:一个接收事件的对象,可选。 为适合于本实例环境,你将装载一个3D Studio Max文件(一幢房子)。该房子看起来并没有什么特别的,但是Irrlicht引擎能为你创建一个相当酷的纹理贴图。只需使用造型操纵器并为之创建一个planar纹理贴图即可: #include <irrlicht.h> #include <iostream> using namespace irr; #pragma comment(lib, "Irrlicht.lib") int main() { //让我们假定用户在本例中使用OpenGL //当然,也可以指定DirectX 8, 9, 等等. video::E_DRIVER_TYPE driverType = video::EDT_OPENGL; //创建设备,如果创建失败立即退出。 IrrlichtDevice *device = createDevice(driverType, core::dimension2d(640, 480), 16, false, true); if (device == 0) return 1; video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager();
|
我对从这个导入文件产生的发射光线颜色的效果并不满意。下列代码显示怎样实现这些步骤: scene::IAnimatedMesh* mesh = smgr->getMesh("room.3ds"); smgr->getMeshManipulator()->makePlanarTextureMapping( mesh->getMesh(0), 0.008f); scene::ISceneNode* node = 0; node = smgr->addAnimatedMeshSceneNode(mesh); node->setMaterialTexture(0, driver->getTexture("wall.jpg")); node->getMaterial(0).EmissiveColor.set(0,0,0,0); | 四、 水动画 你将添加的第一个特殊的效果是水动画。为此,WaterSurfaceSceneNode导入一个造型文件并使之象水表面一样地波动。如果你让这个场景结点使用一种相当好的材质如MT_REFLECTION_2_LAYER,那么它看起来相当酷: mesh = smgr->addHillPlaneMesh("myHill", core::dimension2d(20,20), core::dimension2d(40,40), 0, 0, core::dimension2d(0,0), core::dimension2d(10,10)); node = smgr->addWaterSurfaceSceneNode(mesh->getMesh(0),3,300,30); node->setPosition(core::vector3df(0,7,0)); node->setMaterialTexture(0,driver->getTexture("water.jpg")); node->setMaterialTexture(1,driver->getTexture("stones.jpg")); node->setMaterialType(video::EMT_REFLECTION_2_LAYER); |
作为输入造型,你可以创建一个陡峭的平面造型,但是你也可以为此使用任何其它的造型。你甚至能重用room.3ds输入文件(它看上去确实很奇怪)。该实例还用一个普通的石头纹理模型来绘制所有另外的表面。 五、透明的告示板和灯光 第二个特殊的效果是很基本的但是非常有用:一个透明的告示板,伴之有一个动态的灯光。为产生这种效果,你只需要产生一个灯光场景结点,并让它四处飞行;而且,为了让它看起来更酷一些,可以把一个告示板场景结点依附到它上面: //创建灯光 node = smgr->addLightSceneNode(0, core::vector3df(0,0,0), video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 600.0f); scene::ISceneNodeAnimator* anim = 0; anim = smgr->createFlyCircleAnimator(core::vector3df(0,150,0),250.0f); node->addAnimator(anim); anim->drop(); // 把告示板依附到灯光 node = smgr->addBillboardSceneNode(node, core::dimension2d(50, 50)); node->setMaterialFlag(video::EMF_LIGHTING, false); node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); node->setMaterialTexture(0,driver->getTexture("particlewhite.bmp")); |
六、 粒子系统
下面介绍的这个特别效果更有趣:一个粒子系统。在Irrlicht引擎中,粒子系统既是组件化的,也是可扩展的,但是仍然易于使用。你只需要简单地把粒子发射器放到一个粒子系统场景结点,这样以来粒子看上去没有产生源。这些发射器可以据需要进行灵活配置,并经常带有许多参数,如粒子方向,粒子数量,以及粒子颜色等。
当然,发射器类型有区别(例如,一个点发射器能够使粒子从一个固定的点上发出粒子)。如果该引擎提供的粒子发射器还不能满足你的要求,你可以容易地创建你自己的发射器。这只需简单地从IParticleEmitter接口派生一个新类并使用setEmitter()方法把它依附到粒子系统上去即可。
下一个实例将创建一个盒子粒子发射器。你可能已经猜出,它从一个跳跃的盒中随机生成粒子。由参数来定义盒子,粒子的方向,每秒产生粒子的最小和最大数目,颜色以及粒子的最小和最大生命周期。
一个完全由发射器组成的粒子系统将是令人生厌的,因为缺乏真实感。因此,Irrlicht支持粒子影响器-它负责在粒子到处飞扬时予以修整。一旦添加到粒子系统上,它们就能模仿另外的更真实的效果,象重力或风。在本例中的粒子影响器只是简单地修改粒子的颜色来产生一种淡出效果。
可能你已经猜出,粒子影响器是通过派生IParticleAffector接口实现的,然后通过使用addAffector()方法把它添加到粒子系统上去。在你为该粒子系统设置了一种好看的材质后,你就有了一个看上去相当酷的野外宿营火的效果。通过调整材质,纹理,粒子发射器,还有影响器参数,你能容易地创建烟雾,下雨,爆炸,下雪等效果:
scene::IParticleSystemSceneNode* ps = 0; ps = smgr->addParticleSystemSceneNode(false); ps->setPosition(core::vector3df(-70,60,40)); ps->setScale(core::vector3df(2,2,2)); ps->setParticleSize(core::dimension2d(20.0f, 10.0f)); scene::IParticleEmitter* em = ps->createBoxEmitter( core::aabbox3d(-7,0,-7,7,1,7), core::vector3df(0.0f,0.03f,0.0f), 80,100, video::SColor(0,255,255,255), video::SColor(0,255,255,255), 800,2000); ps->setEmitter(em); em->drop(); scene::IParticleAffector* paf =ps->createFadeOutParticleAffector(); ps->addAffector(paf); paf->drop(); ps->setMaterialFlag(video::EMF_LIGHTING, false); ps->setMaterialTexture(0, driver->getTexture,"particle.bmp")); ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA); | 七、 影子投射 最后但也不容忽视一个问题是,你需要为一个动画角色产生一个动态的影子。为此,你装载一个Quake2.md2模型文件并把它放到你的world上去。为了创建影子,你只需要调用方法addShadowVolumeSceneNode()。你可能通过调用ISceneManager::setShadowColor()来控制影子的颜色;注意,这仅是全局可调整的,并影响所有的影子。好,下面就是你的产生动态影子效果的代码: mesh = smgr->getMesh("../../media/faerie.md2"); scene::IAnimatedMeshSceneNode* anode = 0; anode = smgr->addAnimatedMeshSceneNode(mesh); anode->setPosition(core::vector3df(-50,45,-60)); anode->setMD2Animation(scene::EMAT_STAND); anode->setMaterialTexture(0, driver->getTexture("../../media/Faerie5.BMP")); anode->addShadowVolumeSceneNode(); smgr->setShadowColor(video::SColor(220,0,0,0)); | 八、 游戏循环 最后,你能进入由device->run()方法控制的游戏循环。该循环将不断运行,直到通过获取一个关闭窗口事件(例如在Windows操作系统下的ALT-F4击键)来退出设备。你必须在一个beginScene()和endScene()命令对之间绘制每样东西。beginScene()用指定的一种颜色清屏,如果需要的话,可以同时清除深度缓冲区。然后你就可以让场景管理器和GUI环境来绘制它们的内容。随着调用endScene(),每一样东西都被绘制到屏幕上去。在本例中,你还可以动态地在标题栏上显示帧每秒(FPS)数,这对于严肃的游戏开发者是十分重要的事情: scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(); camera->setPosition(core::vector3df(-50,50,-150)); int lastFPS = -1; while(device->run()) { driver->beginScene(true, true, 0); smgr->drawAll(); driver->endScene(); int fps = driver->getFPS(); if (lastFPS != fps) { core::stringw str = L"Campfire FX example ["; str += driver->getName(); str += "] FPS:"; str += fps; device->setWindowCaption(str.c_str()); lastFPS = fps; } } device->drop(); |
结束循环后,你必须删除先前用createDevice()方法创建的Irrlicht设备。通过使用Irrlicht引擎,你应该删除所有你用以’create’开头的方法或函数创建的所有对象。你可以通过简单地调用device->drop()来删除该设备对象。 九、你可能喜欢的Irrlicht插件 正如在前面所介绍的,Irrlicht有一群勤奋的独立开发人员并为之产生了大量的插件,也用之开发了相当多的游戏。这些开发者中提出的许多的改进被再次集成到Irrlicht的随后的发行版本中。下面我列举其中的几个例子,我想这会吸引许多颇有前程的开发者感兴趣: · OCTTools,是一套用于Irrlicht的工具,由Murphy McCauley所创建,用于操作OCT文件相关的:输出器,加载器,甚至更多。 · ICE(Irrlicht通用引擎)是一个开发框架,它提供了一个工程的轮廓实现,从而加快了新工程的开发。 · MIM,由Murphy McCauley所创建,是一个非常有用的基于XML的文件格式,可用于Irrlicht的加载器,转换器及其各种工具。 · My3D是一个开发工具包,它能够使你把来自于各种3D包(3DStudio MAX,Giles,等等)中的灯光贴图场景直接输出到Irrlicht中。 · Dusty引擎允许程序员创建"任务"-这些"任务"可以完成程序员想做的任何事情。之后,这些任务被添加到一棵普通的任务树上去,而每个任务可以有它们希望数目的孩子任务。任务"组"允许游戏设计者在一棵完整的树上执行普通的操作,例如暂停,继续或破坏等。 · Irrlicht RPG(Erring Light)是一个3D 绕行走游戏引擎,最初是针对RPG类游戏开发的。 · 2D 图像和精灵类组成了一个很有用的库,它扩展了Irrlicht的2D能力。 · Zenprogramming站点,提供第一个针对Irrlicht的非正式的外部地形生成器,此处也提供很多相关的教程。 摘要: 一、 简介 ClanLib是一个主要针对游戏开发者的跨平台C++框架。尽管API主要为游戏开发设计,你照样可以容易地使用ClanLib来开发一个科学的3D可视化工具或多媒体应用程序(例如Gecko多媒体系统)。 ClanLib拥有各种API-2D和3D图形,声音,网络,I/O,输入, GUI以及资源管理。它还提供透明的OpenGL支持,因此你可以使用本机OpenGL命令而让ClanLib处理依赖于操作系统的窗口管理和其它一切事情。ClanLib通过DirectX或简单的Direct Media Layer(一平台独立的多 此处阅读全文
2006年01月10日
编程修养(二) 6、if 语句对出错的处理 ——————————— 我看见你说了,这有什么好说的。还是先看一段程序代码吧。 if ( ch >= '0' && ch <= '9' ){ /* 正常处理代码 */ }else{ /* 输出错误信息 */ printf("error ......\n"); return ( FALSE ); }
这种结构很不好,特别是如果“正常处理代码”很长时,对于这种情况,最好不要用else。先判断错误,如:
if ( ch < '0' || ch > '9' ){ /* 输出错误信息 */ printf("error ......\n"); return ( FALSE ); }
/* 正常处理代码 */ ......
这样的结构,不是很清楚吗?突出了错误的条件,让别人在使用你的函数的时候,第一眼就能看到不合法的条件,于是就会更下意识的避免。
7、头文件中的#ifndef —————————— 千万不要忽略了头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:
#ifndef <标识> #define <标识>
...... ......
#endif
<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
#ifndef _STDIO_H_ #define _STDIO_H_
......
#endif
(BTW:预编译有多很有用的功能。你会用预编译吗?)
8、在堆上分配内存 ————————— 可能许多人对内存分配上的“栈 stack”和“堆 heap”还不是很明白。包括一些科班出身的人也不明白这两个概念。我不想过多的说这两个东西。简单的来讲,stack上分配的内存系统自动释放,heap上分配的内存,系统不释放,哪怕程序退出,那一块内存还是在那里。stack一般是静态分配内存,heap上一般是动态分配内存。
由malloc系统函数分配的内存就是从堆上分配内存。从堆上分配的内存一定要自己释放。用free释放,不然就是术语——“内存泄露”(或是“内存漏洞”)—— Memory Leak。于是,系统的可分配内存会随malloc越来越少,直到系统崩溃。还是来看看“栈内存”和“堆内存”的差别吧。
栈内存分配 ————— char* AllocStrFromStack() { char pstr[100]; return pstr; }
堆内存分配 ————— char* AllocStrFromHeap(int len) { char *pstr;
if ( len <= 0 ) return NULL; return ( char* ) malloc( len ); }
对于第一个函数,那块pstr的内存在函数返回时就被系统释放了。于是所返回的char*什么也没有。而对于第二个函数,是从堆上分配内存,所以哪怕是程序退出时,也不释放,所以第二个函数的返回的内存没有问题,可以被使用。但一定要调用free释放,不然就是Memory Leak!
在堆上分配内存很容易造成内存泄漏,这是C/C++的最大的“克星”,如果你的程序要稳定,那么就不要出现Memory Leak。所以,我还是要在这里千叮咛万嘱付,在使用malloc系统函数(包括calloc,realloc)时千万要小心。
记得有一个UNIX上的服务应用程序,大约有几百的C文件编译而成,运行测试良好,等使用时,每隔三个月系统就是down一次,搞得许多人焦头烂额,查不出问题所在。只好,每隔两个月人工手动重启系统一次。出现这种问题就是Memery Leak在做怪了,在C/C++中这种问题总是会发生,所以你一定要小心。一个Rational的检测工作——Purify,可以帮你测试你的程序有没有内存泄漏。
我保证,做过许多C/C++的工程的程序员,都会对malloc或是new有些感冒。当你什么时候在使用malloc和new时,有一种轻度的紧张和惶恐的感觉时,你就具备了这方面的修养了。
对于malloc和free的操作有以下规则:
1) 配对使用,有一个malloc,就应该有一个free。(C++中对应为new和delete) 2) 尽量在同一层上使用,不要像上面那种,malloc在函数中,而free在函数外。最好在同一调用层上使用这两个函数。 3) malloc分配的内存一定要初始化。free后的指针一定要设置为NULL。
注:虽然现在的操作系统(如:UNIX和Win2k/NT)都有进程内存跟踪机制,也就是如果你有没有释放的内存,操作系统会帮你释放。但操作系统依然不会释放你程序中所有产生了Memory Leak的内存,所以,最好还是你自己来做这个工作。(有的时候不知不觉就出现Memory Leak了,而且在几百万行的代码中找无异于海底捞针,Rational有一个工具叫Purify,可能很好的帮你检查程序中的Memory Leak)
9、变量的初始化 ———————— 接上一条,变量一定要被初始化再使用。C/C++编译器在这个方面不会像JAVA一样帮你初始化,这一切都需要你自己来,如果你使用了没有初始化的变量,结果未知。好的程序员从来都会在使用变量前初始化变量的。如:
1) 对malloc分配的内存进行memset清零操作。(可以使用calloc分配一块全零的内存) 2) 对一些栈上分配的struct或数组进行初始化。(最好也是清零)
不过话又说回来了,初始化也会造成系统运行时间有一定的开销,所以,也不要对所有的变量做初始化,这个也没有意义。好的程序员知道哪些变量需要初始化,哪些则不需要。如:以下这种情况,则不需要。
char *pstr; /* 一个字符串 */ pstr = ( char* ) malloc( 50 ); if ( pstr == NULL ) exit(0); strcpy( pstr, "Hello Wrold" );
但如果是下面一种情况,最好进行内存初始化。(指针是一个危险的东西,一定要初始化)
char **pstr; /* 一个字符串数组 */ pstr = ( char** ) malloc( 50 ); if ( pstr == NULL ) exit(0);
/* 让数组中的指针都指向NULL */ memset( pstr, 0, 50*sizeof(char*) );
而对于全局变量,和静态变量,一定要声明时就初始化。因为你不知道它第一次会在哪里被使用。所以使用前初始这些变量是比较不现实的,一定要在声明时就初始化它们。如:
Links *plnk = NULL; /* 对于全局变量plnk初始化为NULL */
10、h和c文件的使用 ————————— H文件和C文件怎么用呢?一般来说,H文件中是declare(声明),C文件中是define(定义)。因为C文件要编译成库文件(Windows下是.obj/.lib,UNIX下是.o/.a),如果别人要使用你的函数,那么就要引用你的H文件,所以,H文件中一般是变量、宏定义、枚举、结构和函数接口的声明,就像一个接口说明文件一样。而C文件则是实现细节。
H文件和C文件最大的用处就是声明和实现分开。这个特性应该是公认的了,但我仍然看到有些人喜欢把函数写在H文件中,这种习惯很不好。(如果是C++话,对于其模板函数,在VC中只有把实现和声明都写在一个文件中,因为VC不支持export关键字)。而且,如果在H文件中写上函数的实现,你还得在makefile中把头文件的依赖关系也加上去,这个就会让你的makefile很不规范。
最后,有一个最需要注意的地方就是:带初始化的全局变量不要放在H文件中!
例如有一个处理错误信息的结构:
char* errmsg[] = { /* 0 */ "No error", /* 1 */ "Open file error", /* 2 */ "Failed in sending/receiving a message", /* 3 */ "Bad arguments", /* 4 */ "Memeroy is not enough", /* 5 */ "Service is down; try later", /* 6 */ "Unknow information", /* 7 */ "A socket operation has failed", /* 8 */ "Permission denied", /* 9 */ "Bad configuration file format", /* 10 */ "Communication time out", ...... ...... };
请不要把这个东西放在头文件中,因为如果你的这个头文件被5个函数库(.lib或是.a)所用到,于是他就被链接在这5个.lib或.a中,而如果你的一个程序用到了这5个函数库中的函数,并且这些函数都用到了这个出错信息数组。那么这份信息将有5个副本存在于你的执行文件中。如果你的这个errmsg很大的话,而且你用到的函数库更多的话,你的执行文件也会变得很大。
正确的写法应该把它写到C文件中,然后在各个需要用到errmsg的C文件头上加上 extern char* errmsg[]; 的外部声明,让编译器在链接时才去管他,这样一来,就只会有一个errmsg存在于执行文件中,而且,这样做很利于封装。
我曾遇到过的最疯狂的事,就是在我的目标文件中,这个errmsg一共有112个副本,执行文件有8M左右。当我把errmsg放到C文件中,并为一千多个C文件加上了extern的声明后,所有的函数库文件尺寸都下降了20%左右,而我的执行文件只有5M了。一下子少了3M啊。
[ 备注 ] ————— 有朋友对我说,这个只是一个特例,因为,如果errmsg在执行文件中存在多个副本时,可以加快程序运行速度,理由是errmsg的多个复本会让系统的内存换页降低,达到效率提升。像我们这里所说的errmsg只有一份,当某函数要用errmsg时,如果内存隔得比较远,会产生换页,反而效率不高。
这个说法不无道理,但是一般而言,对于一个比较大的系统,errmsg是比较大的,所以产生副本导致执行文件尺寸变大,不仅增加了系统装载时间,也会让一个程序在内存中占更多的页面。而对于errmsg这样数据,一般来说,在系统运行时不会经常用到,所以还是产生的内存换页也就不算频繁。权衡之下,还是只有一份errmsg的效率高。即便是像logmsg这样频繁使用的的数据,操作系统的内存调度算法会让这样的频繁使用的页面常驻于内存,所以也就不会出现内存换页问题了。
11、出错信息的处理 ————————— 你会处理出错信息吗?哦,它并不是简单的输出。看下面的示例:
if ( p == NULL ){ printf ( "ERR: The pointer is NULL\n" ); }
告别学生时代的编程吧。这种编程很不利于维护和管理,出错信息或是提示信息,应该统一处理,而不是像上面这样,写成一个“硬编码”。第10条对这方面的处理做了一部分说明。如果要管理错误信息,那就要有以下的处理:
/* 声明出错代码 */ #define ERR_NO_ERROR 0 /* No error */ #define ERR_OPEN_FILE 1 /* Open file error */ #define ERR_SEND_MESG 2 /* sending a message error */ #define ERR_BAD_ARGS 3 /* Bad arguments */ #define ERR_MEM_NONE 4 /* Memeroy is not enough */ #define ERR_SERV_DOWN 5 /* Service down try later */ #define ERR_UNKNOW_INFO 6 /* Unknow information */ #define ERR_SOCKET_ERR 7 /* Socket operation failed */ #define ERR_PERMISSION 8 /* Permission denied */ #define ERR_BAD_formAT 9 /* Bad configuration file */ #define ERR_TIME_OUT 10 /* Communication time out */
/* 声明出错信息 */ char* errmsg[] = { /* 0 */ "No error", /* 1 */ "Open file error", /* 2 */ "Failed in sending/receiving a message", /* 3 */ "Bad arguments", /* 4 */ "Memeroy is not enough", /* 5 */ "Service is down; try later", /* 6 */ "Unknow information", /* 7 */ "A socket operation has failed", /* 8 */ "Permission denied", /* 9 */ "Bad configuration file format", /* 10 */ "Communication time out", };
/* 声明错误代码全局变量 */ long errno = 0;
/* 打印出错信息函数 */ void perror( char* info) { if ( info ){ printf("%s: %s\n", info, errmsg[errno] ); return; }
printf("Error: %s\n", errmsg[errno] ); }
这个基本上是ANSI的错误处理实现细节了,于是当你程序中有错误时你就可以这样处理:
bool CheckPermission( char* userName ) { if ( strcpy(userName, "root") != 0 ){ errno = ERR_PERMISSION_DENIED; return (FALSE); }
... }
main() { ... if (! CheckPermission( username ) ){ perror("main()"); } ... }
一个即有共性,也有个性的错误信息处理,这样做有利同种错误出一样的信息,统一用户界面,而不会因为文件打开失败,A程序员出一个信息,B程序员又出一个信息。而且这样做,非常容易维护。代码也易读。
当然,物极必反,也没有必要把所有的输出都放到errmsg中,抽取比较重要的出错信息或是提示信息是其关键,但即使这样,这也包括了大多数的信息。
12、常用函数和循环语句中的被计算量 ————————————————— 看一下下面这个例子:
for( i=0; i<1000; i++ ){ GetLocalHostName( hostname ); ... }
GetLocalHostName的意思是取得当前计算机名,在循环体中,它会被调用1000次啊。这是多么的没有效率的事啊。应该把这个函数拿到循环体外,这样只调用一次,效率得到了很大的提高。虽然,我们的编译器会进行优化,会把循环体内的不变的东西拿到循环外面,但是,你相信所有编译器会知道哪些是不变的吗?我觉得编译器不可靠。最好还是自己动手吧。
同样,对于常用函数中的不变量,如:
GetLocalHostName(char* name) { char funcName[] = "GetLocalHostName";
sys_log( "%s begin......", funcName ); ... sys_log( "%s end......", funcName ); }
如果这是一个经常调用的函数,每次调用时都要对funcName进行分配内存,这个开销很大啊。把这个变量声明成static吧,当函数再次被调用时,就会省去了分配内存的开销,执行效率也很好。
13、函数名和变量名的命名 ———————————— 我看到许多程序对变量名和函数名的取名很草率,特别是变量名,什么a,b,c,aa,bb,cc,还有什么flag1,flag2, cnt1, cnt2,这同样是一种没有“修养”的行为。即便加上好的注释。好的变量名或是函数名,我认为应该有以下的规则:
1) 直观并且可以拼读,可望文知意,不必“解码”。 2) 名字的长度应该即要最短的长度,也要能最大限度的表达其含义。 3) 不要全部大写,也不要全部小写,应该大小写都有,如:GetLocalHostName 或是 UserAccount。 4) 可以简写,但简写得要让人明白,如:ErrorCode -> ErrCode, ServerListener -> ServLisner,UserAccount -> UsrAcct 等。 5) 为了避免全局函数和变量名字冲突,可以加上一些前缀,一般以模块简称做为前缀。 6) 全局变量统一加一个前缀或是后缀,让人一看到这个变量就知道是全局的。 7) 用匈牙利命名法命名函数参数,局部变量。但还是要坚持“望文生意”的原则。 8) 与标准库(如:STL)或开发库(如:MFC)的命名风格保持一致。
摘要:编程修养(一) 什么是好的程序员?是不是懂得很多技术细节?还是懂底层编程?还是编程速度比较快?我觉得都不是。对于一些技术细节来说和底层的技术,只要看帮助,查资料就能找到,对于速度快,只要编得多也就熟能生巧了。 我认为好的程序员应该有以下几方面的素质: 1、有专研精神,勤学善问、举一反三。 2、积极向上的态度,有创造性思维。 3、与人积极交流沟通的能力,有团队精神。 4、谦虚谨慎,戒骄戒燥。 5、写出的代码质量高。包括:代码的稳定、易读、规范、易维护、专业。 这些都是程序员的修养,这里我想谈谈“编程修养”,也就是上述中的第5点。我觉得,如果我要了解一个作者,我会看他所写的小说,如果我要了解一个画家,我会看他所画的图画,如果我要了解一个工人,我会看他所做出来的产品,同样,如果我要了解一个程序员,我想首先我最想看的就是他的程序代码,程序代码可以看出一个程序员的素质和修养,程序就像一个作品,有素质有修养的程序员的作品必然是一图精美的图画,一首美妙的歌曲,一本赏心 (全文共7661字)——点击 此处阅读全文
2005年12月30日
摘要:除开五大这种要什么有什么,进去做打字也能光耀门楣的不谈。如果是嫁到一个普通软件公司,怎样的环境才能最快的成长呢? 首先基本的公司项目管理水平是必要的;其次是稳健而不保守的公司技术选型; 还有一班能沟通的同事。 更重要的,是要有一个严苛的环境,那些古训说的都没错,越是严苛,成长越快。 然后有些引导与助力,让你在严寒中节省体力,过得好些的,就是最佳的成长环境了。 一,最复杂多变的业务规则,最难侍候的客户
大多数人碰上这两样东西都是愁眉苦脸,天天抱怨。其实应该把它视为入门锻炼的: 第一阶是努力用细密的编码迎合复杂的业务,以金牌服务的 (全文共2748字)——点击 此处阅读全文
2005年12月12日
摘要: “CG”原为“Computer Graphics”的英文缩写。简单地说就是利用计算机进行视觉设计和绘画。包括有CG艺术与设计、游戏(Game)软件、动画(Animation)、漫画(Comic)的创作,创作手法有计算机绘画、平面印刷品的设计、网页设计、三维动画、影视特效、多媒体技术、以计算机辅助设计为主的建筑设计及工业造型设计等。如今随着计算机技术的发展,CG的概念正逐步在扩大,由CG和虚拟真实技术制作的媒体文化,都可以归于CG范畴,它们已经形成一个可观的经济产业,掌握一手高超的“CG”绘画技术,更是令人羡慕。下面就来介绍几个有关CG的网站。 [被屏蔽广告] CG中国
http://www.cgercn.com
CG中国是一家非常专业的CG网站,网站既有精彩的CG设计欣赏,又有详细的CG教程可供学习。在互动教程中有人们教学、理论方法和CG建模,每一个实例都有从图片到文字的详细过程,让你能够从中找到学习CG的技 (全文共2059字)——点击此处阅读全文
2005年12月09日
摘要: 中国软件需要高手,高手,高高手! 为此本人给自己立下成为高手的必备条件如下: 1)立大志,做大事,求大智!即使做不了大事,也要拥有大的志向,大的智慧! 2)学习为主要,要靠自己,各种好书要快快地看,拼命地看! 3)学很多别人怕学的东西,写很多别人不敢写的代码,算法!世界真理:物以稀为贵! 4)要懂得并行学习,如cpu的并行处理,好好挖掘大脑的多功能潜力!俗话说:如果你不能让人生的道路变长,就让它变宽! 5)要融会贯通,边工作边锻炼!要嚣张,只有嚣张才能博得别人的指责,只有拥有别人的指责,你才能更快的发现自己的不足! 6)“欲练神功,必先自宫”!尽量避免一切无意义的活动!要懂得寂寞,懂得忘记金钱,名义,美女,要知道等你成为高手后这些全部不在话下! 总之,在成为软件行业高手的过程中,需不断创新,要有激情,对待生活要始终保持热情,要循序渐进,要持之以恒!! (全文共447字)——点击 此处阅读全文
|