向量在游戏开发中的作用

来源:互联网 发布:阿里云配置邮件服务器 编辑:程序博客网 时间:2024/05/02 00:17

(一)

向量在游戏开发中是非常实用的,我们在学校学完向量的知识后,只知道向量的基本知识,但对于如何将所学到的向量知识运用到实际的游戏开发中却不知所措。我计划写几篇关于向量在游戏中的应用的文章,总结一下在实际工作中所遇到过的运用向量机智处理问题的技巧。接下来要写的东西都是记录在我的云笔记中,现在计划整理一下后分享出来。

    对于向量的那些基本性质我不想说太多,有兴趣的读者可以自行查阅相关文档,这里只说向量的大小和方向。不知道读者朋友曾经玩过安卓上一款比较火的跳跃游戏没有,游戏名字叫《涂鸦跳跃》,英文名为「Doodle Jump」,游戏截图如下:

    在玩《涂鸦跳跃》时,不知道你有没有想过这么一个问题:从下往上跳跃会直接穿过平台,从上往下掉不会穿过平台,这是如何实现的?

    关于这个问题,我的思考是:可以利用小人运动的速度的方向来判定是否启用碰撞体,速度方向向上时禁用碰撞体,速度方向向下时启用碰撞体。可能我说的这个思路读者朋友还是不明觉厉,下面我用Unity3D写了一个小Demo来帮助理解我的思路。

先上动态效果图:


具体实现代码如下:

[csharp] view plain copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class PlayerController : MonoBehaviour {  
  5.     public Vector3 jumpForce = new Vector3(10, 35, 0);  
  6.     private Rigidbody mRigidbody;  
  7.     private BoxCollider boxCollider;  
  8.   
  9.     // 是否处于碰撞状态  
  10.     private bool stayCollision = false;  
  11.   
  12.     void Awake ()   
  13.     {  
  14.         this.mRigidbody = this.GetComponent<Rigidbody>();  
  15.         this.boxCollider = this.GetComponent<BoxCollider>();  
  16.     }  
  17.       
  18.     void Update ()   
  19.     {  
  20.         // 当刚体静止时,按空格键跳跃才有效  
  21.         if(mRigidbody.IsSleeping() && Input.GetKeyDown(KeyCode.Space))  
  22.         {  
  23.             // 给刚体施加一个力,是其运动起来  
  24.             this.mRigidbody.AddForce(jumpForce);  
  25.         }  
  26.   
  27.         if(this.mRigidbody.velocity.y > 0) // 速度方向向上  
  28.         {  
  29.             // 这里不能重复设置isTrigger,否则IsSleeping()方法会失效  
  30.             // 这里具体原因不得而知,因为被官方封装起来了,  
  31.             // 但是猜测是因为刚体规定自身方法没有调用时为休眠状态  
  32.             if (!this.boxCollider.isTrigger)   
  33.                 this.boxCollider.isTrigger = true;  
  34.         }  
  35.         else if (!stayCollision) // Player没有发生碰撞  
  36.         {  
  37.             if (this.boxCollider.isTrigger)  
  38.                 this.boxCollider.isTrigger = false;  
  39.         }  
  40.     }  
  41.   
  42.     void OnTriggerEnter(Collider collider)  
  43.     {  
  44.         stayCollision = true;  
  45.     }  
  46.   
  47.     void OnTriggerExit(Collider collider)  
  48.     {  
  49.         stayCollision = false;  
  50.     }  
  51. }  


    在这个小案例中,运用了速度向量的方向来判断是向上碰撞还是向下碰撞。作为《向量在游戏开发中的应用》这个小系列文章的第一篇,就用这个非常简单的小案例来开始吧。后续会更精彩,但更新时间不定。


    本Demo使用的的开发工具是Unity5.0.1f1和VS2013,建议下载源码后用Unity5.0.1f1或者更高的版本打开。源码地址:http://download.csdn.NET/detail/wenxin2011/9453669


(二)

向量点乘的几何定义

设二维空间内有两个向量UV,它们的夹角为θ([0, π]),则内积定义为以下实数: 
向量点乘的公式:U·V = **|U||V|**cosθ 
根据该公式可以退到下面三条结论: 
1. U·V > 0,表示向量UV之间的夹角小于90度(锐角) 
2. U·V < 0,表示向量UV之间的夹角大于90度(钝角) 
3. U·V = 0,表示向量UV之间的夹角为90度(相互垂直) 
4. U·V = 1,表示向量UV的方向相同 
5. U·V = -1,表示向量UV的方向相反

关于向量点乘的其他定义、规律以及应用有兴趣的朋友可以参考百度百科,这里就不再啰嗦了。本篇博客主要讲如何将以上三条结论应用到游戏开发中。

