Skip to content

问题考点

为什么做这个项目?

做这个赛车项目,主要是因为它很适合作为 Unity 入门到进阶的小型综合项目。

它能练到很多核心内容:车辆控制、物理碰撞、摄像机跟随、赛道设计、UI 显示、计时/得分、游戏胜负逻辑等。相比只做一个静态场景,赛车项目有明确目标和交互反馈,玩家能直接感受到操作、速度和挑战。

如果写成项目说明,可以这样说:

​ 本项目以赛车竞速为主题,旨在通过 Unity 实现一个具有基础驾驶、碰撞检测、赛道场景和游戏交互的赛车游戏。

​ 通过该项目,可以综合练习 Unity 场景搭建、物理系统、脚本编程、用户输入和游戏逻辑设计,提高对游戏开发流程的理解和实践能力。

简单说:这个项目好玩、目标清楚,而且能把 Unity 的很多关键知识点串起来。

racing_project_reason

程序化赛道生成具体是怎么实现的?

程序化赛道主要在 WorldGenerator.cs 里实现。它不是用 Unity Terrain,而是运行时自己创建 Mesh,把一段段圆筒隧道拼起来。

procedural-track-generation

核心流程

  1. 开局生成两段赛道Start() 里创建 beginPoints,然后生成 2 个 World piece,放进 pieces[0]pieces[1]。 位置间隔是:
c
dimensions.y * scale * Mathf.PI

对应 WorldGenerator.cs (line 34) 和 WorldGenerator.cs (line 76)。

  1. 每段赛道是一个圆筒网格CreateCylinder() 创建一个空物体,添加 MeshFilterMeshRendererMeshCollider,然后把 Generate() 生成的网格塞进去。 见 WorldGenerator.cs (line 131)。

  2. 顶点按圆筒分布CreateShape() 中,外层 x 表示圆周方向,内层 z 表示前进方向。 每个点的位置大概是:

c
float angle = x * Mathf.PI * 2f / xCount;

vertices[index] = new Vector3(
    Mathf.Cos(angle) * radius,
    Mathf.Sin(angle) * radius,
    z * scale * Mathf.PI
);

也就是把一排排顶点绕成圆筒,代码在 WorldGenerator.cs (line 179)。

用 Perlin Noise 做隧道起伏

初始圆筒太规整,所以它对每个顶点采样 Mathf.PerlinNoise(),然后沿着“指向圆筒中心”的方向把顶点往内推:

c
vertices[index] += (center - vertices[index]).normalized 
    * Mathf.PerlinNoise(pX, pZ) 
    * waveHeight;

perlinScale 控制噪声密度,waveHeight 控制起伏强度,offset 控制每段赛道的噪声偏移。对应 [WorldGenerator.cs (line 206)](/E:/Unity/UnityProjects/ClassCode/Racing car/Assets/Procedural Racing/Scripts/WorldGenerator.cs:206)。

两段赛道平滑衔接

它用 beginPoints 保存上一段赛道最后一排顶点。生成新赛道开头几排时,会把“上一段末尾形状”和“新噪声形状”做线性混合:

c
vertices[index] = 
    (perlinPercentage * vertices[index]) 
    + ((1f - perlinPercentage) * beginPoint);

这样不会在两段圆筒之间出现明显断层。对应 WorldGenerator.cs (line 217)。

  1. 三角面拼网格 顶点生成后,代码按每个小方格生成 2 个三角形,也就是每格 6 个顶点索引。 这一段在 WorldGenerator.cs (line 246)。
  2. 无限赛道刷新 游戏不是真的让车往前跑很远,而是赛道块向玩家移动。 当第二段赛道移动到 z <= 0,就销毁第一段,把第二段变成第一段,再生成新的第二段接在后面。 对应 WorldGenerator.cs (line 44) 和 WorldGenerator.cs (line 86)。
  3. 随机生成障碍和得分门 在生成顶点时,会随机决定是否在该顶点位置刷道具:
c
if(Random.Range(0, startObstacleChance) == 0)
    CreateItem(vertices[index], x);

CreateItem() 再随机选择 Gate 或障碍物,并让它朝向圆筒中心,最后挂到当前赛道块下面一起移动。见 WorldGenerator.cs (line 237) 和 WorldGenerator.cs (line 292)。

