XNA 中的驾驶模拟
使用 XNA Game Studio 2.0 开发驾驶模拟器的教程

引言
这是我们旨在制作的 3D 驾驶模拟游戏系列教程的第一部分。随着后续教程的深入,我们将通过添加图形、物理和游戏玩法方面的新功能来让游戏变得更酷。本教程的目的是模拟一辆车在一个无限大的平面上行驶。我知道这与现实世界不完全一样。但我们将在后面的教程中变得更逼真。现在,请想象世界是一个完美的平面,没有障碍物。
背景
本文不适合绝对初学者。需要对 XNA Game Studio 2.0 有基本了解。但如果你是初学者,我建议如下:
- 下载并安装 Microsoft Visual C# 2005 Express Edition 及其 SP1 更新,以及 XNA Game Studio 2.0。所有这些都可以从 www.microsoft.com 免费下载。
- 仔细阅读“XNA Game Studio 文档”,特别是文章“你的第一个游戏:2D 中的 Microsoft XNA Game Studio”和“更进一步:3D 中的 XNA Game Studio”。这些教程都相当容易理解。
- 阅读 Riemer 的 XNA 教程。阅读所有 3 个教程将让你对 XNA 有非常好的理解。但现在,前两个教程已经足够了。四元数(Quaternion)的概念应该被清楚地理解。
要理解这些公式,需要对牛顿力学和几何有合理的了解。
Using the Code
应用程序被分为三个类:Game1
、Stuff
和 Car
。Stuff
类代表可以在屏幕上渲染的任何物体。Car
类是 Stuff
类的特化,代表一辆 Car
。Game1
类是主应用程序,使用 Stuff
和 Car
的对象。
让我们首先了解 Stuff
类。如果你阅读了代码,你会发现我已经尝试封装了在屏幕上存储任何物体的信息并进行渲染所需的一切。我只提供成员变量和函数的概述,因为代码相当直观。
- 成员
viewMatrix
和projectionMatrix
是static
的,因为它们对所有可渲染对象都是相同的。 - 成员
position
和rotation
分别代表物体在 3D 空间中的位置和方向。 - 成员
cameraRotation
代表相机的方向。存储此值对于实现相机延迟效果是必要的。 - 成员
model
代表物体的 3D 模型。 - 函数
UpdateCamera
根据汽车的位置和方向更新viewMatrix
。 - 函数
Draw
将 3D 模型渲染到屏幕上。
现在到了最难的部分——理解 Car
类。这里有两个概念在起作用。第一个是踩下油门、松开油门和刹车对汽车运动的影响。第二个是如何在方向盘向左或向右转动时汽车进行转弯。
第一个概念。在任何时刻,汽车都处于某个档位,由变量 gear
表示。车辆的速度决定了 gear
。我们在变量 topSpeeds
中存储了每个 gear
的最高速度。gear x
的最高速度也是 gear x+1
的最低速度。每个 gear
的加速度都不同。
public void Accelerate()
{
free = false;
if ((gear<7)&&(speed > topSpeeds[gear]))
{
gear++;
}
if (gear == 0)
{
acceleration = accelerations[1];
}
else if (gear < 7)
acceleration = accelerations[gear];
else
{
gear = 6;
acceleration = 0;
speed = topSpeeds[6];
}
}
现在,当踩下刹车时,其效果是施加一个负加速度,其大小等于该 gear
的加速度加上一个常数,如下所示。这里您还必须考虑倒档 gear
以及倒档的最大速度。
public void Brake()
{
free = false;
if((gear>0)&&speed<=topSpeeds[gear-1])
{
gear--;
}
if ((gear == 0) && (speed < -maxReverseSpeed))
{
acceleration = 0;
speed = -maxReverseSpeed;
}
else
{
acceleration = -accelerations[gear]-6.7f;
}
}
同样,当松开油门时,也会施加一个负加速度,其大小与该 gear
的加速度相同。最终 car
会静止。
public void Free()
{
free = true;
if ((gear>0)&&(speed < topSpeeds[gear - 1]))
{
gear--;
}
acceleration = - accelerations[gear];
}
为了确定汽车在每个时刻的新位置,以下代码放在 Update
方法中。
float speed = speed + acceleration * time;
float dist = speed * time;
Vector3 addVector = Vector3.Transform(new Vector3(0, 0, -1), rotation);
position += addVector * dist;
为了确保当油门和刹车都不踩时汽车最终会停下来,使用了这段代码。
float newSpeed = speed + acceleration * time;
if ((free == true) && (newSpeed * speed <= 0.0f))
{
gear = 1;
acceleration = accelerations[gear];
speed = 0.0f;
}
else
speed = newSpeed;
现在让我们了解汽车是如何转弯的。当驾驶员向左或向右转动方向盘时,平面表面与汽车的本体形成一个角度 theta,如图所示。Theta 应该从 0 均匀增加到一个最大值。同样,当方向盘自由时,theta 最终应该变为零。这方面的代码如下:
if (turn == 0)
{
float newAngle = steerAngle;
if (steerAngle < 0.0f)
{
newAngle = steerAngle + time * steerSpeed;
}
else if (steerAngle > 0.0f)
{
newAngle = steerAngle - time * steerSpeed;
}
if (newAngle * steerAngle < 0.0f)
{
steerAngle = 0.0f;
}
else
steerAngle = newAngle;
}
else
{
if (turn == -1)
{
float newAngle = steerAngle - time * steerSpeed;
if (newAngle < -maxSteerAngle)
{
steerAngle = -maxSteerAngle;
}
else
{
steerAngle = newAngle;
}
}
else
{
float newAngle = steerAngle + time * steerSpeed;
if (newAngle > maxSteerAngle)
{
steerAngle = maxSteerAngle;
}
else
{
steerAngle = newAngle;
}
}
}
既然我们已经了解了轮胎是如何转弯的,让我们来看看汽车是如何转弯的。仔细看图。

您会立即理解常数 HD
、VD
和 L
的含义。O
是 3D 模型 的原点。我们假设 car
将绕标记为“转弯中心”的点以半径 r
旋转。可以使用三角学计算 r
的值。
float x = (VD / Math.Abs((float)Math.Tan(steerAngle)) + HD / 2);
float r = (float)Math.Sqrt(x * x + L * L);
现在我们有了半径。可以找出 car
旋转的角度,并创建相应的 Quaternion
。
float theta = speed * time / r;
if (steerAngle < 0.0f)
theta = -theta;
rotation = rotation * Quaternion.CreateFromAxisAngle(new Vector3(0, -1, 0), theta);
现在 car
的 position
可以按以下方式确定:
speed = speed + acceleration * time;
float dist = speed * time;
Vector3 addVector = Vector3.Transform(new Vector3(0, 0, -1), rotation);
position += addVector * dist;
其余代码基本直观。
我想感谢我的朋友 Tarang Bakshi,感谢他在游戏物理和想法方面提供的帮助。
历史
- 2008 年 9 月 14 日:初始帖子