射击游戏中弹幕的开发
http://blog.vckbase.com/knight/archive/2005/03/29/4174.html
作者:牛阿牛
nhf_2003@hotmail.com
email:nhf20021166@163.com
留言:希望能给各位游戏开发一点的介绍;如有问题,请联系.
游戏射击中, boss可能是最重要的设计点!
技术角度:弹幕的组合,肢体的配合,动作的衔接.
艺术方面:绚丽的弹幕,新颖的运动,敏捷的反应.
此处将重点放在boss的弹幕及其组合方面进行阐述.
追逐玩家的火龙,挥动的双钳,伸缩的双臂,总能给玩家更多新鲜感,更多的满足感!当然组合起来,就可能有更好的效果!
商家追求的是利润,策划追求的是艺术,玩家追求是满足!
我们是要将三位上帝的要求用技术来实现!
下面我们考虑一种实现方法
如果是强悍的boss,其弹幕一定很绚丽而又凶险,当然多变性是更重要的…
此处首先说明的是,接近0度或90度的正弦余弦方向步长处理,本处采取,增加子弹的移动距离属性,这样我们就可以根据总移动长度来求x,y方向的移动,不会出现,x或y轴因为步长小而每次都为零的情况..
我们此处也不再考虑,怎样加发射一个子弹,其实就是在链中添加一个子弹…,爆炸或者跑出屏幕,从链表中删除,链表操作,请参考链表处理一文….
子弹的数据结构:
typedef struct _CBullet ///暂作
{
Sprite stSprite;
int nDirx;
int nDiry;
DIR m_nDir; 方向,8个方向….
Point pDest; //目标点x,y的结构体
Point pSour; //原始点
BOOL bExplore;
OBJSIZE m_nSize; //可以分为多个类型的.
WEAPON m_nWeapon; ///武器类型了.武器的类型,主要在方向及威力,性能上起作用…
BYTE m_nKillPow; ///杀伤力
BYTE nIndex; ///某些状态的公用状态值
BYTE nMoveDistance; //移动距离
BYTE nAngle; //角度
Explore MeExplore; ///暂不考虑
Explore MapExplore; //暂不考虑
PLAYERSTATUS m_nStatus; // 子弹状态
}BULLET;
通过数学函数处理:
对每个子弹设定运行函数即可…
///通过定时处理
1) 扇型弹幕的,可以设置速度,形成快慢两种了.
处理时定义临时变量,然后循环一定数目,每次添加子弹,当然根据方向,根据要求设置速度,,,这样就可形成扇型弹幕….
2) 螺旋型弹幕,如果每次添加的子弹时,闪开一定小角度,就可以形成块状扇型的….
通过boss里面的索引状态,然后每次重新设定起始角度,作扇型弹幕,即可,在多次定时器时即可实现螺旋型弹幕
3) 烟花型:原理类似,只要每次添加时,以公用索引角度添加,同时添加垂直和同线异向的子弹,定时器宣传,索引值变化,即可..
4) 鞭子型,,原理类似,每次只添加两个子弹,上下或者左右对称,在定时器中,修改公用索引值,在多次定时处理后,即实现鞭子型的子弹
5) 束型子弹:原理是,每次根据目标位置,确定一个角度范围,然后生成3或4(也 随机也行)个角度范围内的子弹,定时器处理后既成为属性弹幕
6) 加激光,加敌人,就是很简单的了…..
7) 然后对上面的子弹搞一些组合,就会更加丰富
boss的弹幕有间歇,也有组合,这就要求对boss的状态进行处理
typedef enum _FIRESTATUS
{
FIRESTATUS_PREPARE = 0, //准备阶段
FIRESTATUS_FIRING = 1, //射击中
FIRESTATUS_END = 2 //休闲中.
}FIRESTATUS;
至于弹幕的组合
typedef enum _FIRESTYLE
{
FIRE_CIRCLEFAST = 0, //半圆形快速
FIRE_CIRCLESLOW = 1 , //半圆形慢速.
FIRE_CIRCLEBLOCK = 2 , //半园行块状.
FIRE_BOUND = 3 , //束状问题了.
FIRE_BLIM = 4 , //辫子行的.
FIRE_FLAME = 5 , //烟花型 的.
FIRE_LASER = 6, //激光
FIRE_ENEMY = 7, //加敌人,主要指小潜艇了.
FIRE_BLOCK = 8, //块状三乱行的.
FIRE_CIRCLEFASTSLOW = 9 ,//半圆形快慢型的.
FIRE_CIRCLEBOUND = 10 , //半圆形夹杂 束型的.
FIRE_CIRCLELASER = 11 ,// 半圆形夹杂激光
FIRE_CIRCLEENEMY = 12 , // 半圆形夹杂敌人的.
FIRE_BLIMBOUND =13 ,// 辫子型夹杂 束型的.
FIRE_BLIMLASER = 14 ,// 辫子型夹杂激光
FIRE_BLIMENEMY = 15 , // 辫子型夹杂敌人的.
FIRE_FLAMEBOUND = 16 , //烟花型夹杂束型的.
FIRE_FLAMELASER = 17, //烟花型夹杂激光的
FIRE_FLAMEENEMY = 18 , //烟花型夹杂敌人
}FIRESTYLE;
///boss的射击,将根据状态,每个状态持续多少个周期,可以固定,也可以调节
这里的每一个细节都要细分处理,省略不得,什么部位发射什么弹幕,
不同的部位弹幕组合,请注意难度可以调节,保证高手可以闯过…
if((pPart->bExist ==FALSE)||(pPart->bDestroy==TRUE)||(pPart->bFire==FALSE))
{
return TRUE;
}
else
{
///
if(objSize==OBJ_LARGE)
{
x = pMe->gBigBoss.BodyRect.x + pPart->BodyRect.x+ (pPart->BodyRect.dx>>2);
y = pMe->gBigBoss.BodyRect.y + pPart->BodyRect.y+ (pPart->BodyRect.dy>>1);
}
else
{
x = pMe->gSmallBoss.BodyRect.x + pPart->BodyRect.x+ (pPart->BodyRect.dx>>2);
y = pMe->gSmallBoss.BodyRect.y + pPart->BodyRect.y+ (pPart->BodyRect.dy>>1);
}
//
if(pPart->firestatus == FIRESTATUS_FIRING )
{
//进行分情况处理了.ok?
switch(pPart->firestyle)
{
case FIRE_CIRCLESLOW:
//if()
AddLeftCirCleEnemyBullet(pMe,x,y,75,0,30,1,Bullet_Speed_Slow,FALSE);
break;
case FIRE_LASER:
AddEnemyLaserBullet(pMe,x,y,3);
break;
case FIRE_BOUND: //
{
nDir =GetDir(pMe,x,y);
nAngle =Angle(pMe,x,y);
AddBoundEnemyBullet(pMe,x,y,nDir,nAngle,1,7,0,Bullet_Speed_Slow);
}
break;
case FIRE_CIRCLEFAST:
AddLeftCirCleEnemyBullet(pMe,x,y,75,0,30,1,Bullet_Speed_Med,FALSE);
break;
case FIRE_BLIM:
AddLimbEnemyBullet(pMe,x,y,(BYTE)(pPart->fireIndex<<1) ,2,Bullet_Speed_Slow);
break;
case FIRE_FLAME:
AddFireEnemyBullet(pMe,x,y,(BYTE)(pPart->fireIndex) ,0, Bullet_Speed_Small);
break;
case FIRE_ENEMY:
BossAddEnemy(pMe,x,y,OBJ_SMALLSUBMARINE,OBJ_SMALL);
break;
case FIRE_BLOCK: ///
GETRAND(&rand,sizeof(unsigned char));
if(rand&0x04)
{
AddLeftCirCleEnemyBullet(pMe,x,y,75,0,15,1,Bullet_Speed_Slow,FALSE);
}
else
{
AddLeftCirCleEnemyBullet(pMe,x,y,75,0,30,1,Bullet_Speed_Slow,FALSE);
}
break;
case FIRE_CIRCLEFASTSLOW:
if(pPart->fireIndex &0x0008)
{
AddLeftCirCleEnemyBullet(pMe,x,y,75,0,45,1,Bullet_Speed_Med,FALSE);
}
else
{
AddLeftCirCleEnemyBullet(pMe,x,y,75,0,30,1,Bullet_Speed_Slow,FALSE);
}
break;
case FIRE_BLIMLASER:
/*
if(pPart->fireIndex &0x0008)
{
AddEnemyLaserBullet(pMe,x,y,3);
}
else
{
*/
AddLimbEnemyBullet(pMe,x,y,(BYTE)(pPart->fireIndex<<1) ,2,Bullet_Speed_Slow);
//}
break;
case FIRE_CIRCLEBLOCK:
AddLeftCirCleEnemyBullet(pMe,x,y,75,0,30,1,Bullet_Speed_Slow,TRUE);
break;
case FIRE_FLAMEBOUND:
if(pPart->fireIndex &0x0008)
{
AddEnemyLaserBullet(pMe,x,y,3);
}
else//
{
AddFireEnemyBullet(pMe,x,y,(BYTE)(pPart->fireIndex) ,0, Bullet_Speed_Small);
}
break;
}
}
///具体的每一种弹幕添加原理,前面已经有了
/添加左向半圆子弹-包括块状
BOOL AddLeftCirCleEnemyBullet(FPSApp * pMe,int x,int y,BYTE nEnd,BYTE nStart,BYTE nStep,BYTE nIndex,BYTE nSpeed,BOOL bBlock)
{
int nBegin = 0;
BOOL bNormal = TRUE;///
nBegin = nEnd;
if(!bBlock)
{
while(nBegin >= nStart )
{
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_LEFTUP,Cos(nBegin),Sin(nBegin),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal=FALSE;
break;
}
nBegin -= nStep;
}
//
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_LEFTUP,Cos(0),Sin(0),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal=FALSE;
}
nBegin = nEnd;
while(nBegin >=nStart )
{
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_LEFTDOWN,Cos(nBegin),Sin(nBegin),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal=FALSE;
break;
}
nBegin -= nStep;
}
}
else /默认数据,步长2,数量4
{
while(nBegin >= nStart )
{
if(!AddBlockEnemyBullet(pMe,x,y,DIR_LEFTUP,(BYTE)nBegin,2,4,nIndex,nSpeed))
{
bNormal=FALSE;
break;
}
nBegin -= nStep;
}
//
if(!AddBlockEnemyBullet(pMe,x,y,DIR_LEFTUP,0,2,4,nIndex,nSpeed))
{
bNormal=FALSE;
}
nBegin = nEnd;
while(nBegin >=nStart )
{
if(!AddBlockEnemyBullet(pMe,x,y,DIR_LEFTUP,(BYTE)nBegin,2,4,nIndex,nSpeed))
{
bNormal=FALSE;
break;
}
nBegin -= nStep;
}
}
return bNormal;
}
//添加块状子弹,不是半圆,只是其中的一个单元
BOOL AddBlockEnemyBullet(FPSApp * pMe,int x,int y,DIR nDir,BYTE nStart,BYTE nStep,BYTE nCount,BYTE nIndex,BYTE nSpeed)
{
//switch
BOOL bNormal = TRUE;///
BYTE nNum = 0;
BYTE nBegin = nStart;
BOOL bChangeDir = FALSE;
DIR nRealDir = nDir; /
while(nNum<nCount)
{
/
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,nRealDir,Cos(nBegin),Sin(nBegin),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal=FALSE;
break;
}
if(nDir==DIR_LEFTUP)
{
if(nBegin+ nStep>=90)
{
break;
}
else
nBegin+=nStep;
}
else
{
if(bChangeDir==TRUE)
{
///
nBegin+=nStep;
}
else
{
if(nBegin < nStep)
{
bChangeDir = TRUE;
nRealDir= DIR_LEFTUP;
nBegin = nStep - nBegin;
}
else
{
nBegin -= nStep;
}
}
}
nNum++;
}
return bNormal;
}/
添加束型子弹
BOOL AddBoundEnemyBullet(FPSApp * pMe,int x,int y,DIR nDir,BYTE nStart,BYTE nStep,BYTE nCount,BYTE nIndex,BYTE nSpeed)//添加一束一束的子弹
{
//
BOOL bNormal =TRUE;
BYTE nTempAngle =0;
BYTE nCountNum =0 ;
/左三,右三,角度根据nStep 来定吧.
//自身添加了啊.
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,nDir,Cos(nStart),Sin(nStart),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
}
nTempAngle = nStart;
nTempAngle += nStep;
nCountNum=0;
while(nTempAngle<=90)
{
if(nCountNum>=3)
break;
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,nDir,Cos(nStart),Sin(nStart),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
break;
}
nCountNum++;
nTempAngle += nStep;
}
nTempAngle = nStart;
nTempAngle -= nStep;
nCountNum=0;
while(nTempAngle>=0)
{
if(nCountNum>=3)
break;
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,nDir,Cos(nStart),Sin(nStart),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
break;
}
nCountNum++;
nTempAngle -= nStep;
}
/
return bNormal;
}
添加鞭子型子弹
BOOL AddLimbEnemyBullet(FPSApp * pMe,int x,int y,BYTE nAngle,BYTE nIndex,BYTE nSpeed)//辫子型的子弹
{
BOOL bNormal =TRUE;
if(nAngle>180)
nAngle= 180;
if(nAngle>90)
{
//
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_LEFTUP,Cos(180-nAngle),Sin(180-nAngle),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
}
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_LEFTDOWN,Cos(180-nAngle),Sin(180-nAngle),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
}
}
else
{
//
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_RIGHTUP,Cos(nAngle),Sin(nAngle),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
}
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_RIGHTDOWN,Cos(nAngle),Sin(nAngle),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
}
}
return bNormal;
}
添加螺旋型单元子弹
BOOL AddFireEnemyBullet(FPSApp * pMe,int x,int y,BYTE nStart,BYTE nIndex,BYTE nSpeed)//
{
BOOL bNormal =TRUE;
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_LEFTUP,Cos(nStart),Sin(nStart),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
}
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_RIGHTUP,Cos(90-nStart),Sin(90-nStart),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
}
/
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_RIGHTDOWN,Cos(nStart),Sin(nStart),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
}
/
if(!AddEnemyBullet(pMe,WEAPON_ORANGE,x,y,DIR_LEFTDOWN,Cos(90-nStart),Sin(90-nStart),FALSE,nIndex,nSpeed,Bullet_Power))
{
bNormal =FALSE;
}
return bNormal;
}
///添加激光子弹
BOOL AddEnemyLaserBullet(FPSApp * pMe,int x,int y,BYTE nCount)
{
BOOL bNormal = FALSE;
switch(nCount)
{
case 1:
{
if(!AddEnemyBullet(pMe,WEAPON_LASER,x,y,DIR_LEFTDOWN,Cos(0),Sin(0),FALSE,0,Bullet_Speed_Med,Bullet_Power))
{
bNormal =FALSE;
}
}
break;
case 2:
{
if(!AddEnemyBullet(pMe,WEAPON_LASER,x-CELL_W,y-8,DIR_LEFTDOWN,Cos(0),Sin(0),FALSE,0,Bullet_Speed_Med,Bullet_Power))
{
bNormal =FALSE;
}
if(!AddEnemyBullet(pMe,WEAPON_LASER,x-CELL_W,y+8,DIR_LEFTDOWN,Cos(0),Sin(0),FALSE,0,Bullet_Speed_Med,Bullet_Power))
{
bNormal =FALSE;
}
}
break;
case 3:
if(!AddEnemyBullet(pMe,WEAPON_LASER,x-CELL_W,y-CELL_H,DIR_LEFT,Cos(0),Sin(0),FALSE,0,Bullet_Speed_Med,Bullet_Power))
{
bNormal =FALSE;
}
if(!AddEnemyBullet(pMe,WEAPON_LASER,x-CELL_W,y,DIR_LEFT,Cos(0),Sin(0),FALSE,0,Bullet_Speed_Med,Bullet_Power))
{
bNormal =FALSE;
}
if(!AddEnemyBullet(pMe,WEAPON_LASER,x-CELL_W,y+CELL_H,DIR_LEFT,Cos(0),Sin(0),FALSE,0,Bullet_Speed_Med,Bullet_Power))
{
bNormal =FALSE;
}
break;
}
return bNormal;
}
添加自杀式敌人,此处小潜艇而已
BOOL BossAddEnemy(FPSApp * pMe,int x,int y,OBJECT obj,OBJSIZE size)
{
BOOL bNormal = FALSE;
if(!AddSubMarine(pMe,OBJ_SMALL,y-8))
{
bNormal = FALSE;
}
/
if(y+CELL_H< pMe->stWorldRect.dy-CELL_H)
{
if(!AddSubMarine(pMe,OBJ_SMALL,y+CELL_H-8))
{
bNormal = FALSE;
}
}
if(y-CELL_H< 48)
{
if(!AddSubMarine(pMe,OBJ_SMALL,y-CELL_H-8))
{
bNormal = FALSE;
}
}
return bNormal;
}