wangergo 发表于 2013-1-4 01:16:38

如何在WP7上用XNA写2D游戏(六)

<div id="cnblogs_post_body">第 5 章XNA里的2D动画进阶
5.1 游戏精灵的动画切换

上一章,我们做出了可控制移动的精灵。精灵是行走状态,实际游戏里一个游戏人物可跳可走,可跑,收到攻击会受伤,还会死亡。那么这些都是人物的状态在改变,相应的我们要改变精灵播放的动画帧。
1.先定义一个枚举
public enum PersonState
{
   ran,
   walk,
   beattacked,
   dead
}
2.我们给精灵加一个状态属性:
PersonState State{get;set;}
3.然后我们修改update函数里的代码:
if(sprite.State==PersonState.ran)
{
sprite.Textures= RunTextures;
} else
if(sprite.State==PersonState.walk)
{
   sprite.Textures= WalkTextures;
}
......
在游戏运行时,只要改变精灵对象sprite的State属性就可以控制精灵的动画切换了。
//以下为伪代码
if(sprite.beattacked)
//如果精灵收到攻击
{
    sprite.state=PersonState.beattacked;
}
if(sprite.isDead)
{
sprite.state=PersonState.dead;
}
4.在Draw函数里改变响应的Texture2D数组,来改变动画帧。
      按照这个思路,我们开始完善我们的游戏代码吧:
1. 定义Sprite类:
    /// <summary>
    /// 游戏实体对象,相当于精灵实体
    /// </summary>
   public class Sprite
    {
      /// <summary>
      /// 所在位置
      /// </summary>
      public Vector2 Position;
       /// <summary>
       /// 目标位置
      /// </summary>
       public Vector2 EndPosition;
   
   /// <summary>
   /// 运动矢量
   /// </summary>
      public Vector2 Velocity;

      /// <summary>
       /// 奔跑动画帧数
      /// </summary>
      public int RunFrameCount;

      /// <summary>
       /// 死亡动画帧数
      /// </summary>
      public int DeadFrameCount;

      /// <summary>
       /// 被攻击动画帧数
      /// </summary>
      public int BeAttackFrameCount;

       /// <summary>
      /// 攻击动画帧数
    /// </summary>
    public int AttackFrameCount;

   /// <summary>
    /// 贴图
    /// </summary>
      public Texture2D Texture;

      /// <summary>
       /// 跑动动画贴图
      /// </summary>
      public Texture2D[] RunTextures;

      /// <summary>
      /// 死亡动画贴图
      /// </summary>
      public Texture2D[] DeadTextures;

       /// <summary>
       /// 被攻击动画贴图
       /// </summary>
      public Texture2D[] BeattackTextures;

       /// <summary>
       /// 攻击动画贴图
   /// </summary>
      public Texture2D[] AttackTextures;

   /// <summary>
   /// 生命值
   /// </summary>
   public float Life;
   
   /// <summary>
    /// 是否死亡
    /// </summary>
      public bool IsAlive;

      /// <summary>
       /// 精灵状态
      /// </summary>
       public PersonState
State;
}
2. 定义精灵状态枚举
    public enum PersonState
   {
       Ran,
       Attack,
       Beattacked,
       Dead
   }

3. 添加一个“A”键贴图,并且输入到屏幕右下角,如图5-1-1: http://images.cnblogs.com/cnblogs_com/wangergo/60684/5-1.png


                                                       图5-1-1 带攻击键的游戏界面
4.修改LoadContent函数为:
public override void LoadContent()
{
   base.LoadContent();
playerTexture =ScreenManager.Game.Content.Load("Player/1");      
    scrossTexure = ScreenManager.Game.Content.Load("UI/scross");      
    aTexture = ScreenManager.Game.Content.Load("UI/a");      
    solider.RunFrameCount = 12;      
    solider.AttackFrameCount = 10;
    solider.State = PersonState.Ran;      
   solider.RunTextures = newTexture2D;      
   solider.AttackTextures = new Texture2D;      
   for (int i = 0; i < 12;i++ )
   {         
      solider.RunTextures = creenManager.Game.Content.Load("Enemy/Run/"+(i+1));
    }      
   for (int j= 0;j< 10; j++)
    {   
         solider.AttackTextures = ScreenManager.Game.Content.Load("Enemy/Attack/" + (j + 1));   
   }   
   solider.Texture=solider.RunTextures;//预先给士兵贴图一个初始值
   }

