前记: 记得Donald Knuth说过:「不管现在流行什麽语言,你都可以肯定十年二十年之後它不再风光。我总是在自己的书中写些不时髦的东西,但这些东西却值得後代子孙记取。」正当我们为微软一轮轮的技术革新而强迫自己努力学习的时候,我们可曾花时间去考虑一些深层次的东西?在游戏可视化的背后,到底是怎样的设计模式和事件引擎来带动整个游戏?BSCH的这篇文章正是试图从这方面入手向大家讲述如何编写一个飞机游戏,毕竟DirectX不等于游戏,希望这篇文章对于掌握了DirectX的朋友们能起一个抛砖引玉的作用,全文如下:
一个“飞机”小游戏,实现起来比较简单,比较适合初学者学习。下面,我将按照自顶而下的方法来说明如何实现一个“飞机”的游戏,首先看一个函数:
int GameStart() { while(GameOver){ clear(); control(); dirver(); show(); if(key[ESC]) GameOver = 0; } return 1; }
|
这个函数的功能应该不难理解,在此用while语句,是用来控制游戏退出的。看看while中最后一个语句 if(key[ESC])GameOver=0;这个语句,就应当理解while语句的功能。所以在游戏过程中,只要想叫程序退出,千万不能忘记加GameOver = 0 ;这个语句。clear()这个语句看名字就知道是清屏。control()这个函数是游戏的核心,控制整个游戏的,比如说控制飞机,子弹如何飞……后面再说这些,现在说的是游戏的整体思路,枝节方面后面再说。dirver()这个函数是控制着敌人的出现。show()函数是显示图形
??所以整个游戏的思路很简单,清屏——游戏控制——敌机出现——显示: 1.clear(); 清屏函数很容易实现,直接把MemBuf全部赋值成0。 2.control(); 首先定义物体结构。 struct TOBJ{ int mode; //类型,0:空闲,1:敌机;2,自己发射的子弹,3,敌人子弹类型1; int x, y; int type; int index; } Objs[MAXOBJ]; 要控制整个游戏的进程,必须每个飞机,子弹都在动,所以用 for(i=0; i<MAXOBJ; i++) 这个循环对每个物体进行控制。 对单个物体来说,首先要确定每个物体的类型,类型不一样,执行的操作也就不一样
switch(Obj[i].mode){ case 1: //如果是敌机,执行敌机的操作; ......; break; case 2: //如果是自己发射的子弹,执行自己的子弹的操作; ......; break; case 3: //如果是敌人子弹类型1,执行子弹类型1的操作; ......; break; case 4: //执行敌人子弹类型2的操作 ......; break; ...... //执行子弹类型N的操作; }
1.1敌机操作: 首先判断敌机是不是已经死亡,如果死亡,则显示死亡的效果,比如说爆炸,这是最常见的飞机游戏中,敌机死亡的效果,还比如说敌机出现闪烁的效果,这种方式倒是也有,不过效果自然没有爆炸的效果华丽,在此我用闪烁的效果。如果敌机没有死亡,则要往我机上撞,以期消灭我机——显然不能任何时候都跟着咱们跑,所以要有一定的几率性,这样,不会时时刻刻跟着咱们。
switch (Objs[i].type) { case 0: if(!random(20)) Objs[i].type=1; break; case 1: if(Objs[i].x<fly_x) Objs[i].x++; if(Objs[i].x>fly_x) Objs[i].x--; if(!random(40)) Objs[i].type=2; break; case 2: default: if(!random(3)) y+=3; break; } 用程序说明比较方便。这段程序是执行敌机的飞行擦作的。Objs[i].type==0是,1/20的几率让敌机跟着自己飞,此时执行case 1。然后1/40的几率使敌机加速,此时执行case 2:default:; 敌机的飞行任务完成,还必须能发射子弹,一样,用随机函数,比方说: if (!random(30){ ?// 1/30的几率发射子弹 ??j=AllocObj();?// 从Objs中选择一个,也就是空闲的 ??if(!random(5)) Objs[j].mode=2; // 1/5的几率,是子弹类型2; ??else Objs[j].mode=3; ??Objs[j].x=Objs[i].x; ??Objs[j].y=Objs[i].y; }
最后这两句很容易理解,子弹发射的坐标,自然要从发射它的敌机那里发射,所以坐标一样。然后是判断敌机和我机相撞没有if(CheckHit(x,y,fly_x,fly_y,15)) fly_flag=0;CheckHit是碰撞函数,和上面的AllocObj()函数一起在后面给出实现过程。这只是敌机未死亡所执行的操作如果敌机死亡,则要出现死亡效果 if(Objs[i].index){ ?if ((Objs[i].index&3)==0) 画敌机图片 ?if(++Objs[i].index>50) Objs[i].mode=0; } 这段程序很容易理解也就是过段时间,显示次敌机的图片。如果是不显示的话,在前面的主循环中,已经被clear()函数给清屏了。敌机的操作还有最后一步if(Objs[i].y>MAXY) Objs[i].mode=0;如果敌机飞到屏幕下方,则释放函数AllocObj()的实现 int AllocObj() { ?int i = 0; ?while(i<MAXOBJ&&Objs[i].mode) i++; ?if(i>=MAXOBJ) i = MAXOBJ - 1; ?return i; } 函数CheckHit()的实现这个函数通过矩形判断 int CheckHit(int x1,int y1,int x2,int y2,int r) { ?if(abs(x1-x2)<=r&&abs(y1-y2)<=r) return 1; ?return 0; }
1.2我方子弹 先让子弹往上飞Objs[i].y-=3;然后判断是否射中敌人。 for (j=0;j<MAX_OBJ;j++) // 检查与敌机相碰 ?if(Objs[j].mode==1&&CheckHit(x,y,Objs[j].x,Objs[j].y,15)&&!Objs[j].index){ ?Objs[j].index=1; ?Objs[i].mode=0; } 显然,要判断是否射中敌人,要首先扫描所有的敌人,看看是否射中, Objs[i].mode = 0 ??回收此子弹 Objs[j].index = 0; 代表敌机存活 Objs[j].index!=0;??代表敌机死亡,之所以不用Objs[j].mode=0代表敌人死亡,是因为还要出现死亡后的效果,你完全可以用Objs[j].mode=0代表敌机死亡,不过这样子就没有死亡后效果了,射中敌人,敌人就消失,我想你不会要这样的游戏吧:) 1.3敌方子弹 Objs[i].y+=3; Objs[i].x+=random(10)-5; if(CheckHit(x,y,fly_x,fly_y,10)) //敌机子弹射中我机 fly_flag=0; 这段程序最精妙出是x+=random(10)-5;这个语句,它在原横坐标的位置,往左右5幅度的概率相等,随机,因此造成这个子弹很阴险(很难躲避的,不信你自己玩玩看,反正我所见过的人中,打此游戏很少超过5分钟,主要就是这种子弹,根本没有规律性:)) 1.4地方别的子弹,你自己设计 我再给种子弹的设计思路x += -Objs[i].k;ABS(Objs[i].k)<=1 ? (y += 4) : (y+=2) ;putimage(x, y, 5);if(CheckHit(x,y,fly_x,fly_y,8)) fly_flag=0;if(y > MAXM) Objs[i].mode = 0;这种子弹是最常见的。Objs[i].k代表斜率......所有的物体的操作完毕了,剩下的该是我机的操作了 ?if(key[KEY_UP]) if (--fly_y<0) fly_y=0; ?if(key[KEY_DOWN]) if (++fly_y>MAXM-16) fly_y=MAXM-16; ?if(key[KEY_LEFT]) if (--fly_x<0) fly_x=0; ?if(key[KEY_RIGHT]) if (++fly_x>MAXX-16) fly_x=MAXX-16; ?if(key[KEY_CONTROL]){ ?i=AllocObj(); ?Objs[i].x=fly_x, ?Objs[i].y=fly_y-10; ?Objs[i].mode=2; } 画出我机的图片if(!fly_flag) GameOver = 1;在此,整个程序的思路应该很清晰了 2.0下面设计dirver()函数 void drivers() { ?if(!random(20)){ ??int i=AllocObj(); ??Objs[i].x=random(MAXX); ??Objs[i].y=-random(60); ??Objs[i].mode=1; ??Objs[i].type=Objs[i].index=0; ??if(!random(120)) Objs[i].type=2; ??//直接出现的敌机的速度就很快 ?} } 好了,在此整个游戏的核心就已经设计完了,下面我把所有的组合在一起给出
//////////////////////////////核心//////////////////////////////////
#define MAXOBJ 物体最大数量 #define MAXX 屏幕宽度 #define MAXY 屏幕高 struct TOBJ{ int mode; int x, y; int index; int type; }Objs[MAXOBJ];
int AllocObj() { int i=0; while(i<MAXOBJ&&Objs[i].mode) i++; if(i>=MAXOBJ) return MAXOBJ - 1; return i; }
int CheckHit(int x1, int y1, int x2, int y2, int r) { if(abs(x1-x2)<=r&&abs(y1-y2)<=r) return 1; return 0; }
void drivers() { if(!random(20)){ int i=AllocObj(); Objs[i].x=random(MAXX); Objs[i].y=-random(60); Objs[i].mode=1; Objs[i].type=Objs[i].index=0; if(!random(120)) Objs[i].type=2; //直接出现的敌机的速度就很快
} }
int fly_x=MAXX/2, fly_y=MAXY*4/5, fire_flag=0, fly_flag=1; int GameOver=1; char GameOver() { while(GameOver){ clear(); control(); dirvers(); show(); if(key[ESC]) GameOver = 0; } return 1; } void control() { int i, j; int x, y; for(i=0; i<MAXOBJ; i++){ x=Objs[i].x; y=Objs[i].y; switch(Objs[i].mode){ case 1: if(!Objs[i].index){ switch(Objs[i].type){ case 0: if(!random(3)) Objs[i].type = 1; break; case 1: if(x<fly_x) x++; if(x>fly_x) x--; if(!random(40)) Objs[i].type = 2; break; case 2: default: if(!random(3)) y+=3; }//end switch(Objs[i].type);
if(Check(x, y, fly_x, fly_y, 15) fly_flag=0;// 画出敌机;
if(!random(10)){ j=AllocObj(); if(!random(3)) Objs[j].mode=3; else{ Objs[j].mode = 4; Objs[j].k = (fly_y - y)* 1.0 / (fly_x-x); } Objs[j].x=x; Objs[j].y=y; } }//end if(!Objs[i].index)
else{ if((Objs[i].index&3)==0) 画出敌机; if(++Objs[i].index>50) Objs[i].mode=0; }end if(!Objs[i].index)) y++; if(y>MAXY) Objs[i].mode=0; break; case 2: y-=3; 画出我方子弹; for(j=0; j<MAXOBJ; j++) if((Objs[j].mode==1)&&(!Objs[j].index)&&CheckHit(x, y,Objs[j].x, Objs[j].y, 15)){ Objs[j].index = 1; Objs[i].mode = 0; } break; case 3: y+=3; x+=random(20)-10; if(y>MAXY) Objs[i].mode=0; 画出子弹; if(Check(x, y, fly_x, fly_y, 15)) fly_flag=0; break; case 4: x += -Objs[i].k; abs(Objs[i].k)<=1?(y += 4):(y+=2) ; 画出子弹; if(CheckHit(x,y,fly_x,fly_y,8)) fly_flag=0; break; }//end switch(Objs[i].mode);
}//end for
if(key[UP]) if(fly_y--<0) fly_y=0; if(key[DOWN]) if(fly_y++>MAXY) fly_y=MAXY; if(key[LEFT]) if(fly_x--<0) fly_x=0; if(key[RIGHT]) if(fly_x++>MAXX) fly_x=MAXX; if(key[CTRL]){ i=AllocObj(); Objs[i].x=fly_x, Objs[i].y=fly_y-10; Objs[i].mode=2; } if(!fly_flag) GameOver=0; }
| |