游戏开发中的数理基础

1.向量在游戏开发中的用途:
(1) 笛卡尔坐标系:
(2) 大部分游戏的Vector3这个数据结构多数用于两种表示:
一:表示一个方向
二:表示一个点
向量加减法的几何意义:
1.方向+方向 = 方向

    通俗的理解可以看成是物理学里的合力,想象一下现实中的称重,人有一个重量,由于重量在地心引力的作用下的方向都是向下的,1kg+1kg显然是等于2kg,因为它们的方向是一样的,显然相加即可,那么两个人扳手腕,扳手腕的公平性是两个人方向是相对的,也就是一个人的手腕方向刚好是和另一个人的手腕方向相反的,则它们的合力方向为 x=0; y=0 ; z=0;

2.点 + 方向 = 点

    例如一个人在踢球,假设球的坐标为(3,0,2),而这个人踢出去的速度方向为每秒(1,2,0) 则在下一秒的那个瞬间球的位置为 (4,2,2)

3.点 - 点 = 方向

    这个结论很显然的,比如我在(2,2,4) 你在(1,0,0) 那么我对于你的方向则是 (2-1,2-0,4-0) = (1,2,4) 

4.点 + 点 = 无意义(一般情况下不要这么做,闵可夫斯基这种运算可以用于一些碰撞检测的算法)

(3) 向量的模:也就是 向量的大小,向量的长度,利用勾股定理计算即可;

 
  可用于计算距离,计算几何体相交,例如有两个球体(Sphere),已知半径的位置,求他们是否相交,一般可以:PointA - PointB后计算该向量的长度是否小于两个Sphere的半径之和即可

    需要进一步优化的是,计算机计算sqrt是比较耗费cpu的,因为我们只需要知道球是否相交,而不是具体的距离为多少,我们可以只用算到 x²+y²+z² 这一步即可,然后在与 (A球体半径的+B球体的半径)的平方来做比较即可

(4) 单位向量: 指长度为1的向量,那么我们 将长度为n的的向量变成长度为1 的向量的这个操作叫做归一化(Normaliation) 
    
归一化的计算过程:假设单位向量为u,向量为v,模为m,直接用v乘以v的模的倒数即可:u= (1/m)*v

    它经常可用于表示一个角色移动的方向,假设每秒的速度为s,那么每帧移动的位置为 u*s*(1/fps)

    在只关心向量方向,不关心向量大小时,可以使用单位向量,例如求一个面的法线向量的时候

(5) 向量的点积:也可以称为 标量积或内积,点积的结果不是一个向量,而是一个标量(实数)
两个向量的点积结果是一个标量,此标量定义为两个向量中每对分量乘积之和: a·b=ax·bx+ay·by+az·bz

当然也可以写两向量的模相乘后再乘以两向量夹角的余弦: a·b=|a||b|cosθ

由此可以根据点积计算两个向量的夹角,可以用于判断角度: θ=acos((a·b)/(|a||b|)),可以进一步优化,利用上述的单位向量(归一化),这个不会影响角度计算: θ=acos(a·b)

点积的意义何在?

****可以判断两向量是否共线或垂直(假设a与b都是单位向量)
    共线且方向相同: a . b = 1

    共线且方向相反: a . b = -1

    垂直:a . b = 0 

    夹角小于90度的相同方向: a . b > 0 

    夹角大于90度的相反方向: a . b < 0 

****经常用于判断游戏里敌人是否在自己的前方,首先得出向量 v = 敌方position - 我方position 那么我方单位的正前方向量为forward
    若 v . forward > 0 则代表敌方在我前面, < 0  代表在我后边, = 0 在我旁边

****在生产生活中,点积同样应用广泛,利用点积可以判断一个多边形是否面向摄像机还是背向摄像机,向量的点积与它们夹角的余弦成正比,因此在聚光灯的效果计算中,可以根据点积得到关照效果,如果点积越大,说明夹角越小,则物体距离光照的轴线越近,光照越强;

****物理中,点积可以用来计算合力和功。若b为单位矢量,则点积即为 a 在方向b的投影,即给出了力在这个方向上的分解,功即使力和位移的点积,计算机图形学常用来进行方向性判断,如两矢量点积大于0,则它们的方向朝向相近;如果小于0,则方向相反;矢量内积是人工智能领域中的神经网络技术的数学基础之一,此方法还被用于动画渲染;