5.修改Update函数里的代码,当用户触碰A键的时候,改变士兵的状态为攻击状态。      
    public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)      
{         
      base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);         
      float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;         
      updateCount++;         
       if (updateCount > timeSpan)//TimeSpan为时间间隔量         
       {               
            if (solider.State == PersonState.Ran)               
            {                  
                  solider.RunFrameCount += 1;                  
                  if (solider.RunFrameCount > solider.RunTextures.Length - 1)//如果播完最后一帧                  {                     
                         solider.RunFrameCount = 0; //就回到第一帧                  
                   }                  
                   solider.Texture = solider.RunTextures;               
            }               
      if (solider.State == PersonState.Attack)               
      {                  
             solider.AttackFrameCount += 1;                  
             if (solider.AttackFrameCount > solider.AttackTextures.Length - 1)//如果播完最后一帧             {                     
                  solider.AttackFrameCount = 0; //就回到第一帧                  
            }                  
            solider.Texture = solider.AttackTextures;               
          }
      updateCount = 0;         
    }         
    endPosition += elapsedTime * speed;         
    if (endPosition.X>800)         
   {               
         endPosition = new Vector2(0,200);         
      }      
   }

6.修改Draw函数里代码:
    public override void Draw(GameTime gameTime)
    {      
          float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;      
          float totalTime = (float)gameTime.TotalGameTime.TotalSeconds;          ScreenManager.SpriteBatch.Begin();      
          ScreenManager.SpriteBatch.Draw(solider.Texture, endPosition, Color.White);          ScreenManager.SpriteBatch.Draw(scrossTexure, new Vector2(2,380), Color.White);          ScreenManager.SpriteBatch.Draw(aTexture, new Vector2(722, 380), Color.White);          ScreenManager.SpriteBatch.End();
}

7.在模拟器里运行效果如图5-1-2:
   http://images.cnblogs.com/cnblogs_com/wangergo/60684/5-2.png


                                                    图5-1-2操控士兵行走方向

