简介这是一份面向Unity3D初学者与游戏开发入门者的篮球游戏实战Demo基于C#与Unity可视化编辑器构建帮助读者理解一款3D交互游戏从场景搭建到发布优化的完整链路。压缩包共1207个文件约46.65MB以meta、info、bin等工程元数据与缓存文件为主同时包含cs脚本、unity场景、prefab预制体、mat材质、png与jpg贴图、fbx与3ds模型、mp3音效及dll依赖库覆盖场景管理、刚体物理、动画控制、UI计分与音频播放等模块。资源围绕投篮动作、篮球碰撞反弹、得分判定与球员庆祝等典型玩法展开可研究刚体参数调节、Animator Controller与Animation Clip的配合、输入监听与事件触发方式。目前已有949人学习下载适合作为练手项目对照目录结构拆解脚本逻辑与资源组织方式为后续独立开发打下基础。1. 一个简单的篮球游戏的Unity3d Demo从零到能玩的投篮原型很多人第一次搜「Unity3d简单小游戏项目」心里想的其实不是做一个完整商业游戏而是想找一个能跑起来、能改、能看懂的小 Demo用来验证自己的引擎操作能力或者当作课程设计、面试作品的起点。篮球游戏恰好是这类需求里最典型的题材规则简单、物理直观、交互反馈快一个投篮动作就能把输入、物理、碰撞、计分、UI 串成一条完整链路。这篇笔记就围绕「一个简单的篮球游戏的Unity3d Demo」这个目标把选型理由、场景搭建、投篮物理、计分逻辑和常见翻车点拆开讲清楚。适合刚学完 Unity 基础、想动手做一个可玩原型的人也适合想快速搭一个物理交互 Demo 去验证手感的熟手。读完你应该能自己复现一个能投篮、能进球、能计分的篮球小样并且知道哪些参数一动就会毁掉手感。2. 篮球 Demo 的场景搭建与物理选型为什么不用 CharacterController2.1 场景最小构成与 Rigidbody 的取舍一个能玩的篮球 Demo场景里最少要有四类对象地面、篮筐篮板 篮圈、篮球、以及一个负责发射的相机或手部节点。地面用 Plane 或 Cube 都行Cube 更好控制尺寸缩放成 10×0.2×10 就能当半场。篮板用 Cube篮圈用 Torus 或者几个 Cylinder 拼一个圆环注意篮圈要留出足够的内径否则篮球永远卡在圈上。篮球本身必须挂 Rigidbody这是整个 Demo 的物理核心。很多人会问为什么不用 CharacterController答案很直接CharacterController 是为角色移动设计的它不参与完整的刚体动力学篮球需要的是真实的弹跳、旋转、碰撞响应只有 Rigidbody 加 Collider 才能给出这种反馈。篮球的 Collider 用 SphereCollider半径和模型视觉半径对齐差 0.02 都会让球看起来「悬空」或者「陷进地板」。Rigidbody 的关键参数如下参数推荐值说明Mass0.6接近真实篮球质量太轻会飘太重弹不起来Drag0.05空气阻力默认 0 会让球飞得太远Angular Drag0.05控制旋转衰减太小球会一直转Use Gravitytrue必须开否则没有抛物线Collision DetectionContinuous Dynamic高速投篮时必须改否则会穿模InterpolateInterpolate让视觉运动更平滑Collision Detection 这一项是血泪经验。默认的 Discrete 在球速超过一定值时会直接穿过篮板和篮圈表现就是「球明明投进去了却穿出去」。改成 Continuous Dynamic 之后Unity 会对高速物体做连续碰撞检测代价是少量性能但对一个篮球 Demo 来说完全可以接受。物理材质也要单独建一个 Physic MaterialDynamic Friction 设 0.6Bounciness 设 0.7 左右。Bounciness 是弹性的核心0.7 意味着球落地后还能弹起七成高度接近真实篮球手感。如果设成 1球会永远弹下去设成 0.3球拍两下就死了投篮反馈会很差。2.2 篮筐碰撞体的隐藏坑篮圈看起来简单实际是最容易翻车的地方。如果你用 Torus 模型直接加 MeshColliderUnity 会把它当成一个凸包或者非凸网格处理篮球撞上去的响应经常是错的要么弹飞要么卡住。常见做法是用十几个小 BoxCollider 沿着圆环排一圈拼成一个近似圆环的碰撞体。这样篮球碰到的是一个个小方块物理响应稳定也不会出现穿模。具体做法是建一个空物体叫 HoopCollider下面挂 12 到 16 个子物体每个子物体加 BoxCollider位置按圆周均匀分布。半径取篮圈中心到管中心的距离每个 BoxCollider 的尺寸大概是 0.05×0.05×0.05。数量太少会有明显棱角太多没必要12 个已经足够让球感觉是圆的。篮板和篮圈之间要留一点缝隙不要让碰撞体重叠。重叠的碰撞体在物理引擎里会产生异常的分离力表现就是球碰到篮筐附近突然加速或者抖动。我一般会把篮圈碰撞体整体往篮板外侧偏移 0.02肉眼看不出来但物理上干净很多。提示场景搭好后先别急着写脚本把球放在篮筐正上方让它自由落体观察它穿过篮圈、碰到篮板、落地弹跳的全过程。这一步能提前暴露 80% 的碰撞体问题。3. 投篮输入与力度控制从鼠标拖拽到抛物线发射3.1 拖拽式投篮的输入处理最简单的投篮交互是鼠标拖拽按下鼠标记录起点拖动过程中计算方向和力度松开时给球施加一个冲量。这种方式不需要额外的 UI手感也直观。核心逻辑是屏幕坐标转世界方向再映射成力的大小。using UnityEngine; public class ShootController : MonoBehaviour { public Rigidbody ball; // 篮球刚体 public Transform shootOrigin; // 发射参考点 public float maxDragDistance 300f; // 屏幕最大拖拽像素 public float maxForce 12f; // 最大发射力度 private Vector3 dragStart; private bool isDragging false; void Update() { if (Input.GetMouseButtonDown(0)) { dragStart Input.mousePosition; isDragging true; } if (Input.GetMouseButtonUp(0) isDragging) { isDragging false; Vector3 dragEnd Input.mousePosition; Vector3 dragVector dragEnd - dragStart; // 拖拽距离映射为力度做归一化限制 float dragRatio Mathf.Clamp01(dragVector.magnitude / maxDragDistance); float force dragRatio * maxForce; // 方向屏幕向上为前左右为横向偏移 Vector3 dir new Vector3(dragVector.x, dragVector.y, 1f).normalized; ball.AddForce(dir * force, ForceMode.Impulse); } } }这段代码里dragVector是屏幕空间的拖拽向量dragRatio把它归一化到 0 到 1 之间再乘以maxForce得到实际力度。方向向量里 z 固定为 1保证球总是往场景前方飞x 和 y 分别控制左右和上下。ForceMode.Impulse表示瞬间冲量适合投篮这种一次性发力。参数上maxForce是最需要调的。太小球投不到篮筐太大球会飞过整个半场。我一般从 10 开始试根据篮筐距离微调。maxDragDistance决定手感灵敏度300 像素意味着你拖满整个屏幕宽度才能达到最大力度如果觉得太累可以降到 200。3.2 抛物线预览线的实现思路光有拖拽还不够玩家需要知道球会往哪飞。加一条抛物线预览线能大幅提升可玩性。原理很简单用Physics.Raycast或者手动积分模拟球的轨迹每隔一小段距离放一个点用 LineRenderer 连起来。手动积分的做法是从发射点开始速度向量按v g * dt更新位置按p v * dt更新循环 30 到 50 次每次记录位置。重力用Physics.gravity这样预览线和实际物理一致。注意预览线只在拖拽过程中显示松开后隐藏否则会干扰视线。void DrawTrajectory(Vector3 start, Vector3 velocity, int steps) { lineRenderer.positionCount steps; Vector3 pos start; Vector3 vel velocity; float dt 0.05f; for (int i 0; i steps; i) { lineRenderer.SetPosition(i, pos); vel Physics.gravity * dt; pos vel * dt; } }dt取 0.05 是经验和性能的折中太小点数不够看不出弧线太大弧线会失真。steps取 40 左右能覆盖从投篮点到篮筐的完整路径。如果发现预览线和实际球路对不上检查是不是在 Rigidbody 上加了 DragDrag 会改变实际轨迹而手动积分没有考虑它。注意预览线只是辅助不要让它变成「瞄准镜」。如果预览线太精确游戏会失去挑战性。我一般会把预览线的透明度调低或者只显示前 60% 的路径。4. 进球判定与计分逻辑触发器、碰撞体和状态机4.1 用 Trigger 做进球检测的正确姿势进球判定最直接的做法是在篮圈下方放一个 Trigger 区域篮球进入时加分。但这里有个经典翻车点篮球从下往上穿过篮圈时也会触发导致「从下面扔进去也算分」。解决办法是加一个方向判断只有球从上往下穿过时才计分。具体实现是在 Trigger 的OnTriggerEnter里检查球的 y 方向速度。如果ball.velocity.y 0说明球正在下落这时候才判定进球。同时要记录一个布尔值防止重复计分球在 Trigger 里停留多帧时只加一次分。using UnityEngine; public class ScoreTrigger : MonoBehaviour { public GameManager gameManager; private bool scored false; void OnTriggerEnter(Collider other) { if (scored) return; if (!other.CompareTag(Ball)) return; Rigidbody rb other.attachedRigidbody; if (rb ! null rb.velocity.y 0f) { scored true; gameManager.AddScore(2); } } void OnTriggerExit(Collider other) { if (other.CompareTag(Ball)) { scored false; // 球离开后重置允许下一次进球 } } }scored标志位是关键没有它的话球在 Trigger 里每帧都会触发OnTriggerEnter的变体分数会疯涨。OnTriggerExit里重置标志位保证下一次投篮还能正常计分。AddScore(2)里的 2 是分值三分线外可以改成 3这取决于你有没有做距离判断。Trigger 的 Collider 要设成 Is Trigger尺寸比篮圈内径略小一点这样球只有真正穿过篮圈才会触发。如果 Trigger 太大球擦着篮圈边缘也会算分手感会很假。4.2 计分 UI 与状态重置计分 UI 用 Canvas TextMeshPro 就行不需要复杂。GameManager 里维护一个int score每次加分后更新 UI 文本。球落地后或者出界后要重置到发射点否则玩家得自己去捡球体验很差。重置逻辑放在球上检测球的速度持续低于某个阈值超过 1 秒或者球掉到某个 y 值以下就把它传送回发射点同时清零速度和角速度。注意重置时要先把 Rigidbody 的velocity和angularVelocity设为零再改position否则物理引擎会在下一帧把球拉回去。public class BallReset : MonoBehaviour { public Transform resetPoint; public float resetY -5f; private Rigidbody rb; void Start() { rb GetComponentRigidbody(); } void Update() { if (transform.position.y resetY) { rb.velocity Vector3.zero; rb.angularVelocity Vector3.zero; transform.position resetPoint.position; } } }resetY取 -5 是经验值取决于你的场景高度。如果地面在 y0球掉到 -5 说明已经出界了。重置点一般放在投篮者手部位置高度 1.5 到 2 米之间符合真人投篮高度。5. 篮球 Demo 常见问题排查从穿模到手感玄学5.1 球穿过篮板或篮圈现象投篮后球直接穿过篮板或篮圈没有碰撞反馈。原因Collision Detection 还是默认的 Discrete高速物体在帧与帧之间跳过了碰撞体。解决把篮球 Rigidbody 的 Collision Detection 改成 Continuous Dynamic同时确认篮板和篮圈的 Collider 不是 Trigger。如果还穿检查篮圈是不是用了 MeshCollider 且没有勾选 Convex非凸 MeshCollider 在高速下表现很差换成多个 BoxCollider 拼接。5.2 球在篮圈上抖动或卡住现象球碰到篮圈后剧烈抖动或者直接卡在圈上不掉下来。原因篮圈碰撞体之间有重叠物理引擎在重叠区域产生了冲突的分离力。解决检查篮圈碰撞体的间距确保相邻 BoxCollider 不重叠。如果用的是 Torus 加 MeshCollider换成拼接方案。另外把球的 Rigidbody Interpolate 设为 Interpolate能减少视觉抖动。5.3 投篮力度忽大忽小现象同样的拖拽距离有时候球飞得远有时候飞得近。原因Input.mousePosition在拖拽过程中受到帧率影响如果直接在Update里取不同帧率下拖拽向量的计算会有偏差。解决把输入处理放到FixedUpdate里或者用Time.deltaTime做补偿。更稳妥的做法是记录拖拽的起始和结束位置只在松开时计算一次力度中间不做实时力度更新。5.4 进球判定不触发或重复触发现象球明明进了但不加分或者一次进球加了好几分。原因Trigger 的尺寸不对或者scored标志位没有正确重置。解决把 Trigger 的 Collider 设成球体半径比篮圈内径小 0.05位置在篮圈正下方。确认OnTriggerExit里重置了标志位并且球的 Tag 确实是 Ball。如果球在 Trigger 里弹跳多次可以在加分后加一个短暂的冷却时间比如 0.5 秒内不再检测。5.5 预览线和实际轨迹不一致现象预览线显示能进实际投出去偏了。原因手动积分没有考虑 Rigidbody 的 Drag或者预览线的初始速度和实际发射速度不一致。解决在积分公式里加上 Drag 的影响或者把球的 Drag 设为 0让物理和预览完全一致。另外确认预览线的起点是球的发射位置不是相机位置。提示排查物理问题时把 Unity 的 Physics Debugger 打开能看到碰撞体的实际形状和接触点。很多「玄学」问题看一眼碰撞体就明白了。6. 让篮球 Demo 更耐玩的三个进阶技巧第一个技巧是加一个简单的力度条。拖拽时在屏幕上显示一个填充条长度对应dragRatio玩家能直观看到当前力度。实现方式是用 UI Image 的 fillAmount每帧更新。这个改动很小但手感提升明显尤其是对新手玩家。第二个技巧是给篮球加旋转。投篮时如果球不带旋转飞行姿态会很死板。可以在AddForce的同时加一个AddTorque让球绕自身轴旋转。旋转速度跟力度挂钩力度越大转得越快。注意AddTorque也要用ForceMode.Impulse否则旋转会慢慢衰减。ball.AddForce(dir * force, ForceMode.Impulse); ball.AddTorque(Vector3.right * force * 0.5f, ForceMode.Impulse);第三个技巧是做一个简单的连击系统。连续进球时分数递增比如第一球 2 分第二球 4 分第三球 6 分投丢后重置。这个逻辑只需要在 GameManager 里加一个comboCount进球时自增投丢时清零。连击能给玩家一个继续投的动力也让 Demo 从「能玩」变成「想再玩一次」。验证这些改动是否有效最直接的方法是录一段自己的操作视频回放看球的轨迹、计分触发和 UI 反馈。我自己的习惯是每次改完物理参数先投 20 次统计进球率如果进球率突然从 60% 掉到 20%那一定是某个参数改坏了。这个笨办法帮我省了很多后悔药。希望帮到你。本文还有配套的精品资源点击获取