分享
 
 
 

飞机躲避小游戏---是男人就撑100秒的制作

王朝other·作者佚名  2006-03-26
窄屏简体版  字體: |||超大  

飞机躲避小游戏---是男人就撑100秒的制作

EmilMatthew(EmilMatthew@126.com)

摘要:

可以将这个游戏的整体运作看成一个粒子系统,再加上子弹和飞机的碰撞判定即可.简单起见,这里的飞机采用球体.

关键词: 粒子系统,飞机躲避游戏

Parse and show bmp file on windows platform

EmilMatthew(EmilMatthew@126.com)

Abstract:

We could treat this game as a particle system, which only needs the collide detect additionally.

To be easy enough as a sample program, I use one ball to take place of the plane in the game.

Key Words: Particle System, Plane avoids bullets game

1前言:

是男人就撑100秒是一个流行广泛,但又略显BT意味的小游戏。游戏的玩法就是四周不断的有子弹射出,而你的任务就是控制你的飞机不断的躲避,直到被击中,以躲避时间的长短来评定游戏水平的高低。

这个游戏在实现是比较容易的,由于子弹在这里占据了主要地位,所以考虑以子弹为中心,即考虑构建一个粒子系统,来控制子弹的发射,发射方向的计算,以及出界的判定等。至于飞机方面,则只要有控制的部分(事件驱动,事件监听或用循环,要视具体实现环境而定),把两者结合起来,只要加上飞机与子弹间的碰撞检测即可,这里出于演示的目的,简单起见,采用球代替飞机的造型。

2子弹粒子系统的运作流程:

子弹的粒子系统要控制好子弹的发射,发射方向的计算,出界的判定以及碰撞检测.

该粒子系统的总体框架并不困难,这里给出我实现过程中的总体框架:

while(runFlag)

{

For all particles

{

If(current particle is not lived)

{

Init this particle.

}

Else if(current particle is out of the game area)

{

Current particle set to dead.

}

Else

{

Current particle move and show

If(current particle is collided with the plane)

runFlag=flase;

}

}

}

这里要注意一下if….else if 中的条件判定对应的现实意义,即是否会出现实中无意义但在程序中却出现的情况,如果出现的话,这样的BUG将比较难抓出.

比如这里,如果将if及else if 中语中的条件及对应的内容作相应的交换,即:

if(current particle is out of the game area)

{

Current particle set to dead.

}

Else if(current particle is not lived)

{

Init this particle.

}

在第一个if判定中,会将这样一种情况被包括进去:

if(current particle is out of game area&& current is not lived)

此时,将导致第二个判定永远无法到达.

所以,当条件复杂且多的时候,最好是列张真值表,看看所有可能的情况是否都如期的到达该到的判定条件处,避免在程序调试中浪费过多不避要的时间.

3子弹粒子设计细节:

3.1子弹粒子的数据结构及存储方式.

粒子类以一个类的形式进行封装,里面包含了一些基本的物理属性及粒子相关的一些动作(函数)。简要的情况如下:

class SPhy.CSPhyMc extends MovieClip

{

public var v :Number=0;//1 demision Velocity or together Velocity of vx ,vy

public var vx:Number=0;

public var vy:Number=0;

……

public function setLife(lifeValue:Number):Void

{

life=lifeValue;

}

public function getLife():Number

{

return life;

}

public function isLived():Boolean

{

return life==LIVED;

}

… …

}

而在游戏中,这采用一个数组来实现粒子的群落,理由是使用方便而且快速。(当然,出于一种美学上的要求,你可能会选链表,因为它的插入和删除来的比较漂亮和干净,这就取决于你自己的喜好了)

3.2 发射角的计算.

发射角的计算相当于一道简单的高中向量的题目:

已知两点P1(x1,y1),P2(x2,y2),求P1指向P2的单位向量a.

求解:

a) 计算两点间的距离L=sqrt((x1-x2)^2+(y1-y2)^2)

b) 求出P1P2(向量),P1P2=((x2-x1)/L,(y2-y1)/L);

c) 单位化a=P1P2/(Len(P1P2))

