BlocosIA Wiki Editor

如何编程 3D 汽车和摩托(赛车和越野摩托)

深入 3D 赛车和越野摩托示例:载具物理、追逐视角、路径点(那些数字列表)、对手自动驾驶、圈数和山地。

1. 玩家变成载具

在这两个游戏里,你控制的不是一个走路的角色:玩家驾驶 积木会把 3D 世界里的玩家换成一辆汽车或一辆摩托。

• evt_bandeira
• m3d_jog_veiculo [VEIC: carro, COR: #3B6FD8]
• m3d_camera [MODO: perseguicao]
• m3d_jog_vel [VEL: 4.3]
• m3d_controles [MODO: sim]

在内部,载具保存着一个 速度(向前的速度),并且每一帧(每秒 60 次):

  • W 增加加速度;S 刹车(停下后则倒车);不按键时,摩擦力 会让车慢慢停下。这就是 惯性 的感觉。
  • A/D 改变方向,但转弯幅度 和速度成正比:停着的车不会转弯(和真车一样)。
  • 速度会变成向前的一步:x 改变 sin(方向) × 速度,z 改变 −cos(方向) × 速度。
  • 遇到 1 格 高的台阶,载具会爬上去;撞到墙会损失速度,并触发 当玩家的载具撞车时。
  • 摩托 加速更快、转弯更快,按空格可以跳;转弯时会倾斜,车轮会随速度转动。

追逐 摄像机待在载具后面,并且 带一点延迟 地转动(它在“追赶”车的方向),所以转弯看起来很平滑。

2. 对手:那些数字是什么?

在对手的代码里,你会看到这样一个列表:

36, 41, 46.1, 51.1, 56.2, 61.2, 66.3, 71.3, 76.4, 81.6, 86.6, 91, 94.3, 96.4, 97, 96.1, 93.9, 90.3, 85.9, 80.8, 75.6, 70.5, 65.5, 60.4, 55.4, 50.3, 45.3, 40.2, 35.2, 30.1, 25, 19.8, 15.1, 11.2, 8.5, 7.1, 7.3, 8.8, 11.8, 15.8, 20.6, 25.9, 31

它们是绿色对手 路径点(*waypoints*)的 x 坐标。第二个列表 路径 Z 存放 z 坐标。每个列表的第 1 个数字组成第 1 个点,第 2 个数字组成第 2 个点,依此类推:赛道一圈共有 43 个点,就像一路撒下的面包屑。

仔细读这个列表,你就能看出赛道的样子:

  • 36 → 91 每次增加 5:这是 上方的直道,车向右开(x 变大)。
  • 94.3 → 97 → 85.9 慢慢变大,到达 97(最右边的点)再变回来:这是 右侧弯道(半个圆)。在弯道中 x 变化不大,因为车更多是在 z 方向上移动。
  • 80.8 → 15.1 每次减少 5:这是 下方的直道,往左开回去。
  • 11.2 → 7.1 → 31:这是 左侧弯道(7.1 是最左边的点),然后回到起点附近。

这些数字从哪来? 赛道是一个“体育场”形状的椭圆:两条 52 格长的直道和两个半径为 18 的半圆(一圈大约 217 格)。程序沿着赛道走了一圈,每隔约 5 格记下一个点(217 ÷ 5 ≈ 43)。在直道上只是简单地相加;在弯道上使用余弦和正弦(x = 圆心 + 半径 × cos(角度))。每个对手在不同的 车道 上行驶(稍微靠内或靠外),所以三个列表很像,但不完全一样。这些列表以逗号分隔的文字保存,用 "," 分割文字 积木会把文字变成列表。

3. 自动驾驶(对手的积木)

• evt_bandeira
• m3d_corpo [FORMA: carro, COR: #3E9E3A, TAM: 100]
• m3d_corpo_fisica [MODO: sim]
• var_definir [VAR: caminhoX, VALOR: (arr_dividir)]
• var_definir [VAR: caminhoZ, VALOR: (arr_dividir)]
• var_definir [VAR: ponto, VALOR: 1]

• evt_receber
• ctl_repetir_ate [COND: (op_maior)]
  • m3d_corpo_dirigir [X: (arr_item), Z: (arr_item), VEL: 12]
  • ctl_se [COND: (op_menor)]
    • var_adicionar [VAR: ponto, VALOR: 1]
    • ctl_se [COND: (op_maior)]
      • var_definir [VAR: ponto, VALOR: 1]
      • var_adicionar [VAR: voltas, VALOR: 1]
  1. 以速度驶向 x z 会让车朝当前的点 一点一点地 转向(像方向盘一样)并前进。它不会瞬移,所以能转出漂亮的弯。
  2. 当 我到 x z 的距离 小于 3.5 格时,对手就到达了,转向 下一个点(点 + 1)。
  3. 超过最后一个点了?回到第 1 个点,并 增加一圈。
  4. 跑完 3 圈后,“完赛的对手”加 1。当你冲过终点线时,你的 名次 就是“完赛的对手 + 1”。

不同的速度(12 / 12.6 / 13.2)让对手有不同的水平。想要更难的对手?提高它的速度。

4. 做自己的赛道,开车录制路径

不需要数学!开着车绕你的赛道跑一圈,让一个脚本每半秒记下一个点:

• evt_tecla [TECLA: g]
• var_definir [VAR: caminhoX, VALOR: (arr_vazia)]
• var_definir [VAR: caminhoZ, VALOR: (arr_vazia)]
• ctl_repetir [VEZES: 60]
  • arr_adicionar [VALOR: (m3d_jog_pos), VAR: caminhoX]
  • arr_adicionar [VALOR: (m3d_jog_pos), VAR: caminhoZ]
  • ctl_esperar [SEG: 0.5]

然后在舞台上显示 caminhoX 和 caminhoZ 变量(或者用“说”),把这些数字用到对手身上。

5. 圈数、草地和撞车(你的车)

  • 防作弊计圈: 只有先经过 赛道中段(下方直道),再经过 黑白格终点线,才算一圈。这样就不能在线上来回开来刷圈。
  • 草地会减速: 游戏会检查车 下面 的方块(y − 0.5)。如果是草地,速度就会下降。
• ctl_sempre
  • ctl_se_senao [COND: (op_igual)]
    • m3d_jog_vel [VEL: 2.4]
    …:
    • m3d_jog_vel [VEL: 4.3]
  • 时间 和 最快圈速 使用 计时器 积木(侦测),它很精确。如果用等待 0.1 秒再加 0.1 的方法,会越来越慢,因为每次重复都会多花一点时间。

6. 摩托和山地

这座山是用代码搭出来的:山路从左往右爬升,高度大约是 8 + x × 0.2(每 5 格升高 1 格)。程序保证山路 从一列到下一列最多只升高 1 格,否则摩托会卡住。一边是石墙(让你留在山路上),另一边是 悬崖。

• evt_bandeira
• m3d_jog_veiculo [VEIC: moto, COR: #FF8C1A]
• m3d_camera [MODO: perseguicao]
• ctl_sempre
  • ctl_se [COND: (op_menor)]
    • m3d_jog_inicio
  • ctl_se [COND: (op_maior)]
    • m3d_jog_marcar
  • 掉下去了? 如果摩托远低于山路(低于 4 + x × 0.2),就会回到上一个存档点。
  • 存档点: 经过黄色标杆后(x 大于 41),把出生点设在玩家所在位置 会把新的起点保存在那里。
  • 侧面视角: 按 C。摄像机会从侧面看山,就像登山赛车类游戏一样。

7. 挑战

  • 做一条 8 字形赛道,并开车录制对手的路径(第 4 节)。
  • 做一个 加速:按下 Shift 时,“玩家速度 6”持续 2 秒。
  • 在越野摩托中,添加一个沿着山路点行驶的“幽灵”对手。
  • 把摄像机切换为 俯视,做一个遥控车游戏。

在编辑器中打开