首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Silverlight动画]转向行为 - 2D向量

[Silverlight动画]转向行为 - 2D向量

作者头像
用户1172164
发布2018-01-16 11:58:09
5630
发布2018-01-16 11:58:09
举报

转向行为已经被各种语言实现过多次了,其最底层是用向量来描述的(也是最常见的实现方式)。

概括的看,一个向量由两部分组成:一个方向和一个大小。比如,一个运动中对象的速度由它要去哪里(方向)和移动快慢(大小)两部分组成。因此,把速度看作一 个向量是最贴切不过的。加速度——任何改变对象速度的作用力——同样也是由力的方向和大小组成(另一个向量)。向量同样也可以用来描述对象间的位置关系, 其中大小代表距离,方向代表角度。

向量还可以用来表示一个角色(脸)的朝向,这种情况下就只管方向,而忽视大小,也可以说大小等于1。这样的向量叫做单位向量(unit vector)。实际上,只有一单位长度的向量,在数学运算上能起到很大的优化作用。

向量的所有这些特性对转向行为来说都很有用,因为速度,队伍方向,对象间距离,对象的朝向都会被大量的使用。

    public class Vector2D
    {
        private double _x;
        private double _y;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public Vector2D(double x, double y)
        {
            _x = x;
            _y = y;
        }

        public double x
        {
            get
            {
                return _x;
            }
            set
            {
                _x = value;
            }
        }

        public double y
        {
            get
            {
                return _y;
            }
            set
            {
                _y = value;
            }
        }

        /// <summary>
        /// 克隆
        /// </summary>
        /// <returns></returns>
        public Vector2D clone()
        {
            return new Vector2D(x, y);
        }

        /// <summary>
        /// 初始化向量
        /// </summary>
        /// <returns></returns>
        public Vector2D zero()
        {
            _x = 0;
            _y = 0;
            return this;
        }

        /// <summary>
        /// 这个向量是否等于零,即x,y,长度为零。
        /// </summary>
        /// <returns></returns>
        public bool isZero()
        {
            return _x == 0 && _y == 0;
        }

        /// <summary>
        /// 向量大小
        /// </summary>
        public double length
        {
            get
            {
                return Math.Sqrt(lengthSQ);
            }
            set
            {
                double a = angle;
                _x = Math.Cos(a) * value;
                _y = Math.Sin(a) * value;
            }
        }
        /// <summary>
        /// 得到这个向量长度的平方。
        /// </summary>
        public double lengthSQ
        {
            get
            {
                return _x * _x + _y * _y;
            }
        }

        /// <summary>
        /// 得到这个向量角度。
        /// </summary>
        public double angle
        {
            get
            {
                return Math.Atan2(_y, _x);
            }
            set
            {
                double len = length;
                _x = Math.Cos(value) * len;
                _y = Math.Sin(value) * len;
            }
        }

        /// <summary>
        /// 单位化向量,设定长度为一,更有效率。
        /// </summary>
        /// <returns></returns>
        public Vector2D normalize()
        {
            if (length == 0)
            {
                _x = 1;
                return this;
            }
            double len = length;
            _x/=len;
            _y/=len;
            return this;
        }

        /// <summary>
        /// 截断
        /// </summary>
        /// <param name="max"></param>
        /// <returns></returns>
        public Vector2D truncate(double max)
        {
            length = Math.Min(max, length);
            return this;
        }

        /// <summary>
        /// 倒置
        /// </summary>
        /// <returns></returns>     
        public Vector2D reverse()
        {
            _x = -_x;
            _y = -_y;
            return this;
        }

        /// <summary>
        /// 是否单位化
        /// </summary>
        /// <returns></returns>
        public bool isNormalized()
        {
            return length == 1.0;
        }

        /// <summary>
        /// 积
        /// </summary>
        /// <param name="v2"></param>
        /// <returns></returns>
        public double dotProd(Vector2D v2)
        {
            return _x * v2.x + _y * v2.y;
        }

        /// <summary>
        /// 差
        /// </summary>
        /// <param name="v2"></param>
        /// <returns></returns>
        public double crossProd(Vector2D v2) {
            return _x * v2.y - _y * v2.x;
        }

        /// <summary>
        /// 两个向量之差
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static double angleBetween(Vector2D v1, Vector2D v2)
        {
            if (!v1.isNormalized())
            {
                v1 = v1.clone().normalize();
            }
            if (v2.isNormalized())
            {
                v2 = v2.clone().normalize();
            }
            return Math.Acos(v1.dotProd(v2));
        }

        /// <summary>
        /// 确定给定向量的方向
        /// </summary>
        /// <param name="v2"></param>
        /// <returns></returns>
        public int sign(Vector2D v2)
        {
            return perp.dotProd(v2) < 0 ?-1: 1;
        }

        /// <summary>
        /// 垂直与这个向量的向量
        /// </summary>
        public Vector2D perp
        {
            get
            {
                return new Vector2D(-y, x);
            }
        }

        /// <summary>
        /// 两个向量间的距离
        /// </summary>
        /// <param name="v2"></param>
        /// <returns></returns>
        public double dist(Vector2D v2)
        {
            return Math.Sqrt(distSQ(v2));
        }

        /// <summary>
        /// 计算向量到另一个给定向量的距离
        /// </summary>
        /// <param name="v2"></param>
        /// <returns></returns>
        public double distSQ(Vector2D v2)
        {
            double dx = v2.x - x;
            double dy = v2.y - y;
            return dx * dx + dy * dy;
        }

        /// <summary>
        /// 和
        /// </summary>
        /// <param name="v2"></param>
        /// <returns></returns>
        public Vector2D add(Vector2D v2)
        {
            return new Vector2D(_x + v2.x, _y + v2.y);
        }

        /// <summary>
        /// 差
        /// </summary>
        /// <param name="v2"></param>
        /// <returns></returns>
        public Vector2D subtract(Vector2D v2)
        {
            return new Vector2D(_x - v2.x, _y - v2.y);
        }

        /// <summary>
        /// 翻倍
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public Vector2D multiply(double value)
        {
            return new Vector2D(_x * value, _y * value);
        }

        /// <summary>
        /// 减倍
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public Vector2D divide(double value)
        {
            return new Vector2D(_x / value, _y / value);
        }

        /// <summary>
        /// 是否相等
        /// </summary>
        /// <param name="v2"></param>
        /// <returns></returns>
        public bool equals(Vector2D v2)
        {
            return _x == v2.x && _y == v2.y;
        }

        public override string ToString()
        {
            return "[Vector2D(x:" + _x + ",y:" + _y + ")]";
        }
    }

