上一篇:C#开发WPF/Silverlight动画及游戏系列教程(Game Course):(十八) 完美精灵之八面玲珑②

首先我要对上一节中最后的ChangeAction()方法进行一些补充说明。该方法的作用之一是根据精灵的当前动作(Action)来设置精灵切图动画的起始帧和结束帧:


附件: 1.jpg


      如上图,我们可以很清楚的看到精灵这5个动作所分别对应的CurrentStartFrame和CurrentEndFrame。这两个参数很重要是因为在精灵的生命线程中我们可以通过如下黄色区域代码来实现动态的更新精灵角色图片以形成连续动画:
  1.         //精灵线程间隔事件

  2.         private void Timer_Tick(object sender, EventArgs e) {

  3.             ……

  4.             //动态更改精灵图片源以形成精灵连续动作

  5.             Body.Source = Source[(int)Direction, FrameCounter];

  6.             FrameCounter = FrameCounter == CurrentEndFrame ? CurrentStartFrame : FrameCounter + 1;

  7.         }
复制代码
举个例子:当精灵在跑步的时候FrameCounter从5开始记数,然后以1为单位阶梯推进,目标是12,当到了12后再返回5继续重复前面的过程;当精灵在施法的时候,FrameCounter从20开始记数,然后以1为单位阶梯推进,目标是25,当到了25后再返回20重复前面过程。其他的以此类推……

      充实了精灵动画原理后,我们再重新回到本节的主题上:如何使精灵在移动的时候表现出正确的朝向以及精确的定位与停止。

      大家是否还记得我在第六节结尾的地方略有提到相关的实现方法,但是并未对之进行实现,也算留给大家的一个小思考吧。但是本节我既然起了完美精灵这个题目,就不打算辜负所有朋友们的期待,我们首先分析实现8方向精灵的步骤:

      1、获取主角当前的坐标,这在第十五节中已经完美实现了,而且同样是定位到脚底的(Spirit.X,Spirit.Y)。

      2、获取目标坐标,即鼠标左键(或右键)点击的点的坐标,该坐标我们可以通过鼠标左键(或右键)点击事件轻松得到,这在前面的章节里有大量的提及。

      3、以以上两个坐标为参数,通过正切值计算公式计算出主角当前的朝向并返回一个数字代号(0-7分别对应8个方向)

      具体如何操作,且看下图:


附件: 2.jpg




      原理在上图右半部分的注释中描述很清楚;我依据此原理写了个通用判断朝向的方法,精华哦:
  1.         /// <summary>

  2.         /// 通过正切值获取精灵的朝向代号

  3.         /// </summary>

  4.         /// <param name="targetX">目标点的X值</param>

  5.         /// <param name="targetY">目标点的Y值</param>

  6.         /// <param name="currentX">当前点的X值</param>

  7.         /// <param name="currentY">当前点的Y值</param>

  8.         /// <returns>精灵朝向代号(以北为0顺时针依次1,2,3,4,5,6,7)</returns>

  9.         public static double GetDirectionByTan(double targetX, double targetY, double currentX, double currentY) {

  10.             double tan = (targetY - currentY) / (targetX - currentX);

  11.             if (Math.Abs(tan) >= Math.Tan(Math.PI * 3 / 8) && targetY <= currentY) {

  12.                 return 0;

  13.             } else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX > currentX && targetY < currentY) {

  14.                 return 1;

  15.             } else if (Math.Abs(tan) <= Math.Tan(Math.PI / 8) && targetX >= currentX) {

  16.                 return 2;

  17.             } else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX > currentX && targetY > currentY) {

  18.                 return 3;

  19.             } else if (Math.Abs(tan) >= Math.Tan(Math.PI * 3 / 8) && targetY >= currentY) {

  20.                 return 4;

  21.             } else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX < currentX && targetY > currentY) {

  22.                 return 5;

  23.             } else if (Math.Abs(tan) <= Math.Tan(Math.PI / 8) && targetX <= currentX) {

  24.                 return 6;

  25.             } else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX < currentX && targetY < currentY) {

  26.                 return 7;

  27.             } else {

  28.                 return 0;

  29.             }

  30.         }
复制代码
由于Math.Tan()函数的参数为弧度单位,因此需要将角度通过公式换算成弧度,并且至于之中的比较算法逻辑是否为最优,仍然那句老话:仁者见仁,智者见智。或许你写的算法更优秀呢?
TOP