现在的位置: 首页 > 综合 > 正文

超级玛丽制作揭秘12旋风攻击,小怪,火圈

2013年10月13日 ⁄ 综合 ⁄ 共 3562字 ⁄ 字号 评论关闭

书接上回。前面介绍了子弹的生成、显示、运动、碰撞、消失的过程。这个过程可以推广到其他精灵上。今天讲旋风、蘑菇兵、火圈。
作为魔法攻击方式的旋风,和子弹大同小异。
旋风的存储与子弹同存储在一个数组中,如下:
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

 

抱歉!评论已关闭.