(6) 向量的叉乘:得到一个与这两个向量都垂直的向量,这个向量的模是以这两个向量为边的【平行四边形的面积】
v1和v2向量的叉乘运算:相应元素的乘积的和:v1( x1, y1,z1) x v2(x2, y2, z2) = (y1z2 - y2z1)i+(x2z1 - x1z2)j+(x1y2-x2y1)k

性质1:c⊥a,c⊥b,即向量c与向量a,b所在平面垂直
性质2:模长|c| = |a||b| sin< a,b>

运用:

    1.根据叉乘得到a b 向量的相对位置,和顺时针或逆时针方位

    2.简单的说:点乘判断角度,叉乘判断方向;当一个敌人在你的身后的时候,叉乘可以判断你是往左转还是往右转更好的转向敌人,点乘得到你当前的面朝向的方向和你到敌人的方向的所成的角度大小

    3.得到a b 夹角的正弦值,计算向量的夹角(0,90) 可以配合点乘和Angle方法计算出正负的方向;
    4.根据叉乘大小,得到a b 向量所形成的【平行四边形】的面积大小,根据面积大小得到向量的相对大小;
(7).你代码中的向量到底代表着什么含义完全取决于你的控制:它可以是一个位置,方向或速度,以下是游戏中常见的一些向量的用法:
1.位置:向量代表着真实位置与你的世界坐标原点(0,0,0)的一个偏移量
2.方向:向量看起来非常像一个箭头指着某个方向,它确实可以这么用,举个例子来说,如果你有一个指向南的向量,那么你可以把这个向量赋予你的所有单位作为它们新的方向,那么它们都将指向南,方向向量的一个特例是长度为1的向量,它也被称为归一化的向量或者简称为标准向量
3.速度:一个速度向量可以描述一个运动,在这种情况下,它描述的是特定时间内的位置的变化
(8) 举例:物体之间的距离:
如果在这个例子中,向量代表的分别是物体 A 和 B 的位置,那么 B - A 将是代表A 和 B 物体位置差的向量, B - A 所得到的结果将表示 A 位置移动到B位置所需的方向和距离:
(9) 举例:速度,大炮炮弹的 速度向量描述的是它下一秒将要移动的距离
position = (0, 10, 10) # 初始位置

velocity = (500, 0, 0) # 下一秒将要在x轴方向移动的距离

每秒钟要基于速度向量来更新一次炮弹的位置:

position += velocity # 更新位置

delta时间:上一次更新到这一次更新的时间差,因为delta时间代表的是逝去时间的一个时间差,所以我们可以用它来得到这次更新到上一次更新的这段时间内的物体移动速度所导致的位置差

position += velocity * delta

考虑重力和空气阻力和风的影响:在一个游戏中重力意味着什么呢?它会产生一个副作用,在物体向下的方向增加物体的速度

gravity_vector = (0, -2, 0)  # 每秒增加y轴向下的速度2单位

加入重力影响后:

velocity += gravity_vector * delta # 应用重力

position += velocity * delta # 更新位置

加入空气阻力:

velocity += gravity_vector * delta # 应用重力

velocity *= 0.5 * delta # 空气每半秒降低一次炮弹的速度

position += velocity * delta # 更新位置

空气会阻挡炮弹的前进进而减少它的运能,所以需要调整下炮弹在空气中前进的速度:
# 修正 动能/速度
velocity += gravity_vector * delta # 应用重力
velocity *= 0.5 * delta # 空气每半秒降低一次炮弹的速度
final_change_per_second = velocity + wind_force_per_second   # 所有作用力 = 动能+每秒风的恒定力
position += final_change_per_second * delta   # 更新位置
(10)  方向:向量A到向量B 的距离当然可以用来表示方向,但是在 向量长度无关紧要的时候,我们就把方向向量的长度缩减为1,这个缩减称为归一化,得到的向量称为标准向量
    一个标准向量代表的是一个角度,而没有实际位置移动相关的其他任何信息,如果我们用一个标量数字乘以一个标准向量,我们就得到一个方向向量,同时它的长度就是标量数字的大小;
(11)平面和法向量: 一个标准向量也可以用来描述一个平面所朝向的方向,你可以把平面想象成从一个特定点P出发的无限大的片,对这个片的旋转可以通过法向量N来精确描述出来,要旋转这个片/平面,你应该改变它的法向量
(12) 点积返回的结果与两个向量之间夹角的关系:
    请注意,上图中从1到0 以及 从0 到 -1 的变化不是线性的,而是遵循余弦曲线进行变化的,所以,要从点积的结果中得到一个角度,你需要对返回的结果调用反余弦:angle = acos(A dot B)
