zoukankan      html  css  js  c++  java
  • 处理模型——使用加速度控制速度

    问题

    你想让模型能够漂亮的加速而不是从静止直接提高到最大速度。图4-9显示了你想实现的这种加速过程。

    1

    图4-9 模型的速度时间图像

    解决方案

    通过添加加速度,你可以定义你的模型的速度变化有多快。加速度就是每帧速度的增加量。

    工作原理

    你需要保存模型的位置和旋转,这是因为你需要知道哪个方向是向前的。在本例中,你只是让模型绕着它的向上的y轴旋转并沿着X和Z轴移动。如果你想让模型只基于X和Z轴沿着一个表面移动可参加教程4-17。

    因为模型以后的速度取决于当前速度,所以你需要保存当前速度。你将存储速度矢量而不是标量。这个速度矢量包含模型当前前进的方向,它的长度表示速度的大小。所以在类中添加三个变量:

    Vector3 modelPosition = new Vector3(); 
    float modelYRot = 0; 
    Vector3 modelVelocity= new Vector3(); 

    你也可以定义模型的最大加速度和转弯速度:

    const float modelMaxAcceleration = 30.0f;
    const float modelMaxTurnSpeed = 0.002f; 

    在Update方法中接受自上一帧以来经过的时间为参数,检测模型移动的距离:

    float elapsedSeconds = (float)gameTime.ElapsedGameTime.Milliseconds / 1000.0f; 
    float forwardReq= 0; 
    float angleReq = 0; 
    if (keyState.IsKeyDown(Keys.Up)) forwardReq += 1.0f; 
    if(keyState.IsKeyDown(Keys.Down)) forwardReq -= 1.0f; 
    if (keyState.IsKeyDown(Keys.Left))angleReq += 1.0f; 
    if (keyState.IsKeyDown(Keys.Right)) angleReq -= 1.0f; 

    当模型向前加速时变量forwardReq为正,减速或向后加速时为负。变量angleReq表示模型左转还是右转。

    在光滑表面上加速

    下面的代码添加基本加速行为:

    Matrix rotMatrix = Matrix.CreateRotationY(angle); 
    Vector3 forwardDir = Vector3.Transform(new Vector3(0, 0, -1), rotMatrix); 
    velocity = velocity + elapsedTime * forwardReq *maxAccel *forwardDir; 
    modelPosition += velocity; 
    modelYRot += rotationReq * maxRotSpeed* velocity.Length(); 

    前两行代码计算模型当前的Forward矢量,这个矢量用来获得模型加速的方向。这个Forward向量是在向上y-轴上附加在默认的(0,0,-1) Forward方向上的旋转获得的。

    注意:本教程中的模型只能绕y轴旋转,所以Forward向量只是基于绕向上y轴的旋转。可参加教程2-3和2-4学习计算整个3D空间或四元数的旋转。

    接下来的代码计算速度。在前一个速度的基础上基于用户输入添加一个新的矢量。自上一帧经过的时间越长,越需要调整这个速度矢量。另外,加速度越大,越需要调整这个速度矢量。最后你还要考虑模型的最大加速度。将这三个因素相乘获得这一帧需要调整多少速度。因为你需要将一个Vector3添加到速度矢量上,要乘以带有forwardDir的这个值获取想要添加的矢量。

    注意:如果没有旋转,速度和moveDirection会指向相同的方向,只是简单使速度矢量变大让模型移动地更快。

    最后,这个Vector3添加到模型的位置,模型的旋转被调整。模型运动得越快,转向也越快。

    当使用这个代码时,你注意到有两个缺点。首先,模型将一直加速,它没有一个最大速度。你想如图4-9所示增加速度并终止于一个确定的最大速度。使用这个代码,模型将一直以相同的步进加速。

    第二,如果模型在一个方向上的速度很大,在旋转模型后,它可能仍在相同的方向。如果模型是在一个冰面上当然不错,但通常这并不是你想要的结果。

    添加摩擦力

    在真实情况中,模型的速度会因为模型与空气、表面之间的摩擦等慢慢减小。当你停止加速,摩擦力会让速度减小直至停止。当持续加速,摩擦力会导致速度增加到一个特定值不在增加。

    你可以通过减去前面的速度获取摩擦力。下面的代码添加了摩擦力:

    velocity = velocity * (1 - friction * elapsedTime) + elapsedTime * forwardReq *maxAccel *forwardDir; 

    两帧之间的时间越长,摩擦力的效果越明显,所以你要根据流逝时间乘以friction变量。

    让模型保持在向前方向运动

    虽然模型现在已经以一个比较自然的方式加速了,但在它旋转时仍有一点瑕疵(除非是在空间游戏或冰面滑行游戏中)。通常,你只想让模型沿着前进方向移动。

    你需要知道沿着Forward 方向上的速度矢量是多少。这可以通过点乘得到:它将Velocity (V) 矢量投影到Forward (F)上,如图4-10所示,并返回投影速度的大小。

    2

    图4-10 将速度矢量投影到Forward矢量上

    所以在更新速度后使用下列代码:

    float forwardSpeed = Vector3.Dot(velocity, forwardDir);
    velocity = forwardSpeed * forwardDir; 
    modelPosition += velocity * elapsedTime; 
    modelYRot+= rotationReq * maxRotSpeed * forwardSpeed; 

    forwardSpeed变量表示Velocity矢量在Forward方向上分量的大小,然后乘以Forward方向并将结果存储在一个新的Velocity矢量中。通过这个方法,你可以保证模型将沿着向前方向移动。

    注意:使用forwardSpeed变量的一个额外好处是当模型向前运动是它为正,向后运动时它为负,而velocity. Length ()总是正的。

    代码 Accelerate方法根据加速度调整模型的位置,速度和旋转,将它们和最大加速度和旋转速度相加。你还要获取用户输入和摩擦力变量。

    private float Accelerate(ref Vector3 position, ref float angle, ref Vector3 velocity,float forwardReq, 
    		float rotationReq, float elapsedTime, float maxAccel, float maxRotSpeed,float friction) 
    {
        Matrix rotMatrix = Matrix.CreateRotationY(angle); 
        Vector3 forwardDir= Vector3.Transform(new Vector3(0, 0, -1), rotMatrix); 
        
        velocity = velocity * (1- friction * elapsedTime) + elapsedTime * forwardReq * maxAccel *forwardDir; 
        loat forwardSpeed = Vector3.Dot(velocity, forwardDir); 
        velocity = forwardSpeed * forwardDir;
        
        modelPosition += velocity * elapsedTime; 
        modelYRot += rotationReq * maxRotSpeed* forwardSpeed; 
        
        return forwardSpeed; 
    }

    这个方法返回forwardSpeed变量,若模型向后运动则为负值。

    扩展阅读

    你可以通过允许乘以加速度施加在模型上扩展这个方法,例如,重力加速度,你可以加上这个加速度将总和作为forwardDir。

  • 相关阅读:
    hdu 相遇周期
    神 上帝 以及天老爷·(递推应用)
    简单n! (本来只是一个·简单的地推,只是大数问题有点纠结·,本人用数组简单模拟了一下)
    顺序表的应用(删除链表重复元素)
    vuecli下的vuex的极简Demo(实现加1减1操作)
    vuebus全局事件中心简单Demo
    vuecli构建的项目中请求代理与项目打包
    Java 中的 static 使用之静态初始化块
    having和where的区别
    char、varchar和varchar2,以及nvarchar和nvarchar2的区别与联系
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120142.html
Copyright © 2011-2022 走看看