对于实现这样的类,在架构上就存在着挑战,比如决定类的方法该如何工作。对这些方法truncate,normalize,reverse,add,substrat,multiple和divide有两种考虑情况:是直接对调用对象做改变呢,还是返回一个新的对象。

举个例子,假设有vectorA(3,2)意思是x等于3,y等于2,和vectorB等于(4,5),然后执行以下代码:

vectorA.add(vectorB);

根据第一种情况,vectorB不变,而vectorA等于(7,7)。

而另一种情况,vectorA和vectorB都不变,但是产生一个新的等于(7,7)的向量,我们让它等于vectorC。

vectorC = vectorA.add(vectorB);

那么哪种做法才合适呢?对此,我前前后后进行了多番审视,最终发现在很多数学运算时,需要把一个对象——比如位置和速度——用向量来表示,而无所谓运算后对象本身的改变。所以,加、减、乘、除不对原对象进行修改。

然而truncate(截断),reverse(倒置)和normalize(单位化)则更注重对象本身的改变,所以这些操作用来直接改变对象要比返回一个新的更有用。

互换以上操作方式也不是很难。如果想把vectorB加在vectorA上,可以这样:

vectorA = vectorA.add(vectorB);

而如果想让normalize返回一个新的对象,可以使用clone(克隆)函数:

normalizedA = vectorA.clone().normalize();

现在,有了向量类可以表示角色的位置,速度和各种群体。还需要一个类用来表示角色。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2010-06-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档