而你要做的,只是将子弹看成是P1,你自己的飞机看成是P2,即可,最后还应把起始速度去

乘以所求得的单位向量a=(cos(fi),sin(fi))

Vx=v*cos(fi)

Vy =v*sin(fi)

3.3 碰撞检测

碰撞检测是个广泛而重要的话题,可以从简单到复杂,难度突破主要在计算几何上。这里针对本游戏谈两个:

3.3.1两个圆的碰撞检测,这个不用多说了,只要看两个圆的圆心的距离是否比它们的半径之和来的小就是了.

即圆1有:圆心O1(x1,y1),半径r1

圆2有:圆心O2(x2,y2),半径r2

则它们之间的碰撞检测可以这样来做:

If(Len(O1O2)<=r1+r2)

{

Two circles collide.

}

Else

{

Safe condition.

}

如果在视觉效果要求比较高的场和,尤其是不允许出现物体重叠的场和,不仿在Len(O1O2) 后加上一个偏移值。这样可以保证视觉上不会看到两个物体重叠的现像,尽管在精确的数值模型上二者并未相碰。而在数值精度要求高的场和,恐怕情况就要反一下了,图形是第二位的,数据的精准才是最重要的。具体如何去平衡图形和数据间的对应关系,还请诸位自己去斟酌了。

3.3.1圆和三角形间的碰撞检测:

三角形可以用通常用一个五元组Q(P1,P2,k0,k1,k2)来表达(许多飞机的形状通常可以看成一个三角形)

对于Q(P1,P2,k0,k1,k2),其中,P1,P2是三角的位于上部和左下的两个点,假设另一个点为P3,而k0是P1,P2间的斜率,k1是指P1,P3间的斜率,k2是指P2,P3间的斜率. (各字母的意义见图1)

图1

这样,三角形的三条边就可以方便的表达出来了:

如直线P1P2的二维直线方程为y=k0(x-x1)+y1.

P1P3: y=k1(x-x1)+y1

P2P3: y=k2(x-x2)+y2

这样,判定一个点是否在三解形内,就只要判断这个点是否在三条边指向三角形内的一侧.这里,如果要判的点为p(x’,y’),则根据图1的情况,有:

If(k0(x’-x1)+y1>=0&&k1(x’-x1)+y1<=0&& y’>=y2)//考虑到P2P3是水平的情况

{

Collide!

}

Else

{

Safe Condition.

}

显然,这个算法并不算得上好,因为如果三解形旋转的话,原来的某直线的左侧意味着三角形的内侧可能就会意味着外侧。这时,可以考虑再增加一个三元组,用来实时指示当前的三条直线指向三角形内侧的方面,可取的情况有以下几种:

a)左侧 b)右侧 c)上侧(水平时) d下侧(水平时)

4实现部分的关键代码(AS2)

4.1粒子类:

import SMotion.*

import SColDet.*

class SPhy.CSPhyMc extends MovieClip

