Unity3D小车驾驶模拟:灯光阴影、车辆控制与碰撞检测
简介面向Unity3D学习者和课程设计人群的小车驾驶模拟系统完整工程涵盖三维场景搭建、车辆控制、UI交互与光照设置等模块可作为课设作业或入门练手的直观范例。压缩包内含课程报告Word、演示视频、可运行源文件及项目截图便于对照学习与二次开发。资源共2000个文件主要包含C#脚本、场景与预制体asset/prefab、模型贴图fbx/png/mat、音频wav及说明文档docx/pdf/md等整个压缩包约127.23MB。实验基于Directional Light定向光实现场景照明并采用Soft Shadow软阴影提升真实感场景由简单多边形搭建地面、建筑、道路与红绿灯从光源设置到场景布局覆盖了Unity基础制作流程对于理解光照系统、材质贴图和游戏逻辑较有帮助。目前已有1017人学习下载适合作为Unity课程设计、游戏开发入门及驾驶模拟类项目的参考模板。1. 基于Unity3D的小车驾驶模拟系统一个能交差也能扩展的课设骨架第一次打开这份“基于Unity3D的小车驾驶模拟系统”时我心里预期是一个几十G的庞大工程解压之后发现恰恰相反——一个地面、几栋建筑、几条路、几个红绿灯场景里没有过多模型资产却把unity3D小车游戏该有的东西都串全了。后来我理解了这类驾驶模拟系统的定位它不是商业游戏而是专门为“课程设计”准备的骨架重点在于把场景搭建、光照阴影、车辆控制、碰撞与视角跟随这条链路理顺让演示视频和课程报告都有内容可写。无论你是想直接拿它交差还是想在此基础上改成带计分、多关卡的小车驾驶项目这个资源包都能当底座。2. 灯光链路Directional Light 与 Shadow 的取舍逻辑2.1 Directional Light 为什么是驾驶场景的默认选项这个场景里只用了一盏 Directional Light没有补光也没有环境光探头。对很多第一次做小车驾驶模拟的开发者来说可能觉得不够“高级”但方向光的特性决定了它在这种露天场景里是最稳的选择。Directional Light 的定位是无限远光源它不会因为物体离得近而变亮或变暗也不会因为摆放位置不同而改变照射角度真正决定光照方向的只有它的 Rotation 参数。也就是说你把这盏灯放在 (0, 10, 0) 还是 (100, 100, 100)只要旋转角度一样画面结果就完全一样。所以我在调整这类场景灯光时第一件事不是看位置而是看旋转角度。常见做法是把灯旋转到类似 (50, -30, 0) 的角度这个角度能让地面得到一个偏斜的光照方向建筑能产生侧面阴影而不是正午那种完全垂直的顶光。顶光最大的问题在于道路和建筑侧面缺乏立体感整个画面会显得“平”课程设计视频里看起来也会缺少层次。Intensity 通常给到 0.8 到 1.2 就够了。如果场景里的物体都是浅色材质0.8 会比较柔和如果地面颜色偏深可以提到 1.2。超过 1.5 之后再调高画面会泛白阴影和亮部同时丢失细节属于这条链路上最普遍的误操作。2.2 Hard Shadow 还是 Soft Shadow按性能边界选不按肉眼喜恶选Shadow 属性是这个场景的第二个关键节点。Unity 给 Shadow 提供了 Hard Shadow 和 Soft Shadow 两个选项。Hard Shadow 的阴影边缘锐利像素感强每个阴影边界都像被刀切过Soft Shadow 则会在阴影边缘做一层模糊过渡视觉上更接近真实环境光下的阴影。单看截图几乎所有人都会觉得 Soft Shadow 更“真实”这也是这个项目最终选择 Soft Shadow 的原因。但我得提醒一句Soft Shadow 的消耗比 Hard Shadow 高不少尤其是场景里有多栋建筑、多个红绿灯同时投射阴影时。如果目标平台是 PC 或演示用笔记本Soft Shadow 没有问题如果你后面把场景搬到移动端或者在地图上加了大量小物件第一件要牺牲的就是 Soft Shadow 分辨率而不是光源数量。课程演示场景里物体数量在几十个以内Soft Shadow 是全场景统一阴影最合适的档位。另外要注意 Shadow Type 下面的 Strength 和 Resolution。Strength 默认是 1阴影非常黑配合 Unity 自带的 Ambient Color环境色略微提高可以缓解。这个项目里没有刻意为每个物体做过多的阴影设置所以整条链路其实是“一盏方向光 一套全局阴影 阴影距离调大”的极简组合。2.3 灯光参数速查表直接抄也能用的档位为了方便按这个项目搭场景我把常见参数整理成一张速查表。这里的数值不是死规范而是我在复现时验证过的起步档位。参数推荐值说明Light TypeDirectional场景光源唯一选择位置不影响效果Rotation(50, -30, 0)决定侧向阴影避免纯顶光Intensity0.8 ~ 1.2超过1.5画面泛白Shadow TypeSoft Shadows演示效果优先Shadow Strength0.7 ~ 1.01.0最黑配合环境色微降Shadow Distance80 ~ 120必须大于场景最长对角线的一半Shadow Distance 经常被忽略。它决定“多远的物体还在渲染阴影”如果场景地面铺到了 60 米宽Shadow Distance 只给 20 米你会看到远处建筑完全没影子像悬浮在场景里。这个参数在 Lighting 窗口Window Rendering Lighting的 Scene 选项卡下通常默认 50 左右1×1 的小场景没问题拉大地面后必须同步调大。为了配合 Soft Shadow我一般会把 Window Rendering Lighting Environment 里的 Ambient Mode 从 Skybox 切成 Color颜色取深灰蓝 (45, 52, 60) 左右。这样做的原因是场景里的天空盒如果不匹配方向光打出来的阴影会偏蓝或偏脏而单独切环境色能让阴影里带一抹冷色视觉更干净。操作路径在不同 Unity 版本里略有差异但关键词是 Environment Lighting。2.4 灯光验收的三个自检点影子方向、亮度和过曝灯光搭完先别急着做车先在 Game 视图切到 16:9从俯视 45 度角扫一遍场景。我每次必看三个点第一地面亮度是否均匀如果一块亮一块暗说明场景里混进了其它光源第二所有建筑的阴影方向是否一致方向光只有一个旋转角理论上全部朝一个方向出现多方向阴影就是有额外光源在捣乱第三路面有没有过曝过曝时道路看起来是一块纯白地面需要把 Intensity 降 0.1 再试。这三个自检点做完灯光链路基本就稳了。方向光阴影距离这种看不见摸不着的参数最容易成为最先踩进去的玄学问题。3. 场景搭建地面、道路、红绿灯与建筑的组装顺序3.1 先铺地面再摆模型Plane 尺寸与坐标归零搭建场景的第一原则是先地面后建筑。项目里的地面是一张巨大的 Plane不是 Terrain。Plane 的好处在于网格简单、像素对齐、生成阴影稳定适合城市道路这类几何风格的场景。Terrain 虽然能做地形起伏但小车驾驶模拟里大部分路面是平的用 Terrain 反而引入大量不必要的刷地形工作。Plane 默认尺寸是 10×10 米在 Scale 里直接改成 X8、Z8 可以达到 80×80 米的有效地面。注意此时 Plane 的 Inspector 中 Mesh Collider 要保留它是小车不穿透地面的第一道屏障。常见做法是单独建一个空物体命名为 Ground挂 Mesh Collider再让平面的 Renderer 保持默认这样即便后续要对地面做特殊碰撞区域可以不用重新建平面。地面坐标尽量归零Position 的 Y 保持 0。这样做的原因是后续所有建筑、红绿灯都要按某个基准面摆放如果地面在 Y0建筑基座高度直接取偶数值即可排除一大堆坐标计算误差。坐标归零这个习惯能直接减少后面“物体悬在半空”的返工次数。3.2 从 Cube 到建筑群道路宽度与碰撞几何的匹配建筑不需要复杂模型所有立方体基本都由 Cube 拉伸而成。窗和门通过材质贴图解决而不是用额外模型。一个常见结构是建筑主体CubeScale 设为 6, 8, 6房顶Cube 压扁Scale 设为 6.2, 0.4, 6.2比主体大一圈防止边缘缝透光如果需要阳台可以再叠一层小 Cube但注意数量控制否则阴影消耗会上去。道路的做法通常是一条横向 Cube。路面宽 4 米长度跟随场景跨度Scale 设为 40, 0.2, 4颜色用深灰材质。把路面铺到建筑之间时我习惯先按建筑群的网格位置把路网摆出来再回过头去调建筑位置而不是在建筑上死磕坐标。红绿灯是多边形素材里一个典型组合一个圆柱(Cylinder)做灯柱两个 Cube 一横一竖做横臂和灯箱底座三个小球Sphere分别代表红、黄、绿。三个球体用三种自发光材质或者用标准材质加 Emission 通道都可以。这种细节很值得抄用 Unlit 材质可以避免灯光把灯体颜色带偏画面更干净。因为灯光是方向光灯箱上的小球在光照下会产生自己的阴影如果想让车在红灯前有停车判断还需要在小球模型内部或旁边挂一个碰撞盒子。碰撞盒用 Box ColliderSize 填小球直径方便代码层判断撞到了哪个灯。3.3 空物体分组让 Hierarchy 不再变成黑匣子搭建过程里一定要开启空物体分组。我观察这个项目场景时发现它的 Hierarchy 层级很干净所有建筑、道路、红绿灯都有父物体。这一点在课程设计中经常被低估如果不分组后面挂脚本、调材质、统一做阴影排除时会非常痛苦。建议这样分组Scene_Root Ground Buildings Building_01 Building_02 Roads Road_01 Road_02 TrafficLights TrafficLight_01 RedSphere YellowSphere GreenSphere Post BoxCollider Props Trees Streetlights每新增一个物体就拖进对应层级而不是散落在根目录。这样做最大的好处是当你要删除一整片区域时只需要删除对应父物体不需要逐个选中当你要给所有建筑同时换材质时也只需要展开父物体批量改。课程设计做到后期Hierarchy 就是你的记忆地图分组越清晰改东西越快。3.4 贴图与材质避免 UV 拉伸的几个尺寸习惯多边形素材的贴图是最后一道工序。很多人搭完模型后随手把贴图拖到材质球上结果墙面的窗户被拉成一道斜线。这里的关键是理解 Unity 的 Cube UV 映射Cube 的每个面默认映射整张贴图如果贴图本身比例不合适透视会变形。经验做法是墙体贴图用 2:1 或 1:1 的画布比例路面贴图用长条形比如 512×128并平铺成 Tiling X2、Y1。常见做法是在材质球上只调 Tiling不调 Offset这样不仅避免拉伸也让道路中线之类的标记能重复出现。对于灯柱这种细节圆柱干脆用纯色材质不为了一点点细节引入一张会被大幅拉伸的纹理。地面的大平面最容易翻车。一张 1024×1024 的沥青纹理平铺到一个 80×80 米平面上纹理密度会低到看不出细节这时把 Tiling 从 1,1 调到 4,4 或 8,8 就可以获得更密的路面颗粒感而不需要换更大的贴图。简单说模型越大的物体Tiling 越大这是多边形场景贴图最实用的经验。4. 小车驾驶逻辑车辆控制脚本、碰撞检测与摄像机跟随参数4.1 车辆控制脚本WASD 输入、速度上限与转向节流场景是骨架车是灵魂。这个项目里的小车控制应该是最常见的 Kit 思路键盘 WASD 控制前后左右。我也看到过很多课程设计把车写成 Transform.Translate 直接移动那会使车毫无物理感。更稳妥的方案是给车挂 Rigidbody再配合一个 C# 脚本去控制速度。下面是复现场景时我整理的一份最小可跑脚本放在车体根物体上using UnityEngine; public class PlayerCarController : MonoBehaviour { [Header(速度参数)] public float maxForwardSpeed 25f; // 前进速度上限米/秒 public float maxReverseSpeed 10f; // 倒车速度上限 public float acceleration 12f; // 加速度 public float brakeDeceleration 20f; // 刹车减速度 public float turnSpeed 90f; // 转向角速度度/秒 private Rigidbody rb; private float currentSpeed 0f; void Start() { rb GetComponentRigidbody(); // 锁住 X/Z 旋转防止碰撞后车辆翻滚失控 rb.constraints RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; } void Update() { float horizontal Input.GetAxis(Horizontal); // 转向只在车有一定速度时生效否则原地打转 if (Mathf.Abs(currentSpeed) 0.2f) { transform.Rotate(0f, horizontal * turnSpeed * Time.deltaTime, 0f); } } void FixedUpdate() { float vertical Input.GetAxis(Vertical); float targetSpeed vertical 0f ? vertical * maxForwardSpeed : vertical * maxReverseSpeed; // 用 Lerp 逼近目标速度产生加速度感 currentSpeed Mathf.Lerp(currentSpeed, targetSpeed, acceleration * Time.fixedDeltaTime); // 注意Unity 6 以后属性名改为 rb.linearVelocity旧版本用 rb.velocity Vector3 velocity transform.forward * currentSpeed; velocity.y rb.velocity.y; // 保留刚体自身的垂直速度重力 rb.velocity velocity; } }这个脚本的逻辑分三层Update 里读取输入并处理转向FixedUpdate 里计算物理速度Start 里锁旋转。车辆不会因为撞到路边而乱翻滚也不会因为按一下 W 就瞬间飞到高速——速度变化由 Lerp 控制加速度的参数决定手感。turnSpeed 的单位是“度/秒”90 意味着按住方向键一秒转 90 度车在路口转弯时基本只用按 0.5 秒左右。如果你发现车在转弯时身体发飘第一反应不要加力而是检查 Rigidbody 的 Drag 和 Angular Drag。Drag 默认是 0我一般会给到 0.05 到 0.1Angular Drag 保持默认 0.05不然转弯后车头会持续旋转。质量保持默认 1 即可这台车没有载重概念。4.2 碰撞检测Box Collider 与 OnCollisionEnter 回调脚本控制的是速度真正阻挡车辆的还是碰撞体。车体本身至少要有两个 Collider一个 Box Collider 包住车身另一个 Capsule Collider 或 Sphere Collider 做保险杠或者直接用一个 Box Collider 把车身包住。我一般会把 Box Collider 的 Center 设在车壳模型 Y1 左右Size 略小于模型边缘避免模型已经碰墙而碰撞盒还没接触。路面和建筑也应该挂 Mesh Collider 或 Box Collider。Plane 挂 Mesh Collider 是最省事的方式但如果地面很大会增加网格碰撞计算直接用一个 Box Collider 把地面包住也可以。建筑如果都是 Cube 拉伸直接保留 Box Collider 自动生成碰撞形状天然是准确的长方体。碰撞反馈在脚本里通过 OnCollisionEnter 实现void OnCollisionEnter(Collision collision) { if (collision.gameObject.CompareTag(Building)) { // 撞上建筑时立即把水平速度清零车停下来 currentSpeed 0f; rb.velocity Vector3.zero; // Unity 6 中可换成 rb.linearVelocity } }tag 是 Unity 里的字符串标签需要在 Inspector 里给所有建筑父物体下的根物体打上 Building 标签。注意 OnCollisionEnter 只会响应挂在 Rigidbody 的物体与 Collider 之间的碰撞如果地面只挂了 Collider 而车没有 Rigidbody这个回调不会触发。这也是最常踩的坑之一脚本明明写好了车还是穿模原因就是车少了 Rigidbody 或者碰撞体缺失。很多课程设计会额外给每个红灯模型加一个 Box Collider配合 tag 判断“红灯区”。做法是给灯的碰撞物体打上 RedLight 标签在 OnCollisionEnter 里判断 tag 后减速停车。这种写法简单比去解析红绿灯状态机更适合短周期课设。4.3 摄像机跟随LateUpdate 与平滑插值最后一块拼图是摄像机。场景里默认的 Main Camera 如果放在固定机位无法跟随小车移动。比较标准的做法是给摄像机挂一个跟随脚本用 LateUpdate 做位置更新。LateUpdate 的时序在 Update 之后、渲染之前适合所有跟车视角因为车辆先动摄像机再追就不会出现画面滞后一帧的感觉。using UnityEngine; public class CameraFollow : MonoBehaviour { public Transform target; // 小车根物体 public Vector3 offset new Vector3(0f, 6f, -7f); // 相对小车的偏移 public float smoothSpeed 5f; // 越大约追得越快 void LateUpdate() { if (target null) return; Vector3 desiredPos target.position target.TransformDirection(offset); Vector3 smoothedPos Vector3.Lerp(transform.position, desiredPos, smoothSpeed * Time.deltaTime); transform.position smoothedPos; transform.LookAt(target.position Vector3.up * 1.5f); } }CameraFollow 的核心是把 offset 相对目标物体的朝向做一次 TransformDirection车头转时摄像机也会跟着转到车后。smoothSpeed 控制追尾滞后5 左右是比较舒适的跟车感给 10 则摄像机几乎贴在车尾转急弯时画面会晃给 2 则像行车记录仪滞后明显但对新手友好。LookAt 看向点加了一个 Vector3.up * 1.5让镜头盯着车身上半部而不是车底视野里会带出更多路面演示视频里效果更好。5. 避坑记录灯光阴影、碰撞体、贴图拉伸与坐标漂移的五个现场5.1 阴影突然整个消失现象场景搭完建筑有模型有贴图但地板上完全看不到影子好像所有物体都悬浮着。原因最常见的是 Shadow Distance 值太小。Unity 的阴影裁剪距离默认在 50 左右如果地面 80×80镜头拉高后建筑距离超出阴影裁剪范围影子就没了。另一种可能是在 Lighting 面板里误关了阴影。解决打开 Directional Light 面板把 Shadow Type 从 No Shadow 改回 Soft Shadows再打开 Lighting 窗口Window Rendering Lighting或 Project Settings Quality 里的 Shadow Distance 调到 100 或更大。如果还不行检查 Quality SettingsEdit Project Settings Quality里的 Shadows 是否被设置成 Disable。课程设计在两个地方都可能翻车排查顺序是先 Light、后 Quality。5.2 小车起步就掉穿地面现象点击 Play 后小车直接从路面掉下去悬在空中或落到地面以下。原因车有 Rigidbody但地面没有 Collider。Plane 的默认 Inspector 里其实自带 Mesh Collider可一旦你在场景里重新建了一个 Cube 拉伸当马路它默认是没有碰撞体的只是视觉地面。小车一启动重力把它往下拽。解决给所有路面和地板统一补碰撞体。最省事的方法是在地面物体上添加 Mesh Collider把 Mesh 选成 Plane 或 Cube 的网格或者像前面说的加一个 Box Collider 当碰撞层。记住一条规则凡是你希望车能开上去的物体至少有一个 Collider。5.3 贴到建筑上的图拉伸成花脸现象墙体正面贴图看着正常侧面却像被撕裂了一样窗框拉成斜线分辨率特别低。原因Cube 的 UV 映射是六面展开Scale 被拉长或压扁后贴图没有按新比例平铺。尤其是把一栋 6×8×6 的 Cube 拉伸成 12×16×2 时画面比例完全变了。解决优先用标准材质控制 Tiling不换贴图。选中材质球把 Tiling 从 1,1 调到 2,1 或 1,2让纹理按墙面尺寸重复。如果贴图本身分辨率只有 256×256直接换 512 或 1024 的版本比在画图软件里硬拉更省时间。重要提醒如果要拉伸单面模型比如立面墙考虑用 Plane 代替 Cube单面模型永远不会出现六面比例打架。5.4 转向方向反了倒车影像镜像现象按左方向键车往右转倒车时画面像镜像一样乱。原因模型自身的正方向轴不是 Unity 默认的蓝色前轴或者脚本里把 horizontal 的正负号搞反了。很多免费模型的前进方向是红色轴或绿色轴直接套 transform.forward 就会反向。解决先选中小车在 Scene 视图里开启 Local 坐标显示看蓝色轴指向哪。如果蓝色轴指向车尾旋转模型让它指向车头或者在脚本里把前进方向改写成 transform.right。最稳妥的办法是在车辆根物体下再包一层空物体用空物体的蓝色轴作为前进方向控制脚本挂在空物体上模型作为子物体旋转校正一次。5.5 演示视频录制时画面卡顿现象录出来的视频一卡一顿画面掉帧车转弯时镜头明显拖尾。原因录制软件抓的是 Unity 渲染帧如果场景里同时开着 Game 视图、Scene 视图或者开着多盏投影仪编辑器性能会被分摊还有一种情况是垂直同步没开帧率虚高但每帧时间不稳定。解决先把 Game 视图分辨率固定为 1280×720关掉 Scene 视图或缩小窗口再打开 Quality 里 VSync 为 1。如果仍然卡顺手加一个 Time.timeScale 0.5f同一个驾驶演示用 2 倍慢速录素材拿到剪辑软件里再倍速画质反而更稳。这一步放在最后一章细说。6. 进阶验收用时间缩放与固定机位拍出稳定演示视频课程设计能不能拿高分一半看画面稳不稳。你可能很难相信那些观感很流畅的驾驶演示视频很多不是现场一遍过的而是开着 0.5 倍速录制后期再拉回 1 倍速。Unity 的 Time.timeScale 可以直接控制整套模拟系统的运行节奏乘上 0.5 之后车子的加速度、转向速度、碰撞响应全部等比例变慢录制时每一帧的画面信息量不变但机器有更充裕的时间去渲染。具体做法是在 Play 模式下先暂停再在 Console 窗口的输入框里执行一句脚本或者直接给车辆控制脚本加两个调试键。我习惯用 F1/F2 切换因为不用打断播放if (Input.GetKeyDown(KeyCode.F1)) { Time.timeScale 0.5f; // 半速运行适合录演示 } if (Input.GetKeyDown(KeyCode.F2)) { Time.timeScale 1f; // 恢复正常速度 }录制之前固定机位也很重要。直接把 Main Camera 切到车辆上方俯视角度然后固定不动用鼠标在 Scene 视图里从远处拉近录制一段“绕车一周 上路行驶”的组合。更建议的是把 Game 视图的纵横比锁成 16:9不要用自由纵横比不然录出来的视频两侧黑边不一样。剪辑时把 0.5 倍速片段统一提高速度到 1 倍音频如果用了引擎声记得同步变速否则声音和画面会对不上。演示视频之外的部分这个资源包已经把关键步骤固化成可直接对照的演示视频和实验报告你只需要补上自己的改动说明。把它们和这套链路对照着看就能看出每个参数对应在哪个环节。从那以后我每做完一个模拟类课设都会强制走一遍同样的流程先搭地面、再布置方向光、挂好车体脚本、最后录半速演示。这套习惯帮我少改了大量报告配图希望帮到你。本文还有配套的精品资源点击获取