(13) 例子: 点到一个平面的最短距离:
    如果要得到某个点到一个平面的最短距离,首先计算出这个点到平面上任意一点的距离向量,不要对这个向量进行归一化,然后将其与平面的法向量相乘,得到的就是这个点到这个平面的最短距离;
(14) 判断这个点是否在这个平面上:上述计算公式中,如果结果等于0,那么这个点就在这个平面上;
(15) 判断一个向量是否与一个平面平行:我们已经知道,如果两个向量的点积等于0的时候,这两个向量就是垂直的, 所以当向量与平面的法向量的点积等于0的时候。那么这个向量就是与这个平面平行的;
(16) 判断线段是否与一个平面相交:让我们假设下,线段P1点开始到P2点结束,在平面上的一个特定点是SP而平面的法向量是SN
如果我们假想一个平面穿过线段的第一个点P1,那么要解决这个问题可以转换为计算哪个点(P2还是SP) 既更接近P1 又与SN更加平行;
dot1 = SN dot (SP - P1)

dot2 = SN dot (P2 - p1)

u = (SN dot (SP - P1)) / (SN dot (P2 - P1))    

//如果 u == 0 那么线段是与平面平行的 ; u <= 1,那么线段与平面相交 ; 如果 u > 1 ,那么线段与平面不相交

可以将线段的向量与 u 相乘得到精准的相交点: intersectionpoint = (P2 - P1) * u

2.抛物线效果:
(1)定义公式需要的变量:
        
/** 水平移动速度*/
        this._hMoveSpeedArea$ = [0.0005,0.001];
        /** 向上初始值 */
        this._upInitSpeedArea$ = [0.001,0.004];
        /** 水平阻力加速度 */
        this._hStopForceA$ = -0.0000008;
        /** 重力加速度 */
        this._g$ = -0.00001;
        /** 弹力衰减值 */
        this._bounce$ = 0.2;
        /** 最小速度 */
        this._minSpeed$ = 0.000001;

(2)随机box的水平速度和向上初始值:
        
/**  抛物线速度*/
        box.HmoveSpeed$ = Utils$.floatRange$(this._hMoveSpeedArea$[0],this._hMoveSpeedArea$[1]);
        box.UpinitSpeed$ = Utils$.floatRange$(this._upInitSpeedArea$[0],this._upInitSpeedArea$[1]);
        box.ySpeed$ = box.UpinitSpeed$;
        /**  抛物线方向*/
        box.dir$ = new Laya.Vector2(vPos.x - this._foodCenerX$, vPos.z - this._foodCenerZ$);
        Laya.Vector2.normalize(box.dir$, box.dir$);
        box.animT$ = 0;

(3)抛物线动画:在onUpdate中更新: deltaT  =  TimeManager$ . getInstance$ (). deltaTime$ ;
  
          box.animT$ += deltaT;
            vPos = box.transform.position;
            addV = this._g$ * deltaT;
            box.ySpeed$ += addV;
            vPos.y += (box.ySpeed$ * 2 - addV) * deltaT * 0.5;
            if(vPos.y <= 0.1 && box.ySpeed$ < 0) {   //向上反弹
                vPos.y = 0.1;
                box.UpinitSpeed$ *= this._bounce$;
                box.ySpeed$ = box.UpinitSpeed$;
            }
            addV = this._hStopForceA$ * deltaT;
            box.HmoveSpeed$ = Math.max(0, box.HmoveSpeed$ + addV);
            if(box.HmoveSpeed$ != 0) {
                horizontalMoveDis = (box.HmoveSpeed$ * 2 - addV) * deltaT * 0.5;
                vPos.x += horizontalMoveDis * box.dir$.x;
                vPos.z += horizontalMoveDis * box.dir$.y;
            }
            box.transform.position = vPos;
            if(box.HmoveSpeed$ == 0 && vPos.y <= 0.1 && Math.abs(box.UpinitSpeed$) <= this._minSpeed$) {//单个方块动画结束
                //动画结束的处理
            }

猜你喜欢

转载自blog.csdn.net/woshiyuyanjia/article/details/134777552