{

public var m:Number=0;//mass

public var g:Number=0;//gravity

public var pF:Number=0;//Positive forces,attention here UpCase!!!!!!!

//Because the compiler was not so perfect as you think ,add a p here to prepare for the case.

public var r:Number =0;//when it become a ball---radius.

public var v :Number=0;//1 demision Velocity or together Velocity of vx ,vy

public var vx:Number=0;

public var vy:Number=0;

public var f :Number=0;//fraction forces.

public var fx:Number=0;

public var fy:Number=0;

public var a :Number=0;//acclerate v

public var ax:Number=0;

public var ay:Number=0;

//plane game use;

public var bigFire:Number=0;

private static var DEAD:Number=0;

private static var LIVED:Number=1;

private var life:Number;

private var mMotionCom:RCSMove;

private var mColDetCom:RCSColDet;

private static var thisP:Object;

public function setLife(lifeValue:Number):Void

{

life=lifeValue;

}

public function getLife():Number

{

return life;

}

public function isLived():Boolean

{

return life==LIVED;

}

public function init():Void

{

thisP=this;

this.vx=0;

this.vy=0;

this.v=3+random(3);

this._width=10;

this._height=10;

this.r=5;

this.initCom();

}

public function initPos(targetPlane:CSPhyMc):Void

{

var randNum:Number=random(100);

//set init positoin:down,left,up,right

if(randNum<25)

{

this._x=random(Stage.width);

trace("Width"+Stage.width+"Height"+Stage.height);

this._y=Stage.height;

}

else if(randNum<50)

{

thisP._x=_root.gStageLeft;

this._y=random(Stage.height);

}

else if(randNum<75)

{

this._x=random(Stage.width);

thisP._y=_root.gStageTop;

}

else

{

this._x=Stage.width;

this._y=random(Stage.height);

}

this.CalVx_Vy(this,targetPlane);

}

private function GetDis(mc1:CSPhyMc, mc2:CSPhyMc):Number

{

return Math.sqrt((mc1._x-mc2._x)*(mc1._x-mc2._x)+(mc1._y-mc2._y)*(mc1._y-mc2._y));

}

private function CalVx_Vy(mcChase:CSPhyMc, mcAim:CSPhyMc):Void

{

var len:Number= GetDis(mcChase, mcAim);

mcChase.vx=(mcAim._x-mcChase._x)/len*mcChase.v;

mcChase.vy=(mcAim._y-mcChase._y)/len*mcChase.v;

}

public function initCom():Void

{

mMotionCom=new RCSMove();

mColDetCom=new RCSColDet();

}

public function outDetect():Boolean

{

var offset:Number=25;

return mColDetCom.particleOutDet(this,0-offset,0-offset,Stage.width+2*offset,Stage.height+2*offset);

}

public function move_show():Void

{

mMotionCom.Move2D(this,this.vx,this.vy);

}

public function collideDect(targetPlane:CSPhyMc):Boolean

{

if(_root.mcLibPlaneName=="ball")

return mColDetCom.TwoBall(targetPlane,this);

//return this.hitTest(targetPlane.getBounds(_root).xMin,targetPlane.getBounds(_root).yMax,false);

}

}

4.2游戏主调度类

class ChaseAim

{

static private var thisP:Object;

private var staturs:Number;//gaming 1,failure 0

private var speed:Number;

private var bulletNum:Number=20;

private var start:Number=0;

private var end:Number=0;

public function init():Void

{ thisP=this;

staturs=1;

speed=3;

bulletNum=20;

for(var i=0;i<11;i++)

{

_root.createTextField("txt"+i,i,0,(i-1)*25,500,25);

}

_root.attachMovie("ball","ball1",11);

_root.ball1._x=250;

_root.ball1._y=200;

_root.ball1.r=20;

for(var i=0;i<bulletNum;i++)

{

_root.attachMovie("bullet","bullet"+i,20+i);

_root["bullet"+i].vx=0;

_root["bullet"+i].vy=0;

_root["bullet"+i].v=3+random(3);

_root["bullet"+i].r=5;

_root["bullet"+i]._width=10;

_root["bullet"+i]._height=10;

GenBullet(_root["bullet"+i]);

}

start=getTimer();

setInterval(EffectF,100);

}

private function EffectF():Void

{

if(thisP.staturs!=0)

{

for(var i=0;i<thisP.bulletNum;i++)

{

if (thisP.CheckOutBounds(_root["bullet"+i])) thisP.GenBullet(_root["bullet"+i]);

if(thisP.TwoBallCol(_root.ball1,_root["bullet"+i]))thisP.staturs=0;

thisP.Move2D(_root["bullet"+i]);

//_root.txt3.text=_root["bullet"+i].vx;

//_root.txt4.text=_root["bullet"+i].vy;

}

if( Key.isDown(Key.LEFT))_root.ball1._x -= thisP.speed;

if( Key.isDown(Key.RIGHT))_root.ball1._x += thisP.speed;

if( Key.isDown(Key.UP))_root.ball1._y -= thisP.speed;

if( Key.isDown(Key.DOWN))_root.ball1._y += thisP.speed;

if(thisP.staturs==0)

{

_root.txt0.text="you failure";

thisP.end=getTimer();

var tmp:Number=thisP.end-thisP.start;

_root.txt1.text="你共坚持了"+tmp/1000+"秒";

//delete this.onEnterFrame;

}

}

}

private function GenBullet(tmpMc:CSPhyMc):Void

{ var left:Number;

var top:Number;

if(random(2))

{

left=random(7)*100-100;

top=random(2)*400;

}

else

{

left=random(2)*600;

top=random(6)*100-100;

}

tmpMc._x=left;

tmpMc._y=top;

CalVx_Vy(tmpMc, _root.ball1);

}

private function CheckOutBounds(tmpMc:CSPhyMc):Boolean

{

if(tmpMc._x<-10||tmpMc._x>510||tmpMc._y<-10||tmpMc._y>410)

return true;

else return false;

}

private function TwoBallCol(ball1:CSPhyMc,ball2:CSPhyMc):Boolean

{

if(Math.sqrt((ball1._x-ball2._x)*(ball1._x-ball2._x)+(ball1._y-ball2._y)*(ball1._y-ball2._y))<=(ball1.r+ball2.r))

return true;

else

return false;

}

private function GetDis(mc1:CSPhyMc, mc2:CSPhyMc):Number

{

return Math.sqrt((mc1._x-mc2._x)*(mc1._x-mc2._x)+(mc1._y-mc2._y)*(mc1._y-mc2._y));

}

private function CalVx_Vy(mcChase:CSPhyMc, mcAim:CSPhyMc):Void

{

var len:Number= GetDis(mcChase, mcAim);

mcChase.vx=(mcAim._x-mcChase._x)/len*mcChase.v;

mcChase.vy=(mcAim._y-mcChase._y)/len*mcChase.v;

}

private function Move2D(mc:CSPhyMc):Void

{

mc._x+=mc.vx;

mc._y+=mc.vy;

}

}