一个小细节:startObstacleChance 会随着生成新赛道逐渐减小,所以 Random.Range(0, startObstacleChance) == 0 的概率会变大,游戏越跑障碍越密。

物理碰撞是怎么做的?

racing_physics_collision

NOTE

这个项目的物理碰撞主要是靠 Unity 自带物理系统 做的,不是自己手写碰撞公式。

核心做法是:

  1. 赛车对象挂了 RigidbodyWheelCollider 在 Car.cs (line 8) 里可以看到:

    CAUTION

    • Rigidbody rb:让赛车参与物理计算
    • WheelCollider[] wheelColliders:负责车轮和地面的接触
    • Physics.Raycast:检测车轮下面有没有地面,用来显示草地效果/刹车痕
    • rb.AddForceAtPosition(...):车尾离地时加一个向下的力,让车更稳定
  2. 撞到障碍物会触发失败 在 Obstacle.cs (line 15) 里:

c
void OnCollisionEnter(Collision other){
    if(other.gameObject.transform.root.CompareTag("Player"))
        manager.GameOver();
}

意思是:当赛车和障碍物发生真实物理碰撞时,如果撞到的是玩家赛车,就调用 GameOver()

WARNING

  1. 穿过得分门用的是触发器 在 Gate.cs (line 19) 里用 OnTriggerEnter,它不是“撞上去挡住”,而是“穿过去检测到”。
  2. 翻车检测也用触发器 CarGameOverTrigger.cs (line 15) 判断车顶是否碰到地面,如果车顶碰到 "World piece",说明赛车翻了,也会游戏结束。

可以写成答辩说明:

本项目通过 Unity 的 Rigidbody、Collider、WheelCollider 和 Trigger 实现物理碰撞。赛车使用 Rigidbody 参与物理运算,车轮使用 WheelCollider 与地面接触;障碍物通过 OnCollisionEnter 检测真实碰撞并触发游戏失败;得分门和翻车检测使用 OnTriggerEnter 判断玩家是否通过或车辆是否翻倒。碰撞后由 GameManager 统一处理游戏结束、车辆散架和场景停止等逻辑。

项目难点是什么?

NOTE

这个项目的难点主要在于 赛车物理和游戏逻辑的配合

racing_project_challenges

可以从这几个方面说:

  1. 车辆控制不容易稳定 赛车要能转向、前进、贴合地面,还不能一转弯就翻车。项目里用了 RigidbodyWheelCollider 和额外的向下力来保持车身稳定。

  2. 碰撞检测要准确 障碍物碰撞、穿过得分门、车顶触地翻车,这些情况不能混在一起。项目里分别用了 OnCollisionEnterOnTriggerEnter 来处理不同事件。

  3. 游戏结束逻辑要统一 撞到障碍物或翻车后,要同时完成车辆散架、停止赛道移动、播放音效、显示失败界面等操作,所以需要 GameManager 统一管理。

  4. 赛道和车辆表现要协调 车在动,赛道也在运动和旋转,摄像机还要跟随。如果这些速度、角度、位置没有配好,游戏体验会很怪。

  5. 细节反馈比较多 比如轮胎痕迹、草地效果、得分音效、失败动画等,这些虽然不是核心功能,但能明显提升游戏完整度。

  6. Perlin Noise(柏林噪声)算法 来做程序化无限赛道生成。

在 WorldGenerator.cs (line 213) 里:

Mathf.PerlinNoise(pX, pZ)

它用柏林噪声改变圆柱赛道网格顶点的位置,让赛道表面有起伏变化。项目还用了“两段赛道循环”的方式:前一段离开视野后销毁,再生成新的一段接到后面,并用 beginPoints 做平滑过渡,所以看起来像无限赛道。

可以写成项目说明:

本项目的主要难点在于赛车物理控制、碰撞检测和游戏状态管理。赛车需要在高速移动和转向时保持稳定,同时要准确判断障碍物碰撞、得分门触发和翻车情况。项目通过 Rigidbody、WheelCollider、Collision 和 Trigger 等 Unity 物理机制实现检测,并由 GameManager 统一处理得分、失败、车辆散架和场景停止等逻辑。

本站访客数0总站访问量0本页访问量0