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


    第 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:



                                                           图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 = new Texture2D[12];       

         solider.AttackTextures = new Texture2D[10];       

         for (int i = 0; i < 12;i++ )

        {           

            solider.RunTextures[i] = creenManager.Game.Content.Load("Enemy/Run/"+(i+1));

         }       

         for (int j= 0;j< 10; j++)

         {    

             solider.AttackTextures[j] = ScreenManager.Game.Content.Load("Enemy/Attack/" + (j + 1));    

         }    

         solider.Texture=solider.RunTextures[0];//预先给士兵贴图一个初始值  

       }

     

      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[solider.RunFrameCount];               

                }               

            if (solider.State == PersonState.Attack)               

            {                   

                 solider.AttackFrameCount += 1;                  

                 if (solider.AttackFrameCount > solider.AttackTextures.Length - 1)//如果播完最后一帧              {                       

                      solider.AttackFrameCount = 0; //就回到第一帧                  

                  }                   

                  solider.Texture = solider.AttackTextures[solider.AttackFrameCount];               

              }

             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:

      

     

     

                                                        图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[12];

                     person.AttackTextures = new Texture2D[10];

                     person.DeadTextures = new Texture2D[10];

                     person.BeattackTextures = new Texture2D[7];

                     for (int i = 0; i < 12; i++)

                    {

                           person.RunTextures[i] = ScreenManager.Game.Content.Load("Enemy/Run/" + (i + 1));           

                     }           

                     for (int i = 0; i < 7; i++)           

                    {               

                          person.BeattackTextures[i] = ScreenManager.Game.Content.Load("Enemy/Beattacked/" + (i + 1));           

                     }           

                    for (int j = 0; j < 10; j++)           

                   {               

                         person.AttackTextures[j] = ScreenManager.Game.Content.Load("Enemy/Attack/" + (j + 1));               

                         person.DeadTextures[j] = ScreenManager.Game.Content.Load("Enemy/Dead/" + (j + 1));

                    }           

                    person.Texture = person.RunTextures[0];           

                    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[solider.RunFrameCount];           

                  }
                  if (solider.State == PersonState.Attack)           

                  {               

                         solider.AttackFrameCount += 1;               

                         if (solider.AttackFrameCount > solider.AttackTextures.Length - 1)//如果播完最后一帧               

                         {                   

                              solider.AttackFrameCount = 0; //就回到第一帧               

                         }               

                         solider.Texture = solider.AttackTextures[solider.AttackFrameCount];           

                  }           

                 if (solider.State == PersonState.Dead)           

                 {               

                     solider.DeadFrameCount += 1;               

                     if (solider.DeadFrameCount > solider.DeadTextures.Length - 1)//如果播完最后一帧                 {                   

                          solider.DeadFrameCount = 0; //就回到第一帧               

                     }               

                    solider.Texture = solider.DeadTextures[solider.DeadFrameCount];           

                  }           

                 if (solider.State == PersonState.Beattacked)           

                 {               

                     solider.BeAttackFrameCount += 1;               

                      if (solider.BeAttackFrameCount > solider.BeattackTextures.Length - 1)//如果播完最后一帧
                     {                   

                          solider.BeAttackFrameCount = 0; //就回到第一帧               

                      }               

                      solider.Texture = solider.BeattackTextures[solider.BeAttackFrameCount];           

                   }       

               }       

     

              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           

              };         

            return soliderRect.Intersects(enemyRect);       

    }

     

    2. 在Update函数增加如下代码:   

        if(IsSpriteHit(solider,enemy)&&solider.State== PersonState.Attack)   

       {               

               enemy.State = PersonState.Beattacked;   

       }

     

    3. 在模拟器执行上述代码效果如图5-2-1:

                                                             

                                                               图5-2-1 士兵战斗碰撞判断

                  这一章的内容讲到这里就玩了,不过两个精灵战斗必然会有伤亡,这就需要我们加上伤害,防御,血量等数据,以及判断精灵是否死亡,精灵死亡后播放死亡动画,这就留给读者继续完善本章节的这个游戏。

       本文版权属于王传炜所有,首发http://www.cnblogs.com/,转载请注明出处。
  • 相关阅读:
    cookie标准话
    thinkphp关联查询
    jq获取表单值与赋值代码
    Github使用.gitignore文件忽略不必要上传的文件 (转)
    python 库 Numpy 中如何求取向量范数 np.linalg.norm(求范数)(向量的第二范数为传统意义上的向量长度),(如何求取向量的单位向量)
    word 使用中 上标符号的实现
    python 编程中的一个关于图片的库 imageio (读取照片RGB内容,转换照片格式)
    交叉熵代价函数(作用及公式推导) ------转载
    cousera 深度学习 吴恩达 第一课 第二周 学习率对优化结果的影响
    python3 读入一个jpg格式的图片,并转换长宽像素个数,然后进行绘制
  • 原文地址:https://www.cnblogs.com/wangergo/p/2605767.html
Copyright © 2020-2023  润新知