超级玛丽制作揭秘23玩家动作控制

来源:互联网 发布:c 窗口程序编程教程 编辑:程序博客网 时间:2024/05/04 05:02

 玩家移动:把行走和跳跃看成两个状态,各自用不同的变量表示横纵方向的速度。
相关属性:
行走:横向速度为movex,纵向不移动。
跳跃:横向速度为jumpx,纵向速度为movey。当前跳跃高度jumpheight
运动方向:idirec

思路:
第一步:玩家按键,按键处理函数设置这些属性。按键松开,清除动作属性。
第二步:用一个函数不停检测这些变量,控制玩家移动。

1. 按键触发:
按键处理函数:int GAMEMAP::KeyProc(int iKey)
代码:
  switch(iKey)
  {
  case VK_RIGHT: //按右
   //判断是否正在跳跃, 即纵向速度不为0
   if(rmain.movey!=0)
   {
    //跳跃过程中, 设置横向速度, 方向向右, 大小为4像素
    rmain.jumpx=4;
   }
   rmain.movex=4; //设置横向速度, 方向向右, 大小为4像素
   rmain.idirec=0; //设置玩家方向, 向右
   break;
   
  case VK_LEFT:  //按左
   //如果是跳跃过程中, 设置横向速度, 方向向左, 大小为4像素
   if(rmain.movey!=0)
   {
    rmain.jumpx=-4;
   }
   rmain.movex=-4;  //设置横向速度, 方向向左, 大小为4像素
   rmain.idirec=1;  //设置玩家方向, 向左
   break;
  
  case KEY_X: //X键跳
   //如果已经是跳跃状态,不作处理,代码中断
   if(rmain.movey!=0)
    break;
   //设置纵向速度,方向向上(负值),大小为13
   rmain.movey=-SPEED_JUMP;
   //将当前的横向速度,赋值给“跳跃”中的横向速度
   rmain.jumpx=rmain.movex;
   break;
   
  case KEY_Z: //FIRE
   if(iBeginFire)
    break; //如果已经开始攻击,代码中断
   iTimeFire=0; //初始化子弹间隔时间
   iBeginFire=1; //置1,表示开始攻击
   break;
按键松开处理函数:void GAMEMAP::KeyUpProc(int iKey)
代码:
 //松开左右键,清除横向速度
 case VK_RIGHT: 
  rmain.movex=0;
  break;
 case VK_LEFT:
  rmain.movex=0;
  break;
 case KEY_X: //跳
//不能清除跳跃的横向速度jumpx
//例如,移动过程中起跳,整个跳跃过程中都要有横向速度
  break;
 case KEY_Z: //FIRE
  iBeginFire=0;  //停止攻击
  break;

2. 控制移动。
动作检测函数:WndProc
代码:时间片的处理中,根据不同状态,调用各种检测函数。
  case WM_TIMER:
   switch(gamemap.iGameState)
   {
   case GAME_IN:
    rmain.Move();//人物移动
    ……
    break;
说明:每45毫秒产生一个WM_TIMER消息,在GAME_IN状态下,调用各种检测函数。其中rmain.Move()就是不断检测玩家动作属性,实现移动。
函数:void MYROLE::Move()
代码:
 if(0 == movey)
 {
  //如果不是跳跃, 横向移动
  MoveOffset(movex, 0);
 }
 else
 {
  //跳跃, 先横向移动, 再纵向移动
  MoveOffset(jumpx, 0);
  MoveOffset(0, movey);
 }

 //玩家帧控制 ”纠错法”
 if(movex<0 && iFrame<3)
 {
  iFrame=3; //如果玩家向左移动, 而图片向右, 则设置为3(第4张图片)
 }
 if(movex>0 && iFrame>=3)
 {
  iFrame=0; //如果玩家向右移动, 而图片向右, 则设置为0(第1张图片)
 }
 //帧刷新
 if(movex!=0)
 {
  if(0==idirec)
   iFrame=1-iFrame; //如果方向向右, 图片循环播放0,1帧
  else
   iFrame=7-iFrame;  //如果方向向左, 图片循环播放3,4帧
 }
 if(movey!=0)
 {
//跳跃过程中, 帧设置为0(向右),3(向左)
//帧刷新后, 重新设置帧, 就实现了跳跃过程中, 图片静止
  iFrame=idirec*3; 
 }

 //跳跃控制
 if(movey<0)
 {
  //向上运动(纵向速度movey为负值)
  jumpheight+=(-movey);  //增加跳跃高度
  
  //重力影响,速度减慢
  if(movey<-1)
  {
   movey++;
  }

  //到达顶点后向下落, 最大跳跃高度为JUMP_HEIGHT * 32, 即3个格子的高度
  if(jumpheight >= JUMP_HEIGHT * 32)
  { 
   jumpheight =  JUMP_HEIGHT * 32; //跳跃高度置为最大
   movey=4; //纵向速度置为4, 表示开始下落
  }
 }
 else if(movey>0)
 {
  //下落过程, 跳跃高度减少
  jumpheight -= movey;
  //重力影响,速度增大
  movey++;   
 }
玩家移动函数:void MYROLE::MoveOffset(int x,int y)
代码:根据增量设置坐标
 //横纵增量为0,不移动,代码结束
 if(x==0 && y==0)
  return;

 //如果碰到物体,不移动,代码结束
 if(!gamemap.RoleCanMove(x,y))
  return;
 //修改玩家坐标
 xpos+=x;
 ypos+=y;
 //判断是否超出左边界
 if(xpos<minx)
  xpos=minx; //设置玩家坐标为左边界
 //判断是否超出右边界
 if(xpos>maxx)
  xpos=maxx; 

3. 碰撞检测
无论行走,跳跃,都是用函数MoveOffset操纵玩家坐标。这时,就要判断是否碰到物体。如果正在行走,则不能前进;如果是跳跃上升,则开始下落。
函数:int GAMEMAP::RoleCanMove(int xoff, int yoff)
代码:
 int canmove=1;//初始化, 1表示能移动
for(i=0;i<iMapObjNum;i++)
 {
  if( RECT_HIT_RECT(玩家坐标加增量,地图物品坐标))
  {
   //碰到物体,不能移动
   canmove=0;
   if(yoff<0)
   {
    //纵向增量为负(即上升运动), 碰到物体开始下落
    rmain.movey=1;
   }
   if(yoff>0)
   {
//纵向增量为正(即下落运动), 碰到物体, 停止下落
    rmain.jumpheight=0;//清除跳跃高度
    rmain.movey=0;//清除纵向速度
    rmain.ypos=MapArray[i].y*32-32;//纵坐标刷新,保证玩家站在物品上
   }
   break;
  }
 }
 return canmove;

玩家移动的过程中,要不断检测是否站在地图物品上。如果在行走过程中,且没有站在任何物品上,则开始下落。
函数:int GAMEMAP::CheckRole()
代码:
 if(rmain.movey == 0 )
 {
  //检测角色是否站在某个物体上
  for(i=0;i<iMapObjNum;i++)
  {
   //玩家的下边线,是否和物品的上边线重叠
   if( LINE_ON_LINE(rmain.xpos,
    rmain.ypos+32,
    32,
    MapArray[i].x*32,
    MapArray[i].y*32,
    MapArray[i].w*32)
    )
   {
    //返回1,表示玩家踩在这个物品上
    return 1;
   }
  }
  //角色开始下落
  rmain.movey=1; 
  rmain.jumpx=0;//此时要清除跳跃速度,否则将变成跳跃,而不是落体
  return 0;

至此,玩家在这个虚拟世界可以做出各种动作,跳跃,行走,攻击。增强版中,加入了水管,玩家在进出水管,就需要两个动画?怎么实现,且听下回分解。
附:
超级玛丽第一版源码链接:http://download.csdn.net/source/497676
超级玛丽增强版源码链接:http://download.csdn.net/source/584350

原创粉丝点击