4.3核心运行函数:

function mainLoop():Void

{

UserPlaneControl();

if(_root.gRunFlag)

{

//trace("yes");

for(var i:Number=0;i<_root.gBulletNum;i++)

{

//trace("yes");

if(!_root[mcUserBulletName+i].isLived())

{

//trace("relife:"+i);

_root[mcUserBulletName+i].setLife(LIVED);

_root[mcUserBulletName+i].init();

_root[mcUserBulletName+i].initPos(_root[mcUserPlaneName]);

}

else if(_root[mcUserBulletName+i].outDetect())

{

//trace("outDetect:"+i);

_root[mcUserBulletName+i].setLife(DEAD);

}

else

{

_root[mcUserBulletName+i].move_show();

if(_root[mcUserBulletName+i].collideDect(_root[mcUserPlaneName]))

_root.gRunFlag=false;

}

}

}

else

{

var timeCount:Number=0;

//clear the main game scence

clearInterval(_root.gIntervalID);

/*for(var i:Number=0;i<_root.gBulletNum;i++)

_root[mcUserBulletName+i].removeMovieClip();

_root[mcUserPlaneName].removeMovieClip();

*/

_root.gTimeEnd=getTimer();

timeCount=_root.gTimeEnd-_root.gTimeStart;

trace("you last:"+String(timeCount)+"secs.");

trace2("You lasted:"+String(timeCount)/1000+"secs.");

/*start the end mc

_root.gNextScence=0;

_root.gIntervalID=setInterval(showEnd,_root.fps,_root.mcEndName,_root.result);

*/

}

}

5实验结论:

通过该模型,实现了一个粒子系统的基本运作模式,该运作模式同样适用于其它的粒子系统,只要在最关键的运动及显示部分加以变换即可.

参考文献:

[1]王德才,基于Direct3D粒子系统实现喷洒车喷洒特殊效果三维仿真,计算机编程技巧与维护,2005.7,80-82

程序完成日:06/02/15

文章完成日:06/03/25

附录:

1测试程序下载:

http://emilmatthew.51.net/EmilPapers/06_10ChaseGame/code.rar

2DOC文档下载:

http://emilmatthew.51.net/EmilPapers/06_10ChaseGame/doc.rar

若直接点击无法下载(或浏览),请将下载(或浏览)的超链接粘接至浏览器地( 推荐MYIE或GREENBORWSER)址栏后按回车.若不出意外,此时应能下载.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有