书接上回。前面介绍了子弹的生成、显示、运动、碰撞、消失的过程。这个过程可以推广到其他精灵上。今天讲旋风、蘑菇兵、火圈。
作为魔法攻击方式的旋风,和子弹大同小异。
旋风的存储与子弹同存储在一个数组中,如下:
struct ROLE FireArray[MAX_MAP_OBJECT];
使用时,用id区分。
旋风生成函数:int GAMEMAP::CheckAni(int itimeclip)
代码部分:
//发子弹
if(iBeginFire)
{
if(0 == iTimeFire )
{
FireArray[iFireNum].show=1;
FireArray[iFireNum].iframe = 0;
//子弹方向
if(0==rmain.idirec)
{
FireArray[iFireNum].movex=1;
}
else
{
FireArray[iFireNum].movex=-1;
}
switch(iAttack)
{
case ATTACK_MAGIC:
FireArray[iFireNum].id=ID_ANI_FIRE_MAGIC;
FireArray[iFireNum].x=rmain.xpos-ID_ANI_FIRE_MAGIC_XOFF;
FireArray[iFireNum].y=rmain.ypos-ID_ANI_FIRE_MAGIC_YOFF;
FireArray[iFireNum].w=FIRE_MAGIC_W;
FireArray[iFireNum].h=FIRE_MAGIC_H;
FireArray[iFireNum].movex=0;
break;
}
//移动数组游标
iFireNum=(iFireNum+1)%MAX_MAP_OBJECT;
}
iTimeFire=(iTimeFire+1)%TIME_FIRE_BETWEEN;
}
这和子弹生成的处理相同。唯一区别是旋风不移动,所以movex属性最后设置为0。
旋风的显示:
旋风在屏幕上的绘制和子弹相同,函数void GAMEMAP::ShowAniObj(MYANIOBJ & bmobj)。代码部分和子弹相同。
但是旋风的帧刷新有些特殊处理,函数void GAMEMAP::ChangeFrame(int itimeclip)
代码部分:
//子弹,攻击控制
for(i=0;i<MAX_MAP_OBJECT;i++)
{
//如果攻击(子弹、旋风)可见
if(FireArray[i].show)
{
switch(FireArray[i].id)
{
case ID_ANI_FIRE_MAGIC:
//旋风当前帧加一
FireArray[i].iframe++;
//如果帧为2(即第三张图片) ,图片坐标修正
if(FireArray[i].iframe == 2)
{
FireArray[i].x+=FIRE_MAGIC_W;
}
//如果帧号大于3,即四张图片播放完,旋风消失,设置为不可见
if(FireArray[i].iframe>3)
{
FireArray[i].show=0;
}
break;
}
}
至此,旋风显示,动画播放结束后消失.
旋风不涉及运动。碰撞检测的处理和子弹相同,唯一区别是:当旋风和小怪碰撞,旋风不消失。
函数为:int GAMEMAP::CheckAni(int itimeclip)
代码如下:
switch(iAttack)
{
case ATTACK_NORMAL:
//子弹消失
FireArray[j].show=0;
break;
//旋风不消失
default:
break;
}
那么,再看小怪消失的函数void GAMEMAP::ClearEnemy(int i)
代码部分:
MapEnemyArray[i].health--;
if(MapEnemyArray[i].health<=0)
{
MapEnemyArray[i].show=0;
}
可以看到, 此时并不区分攻击方式. 但旋风存在的时间长(动画结束后消失),相当于多次调用了这个函数,间接提高了杀伤力.
至此,两种攻击方式都已实现.
再看小怪, 分蘑菇兵和火圈两种.
存储问题. 和攻击方式处理相同, 用数组加游标的方法.蘑菇兵和火圈存储在同一数组中,如下:
struct ROLE MapEnemyArray[MAX_MAP_OBJECT];
int iMapEnemyCursor;
小怪生成:
小怪是由地图文件设定好的.以第二关的地图文件为例,其中小怪部分如下:
;enemy
21 6 1 1 0 15 24
23 6 1 1 0 15 24
48 7 2 2 6 0 0
68 5 2 2 8 0 0
各个参数是什么意义呢?看一下加载函数就全明白了.函数:int GAMEMAP::LoadMap()
代码部分:
//如果文件没有结束后
while(temp[0]!='#' && !feof(fp))
{
//读入小怪数据 横坐标 纵坐标 宽 高 id 运动范围左边界 右边界
sscanf(temp,"%d %d %d %d %d %d %d",
&MapEnemyArray[i].x,
&MapEnemyArray[i].y,
&MapEnemyArray[i].w,
&MapEnemyArray[i].h,
&MapEnemyArray[i].id,
&MapEnemyArray[i].xleft,
&MapEnemyArray[i].xright);
//坐标转换.乘以32
MapEnemyArray[i].x*=32;
MapEnemyArray[i].y*=32;
MapEnemyArray[i].w*=32;
MapEnemyArray[i].h*=32;
MapEnemyArray[i].xleft*=32;
MapEnemyArray[i].xright*=32;
MapEnemyArray[i].show=1;
//设置移动速度(负,表示向左)
MapEnemyArray[i].movex=-ENEMY_STEP_X;
//动画帧
MapEnemyArray[i].iframe=0;
//动画最大帧
MapEnemyArray[i].iframemax=2;
//设置生命值
switch(MapEnemyArray[i].id)
{
case ID_ANI_BOSS_HOUSE:
MapEnemyArray[i].health=BOSS_HEALTH;
break;
case ID_ANI_BOSS_HOUSE_A:
MapEnemyArray[i].health=BOSS_A_HEALTH;
break;
default:
MapEnemyArray[i].health=1;
break;
}
//将火圈存储在数组的后半段,数值长30, BOSS_CURSOR为15
if ( i<BOSS_CURSOR
&& ( MapEnemyArray[i].id == ID_ANI_BOSS_HOUSE
|| MapEnemyArray[i].id == ID_ANI_BOSS_HOUSE_A) )
{
//move data to BOSS_CURSOR
MapEnemyArray[BOSS_CURSOR]=MapEnemyArray[i];
memset(&MapEnemyArray[i],0,sizeof(MapEnemyArray[i]));
i=BOSS_CURSOR;
}
i++;
//读取下一行地图数据
FGetLineJumpCom(temp,fp);
}
看来比生成子弹要复杂一些.尤其是火圈, 为什么要从第15个元素上存储?因为,火圈要不停地生成蘑菇兵,所以"分区管理",数值前一半存储蘑菇兵,后一半存储火圈.
小怪和火圈是怎样显示、运动的呢?火圈怎样不断产生新的小怪?且听下回分解。
附:
超级玛丽第一版源码链接:http://download.csdn.net/source/497676
超级玛丽增强版源码链接:http://download.csdn.net/source/584350