向量点乘的应用情景

  1. 根据向量点乘公式求两个向量的夹角大小。
  2. 根据结论1和2判断两个向量的夹角,可以用来限定两个向量的夹角。
  3. 根据结论3判断两个向量是否垂直。
  4. 根据结论4和5可以判断方位。可判断一个物体位于另一个物体的前面还是后面(或者左边还是右边)。

向量点乘如何判断一个物体在另一个物体的的前面还是后面呢?在《3D数学基础:图形与游戏开发》这本书中对这点做了很好的解释。下面贴出书中原话: 
这里写图片描述

既然能判断前面还是后面,当然也能判断左边还是右边了,只是选择的方向向量不同而已,这里留给读者自己思考。



(三)

向量叉乘的几何意义

  1. 叉积的长度 |a×b| 可以解释成以a和b为邻边的平行四边形的面积。
  2. 混合积 [a b c] = (a×b)·c可以得到以a,b,c为棱的平行六面体的体积。

关于向量叉乘的其他定义、规律以及应用有兴趣的朋友可以参考百度百科,这里就不再啰嗦了。本篇博客不想在叉乘其他地方纠结太多,只说一下如何用叉乘判断方向。

如何用向量叉乘判断方向

在网上有很多人将向量的应用总结为一句话:点乘判断角度,叉乘判断方向。关于点乘在上一篇文章中已经说完了,所以这里就说说如何用向量叉乘判断方向。 
我们都知道在一个平面内的两个非平行向量叉乘的结果是这个平面的法向量,这个法向量是有方向的,而这个方向可以用“右手定则”来判断。具体的判断方法是: 
若坐标系是满足右手定则的,当右手的四指从向量a以不超过180度的转角转向向量b时,竖起的大拇指指向是向量n的方向。如下图: 
这里写图片描述

在右手坐标系中,当向量ab作叉乘运算时, 利用“右手定则”可以知道:当法向量n跟某一坐标轴同向时,四指方向为逆时针方向;当法向量n跟该坐标轴反向时,四指方向为顺时针方向。同时“右手定则”要求转角不超过180度的方向,所以用叉乘判断的转向一定是最优转向(所要转动的角度最小,转动的代价也就最小)。在游戏中可利用这点来判断一个角色是顺时针还是逆时针才能更快速的转向一个敌人。

注意:Unity3D是左手坐标系,所以四指方向跟右手坐标系正好相反。

向量叉乘案例

接下来的案例将展示如何用向量叉乘判断一个方向盘的转向。老规矩,先上个案例的效果图: 
这里写图片描述

案例源码:

using UnityEngine;using System.Collections;public class VectorCrossDemo : MonoBehaviour {    private GameObject wheelObj;    private Vector3 wheelPos = Vector3.zero;     private Vector3 oldVec = Vector3.zero;    private Vector3 currVec = Vector3.zero;    // Use this for initialization    void Start ()     {        wheelObj = GameObject.Find("Wheel");        if(null != wheelObj)        {            wheelPos = wheelObj.transform.position;        }    }    // Update is called once per frame    void Update ()     {        if (Input.GetMouseButton(0))        {            var ray = Camera.main.ScreenPointToRay(Input.mousePosition);            RaycastHit hit;            if (Physics.Raycast(ray, out hit))             {                if(hit.transform.name.Equals("Wheel"))                {                    RotateWheel(hit.point);                }            }        }    }    void RotateWheel (Vector3 pos)    {        currVec = pos - wheelPos;//计算方向盘中心点到触控点的向量                  Vector3 normalVec = Vector3.Cross(currVec, oldVec);//计算法向量        float vecAngle = Vector2.Angle(currVec, oldVec);//计算两个向量的夹角             // 使用“右手定则”可知,当大拇指方向指向我们,四指方向为逆时针方向;        // 当大拇指远离我们,四指方向为顺时针方向。        // 这里叉乘后的法向量平行于z轴,所以用法向量的z分量的正负判断法向量方向        if (normalVec.z > 0)// 和z轴同向,则顺时针转        {            wheelObj.transform.Rotate(Vector3.forward, -vecAngle);// 顺时针转        }        else if (normalVec.z < 0)//和z轴反向,则逆时针转        {            wheelObj.transform.Rotate(Vector3.forward, vecAngle);// 逆时针转        }        oldVec = currVec;//赋值    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

本Demo使用的的开发工具是Unity5.0.1f1和VS2013,建议下载源码后用Unity5.0.1f1或者更高的版本打开。源码下载

转自:http://blog.csdn.net/wenxin2011/article/details/51088236


0 0