• [翻译]XNA外文博客文章精选之twelve



    PS:自己翻译的,转载请著明出处

                                                         RTS风格的移动:转向并向一个目标移动
                                                                                                Phantom
                                         在许多游戏里都会有一个共同的特点,它的RTS(即时战略),PRG(角色扮演)或者其他游戏类型,是使精灵旋转后面对一个目标;在RTS里这通常是一个敌人或者正好是要移动到的一个位置。XNA使它变的十分简单去旋转精灵或者单独的或者做为一组旋转,但是找到角度可以稍微更复杂使用三角法和弧度。
                                         这篇文章将会展示你如何去创建一个unit(个体),当给定一个地方旋转去面对它并且移动到那个位置。它同样会解释在后台的这个三角法和矩阵。
                                         这个unit它本身将会可拖拉的游戏组件部分,这主要是由于IM只有创造一个unit在这个例子中。
    1 public class Unit:DrawableGameComponent
    2 {
    3         private Texture2D m_unit;
    4         private SpriteBatch m_batch;
    5         private Matrix m_matrix;
    6         private float m_movementspeed;
    7         private float m_turningspeed;
                                         这前面的一小片代码中,我们为这个unit包括5个基础的变量,它们都十分简单,这个unit的纹理,sprite batch去绘制它,这个矩阵,它将在后面被使用并且最后连个浮点型数去控制转向和unit的移动的速度。
                                         我们同样需要有一个变量为unit的当前的位置和旋转,但同样一个变量去保存位置,这个位置是我们象要精灵最终移动到的并且角度是面对这个位置。
     1 private Vector2 m_endposistion;
     2 public Vector2 EndPosition
     3 {
     4     get { return m_endposistion; }
     5     set
     6     {
     7         m_endposistion = value;
     8         UpdateEndDirection();           
     9     }
    10 }
    11 
    12     private Vector2 m_position;
    13     private float m_enddirection;
    14     private float m_rotation;
    15     private float m_direction
    16     {
    17       get { return m_rotation; }
    18       set
    19       {
    20          m_rotation = value;
    21          if (m_rotation < 0)
    22          {
    23              m_rotation += (float)(Math.PI * 2);
    24          }
    25          else if (m_rotation > Math.PI * 2)
    26          {
    27              m_rotation -= (float)(Math.PI * 2);
    28          }
    29       }
    30 }
                                        这个m_endposition变量同样有一个公有属性作为当你希望发送这个unit到一个新的位置,每一次这个EndPosition被设置一个被调用的方法同样去更新m_enddirection变量。m_position保存这个units当前的位置和m_rotation保存当前的units的旋转,然而,这是通过m_direction属性被更新的,这个属性执行一个检查每一次它的更新去确保浮点值总是保持在0和2Pi之间;这是因为所有的C#使用弧度去测量在一个圆里的角度,一个整圆是2Pi的弧度,它近似于6.283,大于它或者小于也是可以使用的,但是产生了更多的麻烦,一会在计算上去结实这个数据。
    1 private Vector2 m_origin;
                                        最后一个变量是初始点,这点是unit将会转向的,如果你想要unit开始在中心,因为是我选择要这样,然后设置你的unit的高度和宽度的一半。
                                        下一部分是结构器和加载内容,这都是非常直接的。因为这个unit是一个可拖拽的游戏组件部分,,我们必须传入到游戏对象中,但是我们同样传入unit的开始的位置。
     1 public Unit(Game game, Vector2 pos): base (game)
     2 {
     3       m_position = pos;
     4       m_endposistion = pos;
     5       m_origin = new Vector2(3232;
     6       m_movementspeed = 15f;
     7       m_turningspeed = 0.05f;
     8 }
     9 protected override void LoadContent()
    10 {
    11      m_unit = Game.Content.Load<Texture2D>("Unit"); 
    12      m_batch = new SpriteBatch(GraphicsDevice);
    13      base.LoadContent();
    14 
                                        开始的位置传入到构造器中,我们设置units当前和结束的位置,这是为了让unit开始不动。这个构造器同样设置我们初始位置和为了速度的两个浮点型。
                                        这LoadContent方法是十分简单的,它加载我们的unit纹理和使用spritebatch去绘制它。
    1 public override void Draw(GameTime gameTime)
    2 {
    3     m_batch.Begin();
    4     m_batch.Draw(m_unit, m_position, null, Color.Red, m_direction, m_origin, 1f, SpriteEffects.None, 0.0f);
    5     m_batch.End();
    6     base.Draw(gameTime);
    7 }
                                        Draw比平常略微不同,因为当我们调用draw方法在sprite batch上,我们使用第6个,这样我们可以传入m_direction作为第5个变量,这个变量绘制精灵面对的方向;我们同样传入我们的初始位置告诉它,旋转精灵去指向它。另外一个变量十分简单可以自我解释。
                                        纹理,位置,没有源矩形,风格,当前旋转,在旋转点,缩放,没有效果,没有深度。
                                        我喜欢保留update方法,简单放置大量代码在另外两个方法中。
     1 public override void Update(GameTime gameTime)
     2 {
     3     if (m_direction != m_enddirection)
     4     {// If your not currently facing the right direction turn
     5          TurnTo();
     6     }
     7     else if (m_position != m_endposistion)
     8     {// Else If your not at your end destination move
     9          MoveTo();
    10     }
    11     base.Update(gameTime);
    12 }
                                       当update方法被调用,它首先检查当前的unit是否面对正确的方向,通过m_direction和m_enddirection的比较,如果它不是的,它调用TurnTo方法;如果unit当前是面对正如的方向,然后它把当前的位置(m_position)与结束的位置(m_endposition)相比较,如果unit不在结束的位置上它调用MoveTo方法。
                                       MoveTo方法被用来更新unit的位置,而这正是更复杂的观念在起作用。首先的事情是这个方法的是得到当前和结束位置之间的距离,通过使用Pythagoreantheorem,幸运的是XNA有一个方法处理这个,调用Length方法在一个Vector2上,将给你提供两个角之间的直线距离。通过在两个位置之间你得到的Vector2的当前位置,减去结束点位置,并且调用Length方法在你提供的距离的一个浮点型上。
     1 private void MoveTo()
     2 {
     3        float distance = (m_position - m_endposistion).Length();
     4        if (distance < m_movementspeed)
     5        {
     6            m_position = m_endposistion;
     7        }
     8        else
     9        { 
    10           m_matrix = Matrix.CreateTranslation(-m_position.X, -m_position.Y, 0);
    11           m_matrix = Matrix.CreateRotationZ(-m_direction);
    12           m_matrix *= Matrix.CreateTranslation(0,-m_movementspeed, 0); 
    13           m_matrix *= Matrix.CreateRotationZ(m_direction);
    14           m_matrix *= Matrix.CreateTranslation(m_position.X, m_position.Y, 0);  
                                       一旦你有m_position和m_endposition之间的距离,首先检查的是unit是否达到结束的位置,这将通过m_movmentspeed是否比这个距离小来返回,如果是这种情况,没有做任何更多的计算你点只需要设置m_position与m_endposition相等。
                                       在这种情况下,unit不会达到结束位置,然后你需要unit以当前速度朝着这个结束位置移动。我选择使用一个矩阵去做这个,但是我同样还是要去解释这个方法,使用Pythagoreantheorem(勾股定理)
                                       使用这个Matrix方法,这里有5步在你可以得到unit的新的向量位置之前。你将会看见每一个阶段的显示出的效果,因为它可以影响你的unit在每一个阶段。

                     1.开始的位置。
                     2.矩阵总是旋转在0,0上,这样你需要使用你当前位置的负方向转变这个矩阵
                     3.然后你需要旋转矩阵,这样它很容易去添加你的移动,所以如果你旋转它到你当前方向的负方向时,他将会指向上。
                     4.一旦矩阵被旋转,你现在刚好可以通过准确的速度来转换它,你希望你的unit通过这个速度来传播。
                     5.现在你有效的移动你的unit,如你所希望的,并且现在可以再运用于旋转。
                     6.最后再运用你前面的位置的平移,并且以你最终的位置结束。
    1 Vector3 dest = m_matrix.Translation;
    2 m_position.X = dest.X;
    3 m_position.Y = dest.Y;
                                       一旦你操作你的矩阵去产生渴望的向量,然后你可以使用这个矩阵的Translation属性去找到移动的总和,你创建并设置你的units位置到它们中。
                                       aulternative方法是用来找到两个位置之间的距离,并且使用移动速度除以它,这将产生更新需要得到结束位置的数量。一旦你有它,通过更新所需要的数量,你可以划分Vector2代表在这两个位置之间的距离,这将产生一个向量1更新了移动的值,并且所有你需要去做的是这个units的当前的位置。(译者:这是一位俄罗斯的人写的,十分难懂,可以参照原文看。)
    1 Vector2 distance = m_position - m_endposistion;
    2 float turns = distance.Length() / m_movementspeed;
    3 distance /= turns;
    4 m_position -= distance;
                                       当unit不是面对正确的方向,TurnTo被调用,这个方法更新units的方向。方法的第一部分是有点象MoveTo方法,首先它得到在m_direction和m_enddirection之间的距离,然后它测试去看看这是否较小,然后改变速度,但是如果这个距离是一个负的,它添加一个"-"在这个距离的前面让它变成正的。
     1 private void TurnTo()
     2 {
     3       float d = m_enddirection - m_direction
     4       if (d < 0 && -< m_turningspeed || d > 0 && d < m_turningspeed)
     5       {
     6             m_direction = m_enddirection;// Turn to the end 
     7       }
     8       else
     9       {
    10            if (d > 0 && d > Math.PI)
    11            {
    12                m_direction -= m_turningspeed;
    13            }
    14            else if (d < 0 && (-d  < Math.PI))
    15            {
    16                m_direction -= m_turningspeed;
    17            }
    18            else
    19            {
    20                m_direction += m_turningspeed;
    21            }
    22      }
    23 }
                                       方法的第二部分通过m_turnignspeed给定的数量更新当前的方向,但是它同样决定是否转向左或者右。这两个IF语句决定这个,如果这个距离是一个正数并且比Pi大,或者如果这个距离是个负数并且比Pi小,unit需要转向左因为这是短距离的左转;否则unit需要转向右;
                                       最后的方法是UpdateEndDirection,它每一次被调用一个新的结束位置,这个位置被给予这个unit,它同样是支持三角法的方法。
                                       为了得到结束的方法,你使用向量的X和Y坐标创建在当前和结束位置之间。该计算公式是:ATan(O/A)

                                       ATan是一个数学函数提供在C#中,这个O部分代表从你想要找到的角度的直角三角型的对边,这个A部分代表邻边。我选择去使用X作为邻边和Y作为对边,你可以从相反的方向转动它,但是其他的计算部分稍后也必须改变。
     1 private void UpdateEndDirection()
     2 {
     3      Vector2 d = m_endposistion - m_position;
     4      double a = 0;
     5      float m = 0.5f;
     6      if (d.X != 0 && d.Y != 0)
     7      {
     8           a = Math.Atan(d.Y / d.X);
     9      }
    10      else if (d.X == 0 && d.Y > 0)
    11      {
    12           m = 0.0f;
    13      }
    14      if (d.X < 0)
    15      {
    16          a -= (Math.PI * m);
    17      }
    18      else
    19      {
    20          a += (Math.PI * m);
    21      }
    22      if (a < 0)
    23      {
    24          a += (Math.PI * 2);
    25      }
    26       m_enddirection = (float)a;
    27 }
                                       这个方法的主要的问题是你只可以找到直角三角形的角度,也就是说你不能使用这个计算并且马上找到这个角。首先是它不能计算这个角度,如果这个方向是直接UP的,所以它只是计算不是在这种情况下。否则一个旋转的1/4的modifier既不增加也不减去从产生的这个角度,除非角度是直角。因为X被用来作为三角法的邻近部分计算它是同样对modifier作出决定,如果这个X部分的不同是负的,modifier被减否则它被加。这是因为如果你划分X和Y部分,并且他们中的一个是负的,那么另外的不是负的。你会产生一个负的角度,一个负的角度将使角度接近0的位置,一个负的角度将会使它接近Pi(旋转的一半),所以通过添加一半的Pi(旋转的四分之一)你可以计算角度在正X和通过减去你可以计算角度在一个负X。
                                      最后你检查这个角度是负的,如果它不是,你增加1去保证它是并且设置新的结束方向到这个值中。
                                      在这个例子里提供了,当你点击鼠标右键,它给这个unit一个新的目标去移动。

    源代码:http://www.ziggyware.com/readarticle.php?article_id=153
    (完)
  • 相关阅读:
    linux下yum错误:[Errno 14] problem making ssl connection Trying other mirror.
    linux下sudo命令
    myeclipse修改编译器版本的方法 .
    java 使用POI读写Excel文件(兼容2003、2007)
    Google.ProtocolBuffers.dll 之.Net应用(一)
    禁止Grid、TreeGrid列排序和列菜单
    在VS2008中加入ExtJS智能提示
    教程地址
    ExtJS xtype 一览
    ExtJS中,将Grid表头中的全选复选框取消复选
  • 原文地址:https://www.cnblogs.com/315358525/p/1563537.html
Copyright © 2020-2023  润新知