5.2游戏精灵的碰撞计算
          在一个战斗游戏里,光有一个主角是不太可能的,在上一章节里,我们给主角精灵加了一个攻击动画。如果让主角对敌人做出攻击动作,敌人会出现被攻击状态,直到死亡。那么如何判断主角进入到能对敌人进行攻击的范围呢,这就需要用到游戏精灵的碰撞计算。
          为了简化理解,我们看到主角有高度,有宽度,因为这是个2D的世界。那么我们就可以把主角在平面坐标里用一个矩形表示,矩形的高度近似等于主角的高度,矩形的宽度也近似等于主角的宽度。对敌人而已也是一样的,这样两个精灵物体的碰撞,我们就能变成2D世界里两个矩形是否相交汇。
          在XNA游戏框架里,Rectangle有一个专门的函数Intersects来判断两个矩形是否相交汇。这样我们就解决了精灵的碰撞问题,我们来做一个敌人让主角攻击,如果在主角的攻击范围内,也就是两个游戏精灵能进行碰撞。为了完美表现这个过程,我们需要引入精灵的被攻击动画和死亡动画。
         1.由于主角和敌人都是同一个精灵创建而成,我们可以用同一个函数CreatePerson来实现这个过程:
             Sprite CreatePerson(Vector2 Position)
             {
               Sprite person = new Sprite();         
               person.Position = Position;
               person.RunFrameCount = 12;
               person.AttackFrameCount = 10;
               person.BeAttackFrameCount = 7;
               person.DeadFrameCount = 10;
               person.State = PersonState.Ran;
               person.RunTextures = new Texture2D;
               person.AttackTextures = new Texture2D;
                person.DeadTextures = new Texture2D;
               person.BeattackTextures = new Texture2D;
               for (int i = 0; i < 12; i++)
                {
                     person.RunTextures = ScreenManager.Game.Content.Load("Enemy/Run/" + (i + 1));         
               }         
               for (int i = 0; i < 7; i++)         
                {               
                      person.BeattackTextures = ScreenManager.Game.Content.Load("Enemy/Beattacked/" + (i + 1));         
               }         
                for (int j = 0; j < 10; j++)         
               {               
                     person.AttackTextures = ScreenManager.Game.Content.Load("Enemy/Attack/" + (j + 1));               
                     person.DeadTextures = ScreenManager.Game.Content.Load("Enemy/Dead/" + (j + 1));
               }         
                person.Texture = person.RunTextures;         
                return person;      
          }
         2.创建主角和敌人两个精灵:      
            Sprite solider,enemy;      
            solider= CreatePerson(soliderPosition);      
            enemy = CreatePerson(enemyPosition);
         3.修改Draw函数如下:      
            public override void Draw(GameTime gameTime)      
         {         
                float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;         
                float totalTime = (float)gameTime.TotalGameTime.TotalSeconds;                ScreenManager.SpriteBatch.Begin();         
                ScreenManager.SpriteBatch.Draw(solider.Texture, solider.Position, Color.White);                ScreenManager.SpriteBatch.Draw(enemy.Texture, enemy.Position, Color.White);                ScreenManager.SpriteBatch.Draw(scrossTexure, new Vector2(2,380), Color.White);                ScreenManager.SpriteBatch.Draw(aTexture, new Vector2(722, 380), Color.White);                ScreenManager.SpriteBatch.End();}
         4.增加一个执行精灵动画的方法DoSpriteAnimation,并修改Update函数,如下:   
         /// /// 执行精灵动画-/// ///
         void DoSpriteAnimation(Sprite solider)      
          {         
               if (solider.State == PersonState.Ran)         
            {               
                  solider.RunFrameCount += 1;               
                  if (solider.RunFrameCount > solider.RunTextures.Length - 1)//如播完最后一帧                  {                  
                         solider.RunFrameCount = 0; //就回到第一帧               
                     }               
                     solider.Texture = solider.RunTextures;         
            }
             if (solider.State == PersonState.Attack)         
            {               
                     solider.AttackFrameCount += 1;               
                     if (solider.AttackFrameCount > solider.AttackTextures.Length - 1)//如果播完最后一帧               
                     {                  
                        solider.AttackFrameCount = 0; //就回到第一帧               
                     }               
                     solider.Texture = solider.AttackTextures;         
            }         
             if (solider.State == PersonState.Dead)         
             {               
               solider.DeadFrameCount += 1;               
               if (solider.DeadFrameCount > solider.DeadTextures.Length - 1)//如果播完最后一帧                {                  
                      solider.DeadFrameCount = 0; //就回到第一帧               
               }               
                solider.Texture = solider.DeadTextures;         
            }         
             if (solider.State == PersonState.Beattacked)         
             {               
               solider.BeAttackFrameCount += 1;               
                  if (solider.BeAttackFrameCount > solider.BeattackTextures.Length - 1)//如果播完最后一帧
               {                  
                      solider.BeAttackFrameCount = 0; //就回到第一帧               
                  }               
                  solider.Texture = solider.BeattackTextures;         
               }      
         }      

          public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)      
          {         
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);         
            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;            updateCount++;         
               if (updateCount > timeSpan)//TimeSpan为时间间隔量         
            {               
                   DoSpriteAnimation(solider);               
                   DoSpriteAnimation(enemy);               
                   updateCount = 0;         
               }         
                solider.Position += elapsedTime * speed;         
                if (soliderPosition.X>800)         
                {               
                     soliderPosition = new Vector2(0,200);         
               }      
         }
   5.       写一个判断精灵碰撞的函数IsSpriteHit:      
    /// <summary>
      /// 两个精灵是否相碰
       /// </summary>
       /// <paramname="solider">主角</param>
       /// <paramname="enemy">敌人</param>
       /// <returns>是或者否</returns>
   bool IsSpriteHit(Sprite solider,Sprite enemy)      
   {         
          Rectangle soliderRect = new Rectangle()         
          {               
               Location = new Point()
               {
                  X = (int)solider.Position.X, Y = (int)solider.Position.Y
               },
            Width = solider.Texture.Width,Height = solider.Texture.Height         
         };         
         Rectangle enemyRect = new Rectangle()         
          {               
               Location = new Point()
                {
                      X = (int)enemy.Position.X, Y = (int)enemy.Position.Y
                },
            Width = enemy.Texture.Width,Height = enemy.Texture.Height         
          };         
      returnsoliderRect.Intersects(enemyRect);      
}

2.       在Update函数增加如下代码:   
    if(IsSpriteHit(solider,enemy)&&solider.State== PersonState.Attack)   
   {               
         enemy.State = PersonState.Beattacked;   
   }

3.       在模拟器执行上述代码效果如图5-2-1:
http://images.cnblogs.com/cnblogs_com/wangergo/60684/5-3.png
                                                         
                                                         图5-2-1 士兵战斗碰撞判断                                 
            这一章的内容讲到这里就玩了,不过两个精灵战斗必然会有伤亡,这就需要我们加上伤害,防御,血量等数据,以及判断精灵是否死亡,精灵死亡后播放死亡动画,这就留给读者继续完善本章节的这个游戏。
页: [1]
查看完整版本: 如何在WP7上用XNA写2D游戏(六)