ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从投篮Demo入门Unity3d:物理、碰撞与触发器实战

从投篮Demo入门Unity3d:物理、碰撞与触发器实战 简介这是基于Unity3D打造的一款篮球游戏Demo面向Unity初学者与想要了解3D运动游戏实现的开发者。项目中完整演示了篮球游戏的核心玩法包含篮球场、篮筐、篮球等3D模型借助物理引擎模拟投篮、碰撞与反弹通过C#脚本处理玩家输入、得分判定和游戏流程并集成了动画、音效及UI计分界面。压缩包共1207个文件以Unity场景(.unity)、预制体(.prefab)、C#脚本(.cs)、FBX/3DS模型、材质贴图(.mat/.png/.jpg)、音频(.mp3)为主要类型压缩后约46.65MB。目前已有943人学习下载。开发者可以基于该Demo深入理解场景管理、刚体与碰撞、事件触发、动画状态机等Unity核心机制快速上手小型3D游戏开发的完整流程。 我刚开始用Unity3d那阵儿和很多人一样上来就想做一款带球员动画、能打完整比赛的篮球游戏。结果卡在球员运球和投篮动画上整整一周连球都投不出去。后来我把目标砍到一个“把球投进篮筐”的Unity3d Demo——就是标题里这种简单篮球游戏反而两天就做出了能玩的东西。这个Demo的价值不在画面而在它把Unity3d最常用的几条链路都串起来了场景搭建、刚体物理、触发器判定、输入交互、UI反馈。如果你现在正拿着Unity3d不知道从哪下手或者想花一两个晚上做出一个能给别人展示的成果这个投篮Demo是非常合适的起点。不需要项目源码不需要美术资产编辑器自带的Primitive模型就够了。1. 为什么拿“投篮”当第一个Unity 3D练手项目1.1 Demo虽小链路完整很多人对“简单Demo”有个误解觉得不值得做。我实际做完才发现一个投篮场景的难度和体量刚刚好。先说难度。它比“Hello World”那种纯脚本运动复杂但又比做完整角色控制器简单。你在里面要处理的核心问题只有一个怎么让一个带刚体的球在合适的抛物线轨迹下穿过一个筐。这看起来简单真正做起来你会在无意中把Unity3d的物理系统、碰撞器、触发器、相机视角、甚至UI全过一遍。再说体量。整个场景我用的全是内置物体一个Plane当地板一个Sphere当篮球几个Cube拼成篮板和篮筐再配一个Canvas显示分数。从打开新项目到能投进第一球我实际用时大概是两个晚上。这种短周期的正反馈特别重要尤其是刚开始学引擎的时候拖得越久越容易弃坑。所以别小看这个“简单”的定位。它让你能看到一个游戏从零到“能玩”的完整过程而不是某个孤立的特效或功能。1.2 引擎选型为什么是Unity3d而不是UE5网上关于Unity3d和UE5的对比讨论一直很多。就这个项目而言选Unity3d的理由非常实际。UE5强在渲染、Chaos物理、大世界工作流。但用在这个篮球Demo上就像开着大货车去便利店买瓶水——货箱大是够大但启动项目、编译shader这些开销对你调试一个球的物理手感来说完全是多余的。更直白地说UE5默认的C和蓝图体系对新手调试这种“按住鼠标拖一下、球飞出去”的交互开发节奏明显更重。Unity3d用的是C#脚本挂到物体上就能跑改完参数不用重新编译整个项目编辑器的Play模式能立刻看到物理表现。你调整Bounciness、拖拽系数时几乎可以做到“改一改数值点一下Play”的即时反馈。这种快速迭代对搭建手感非常重要。另外这个Demo后续如果要发布到移动端Unity3d的跨平台导出基本是顺手的事。我也在标题相关热词里看到“unity3d视频流”“webrtc demo”这类方向说明Unity3d在除了游戏外的实时互动场景也有成熟方案但那是后话。对一个投篮游戏Demo来说选Unity3d就是选快速、直观、低门槛。2. 场景搭建先把场地和篮筐“立”起来2.1 用最基础的Primitive搭建场景先明确一个原则这个阶段不要纠结好看先把物理结构搭对。打开Unity3d新建一个3D项目后我按这样的层级结构搭场景Ground一个Plane缩放设成(2, 1, 2)放在原点。Basketball一个Sphere半径设0.3放在(0, 1, -5)附近。BasketRoot一个空物体专门用来管理篮筐整体方便整体移动。Backboard一个Cube缩放成薄板放在篮筐后方。RimLeft / RimRight / RimFront / RimBack四个细长Cube拼成一个矩形框作为篮圈。ScoreZone一个空物体之后放触发器组件。这种“空物体做根节点子物体做部件”的组织方式是Unity3d里的基本功。先把篮筐挂在一个空物体下后续想整体抬高或移位直接动BasketRoot就行不用分别改四个Cube的位置。篮筐高度我建议放在2.8米左右也就是Y轴坐标大约2.8。为什么是这个值真实篮筐高度是3.05米但Demo里玩家用鼠标拖拽投球视角往往偏上稍微低一点可以让命中率更容易被接受玩起来挫败感小。你要做硬核模拟再调回3.05也只是一个数字的事。这里还涉及一个细节篮球的尺寸。真实篮球直径约24厘米对应Unity3d单位就是0.24米半径。但Demo里我用0.3米半径视觉上更明显也降低命中难度。在练习阶段先保证“能玩”比“绝对真实”重要。2.2 物理材质参数和碰撞体的关键设置光有模型还不够要让它“像篮球”而不是“像铁块”物理材质是关键。我在项目中创建了两个Physic Material材质名主要参数作用BallPhysicsDynamic Friction 0.2Static Friction 0.3Bounciness 0.7让篮球落地能弹起来又不会像皮球一样乱跳RimPhysicsBounciness 0.3Friction 0.2降低球碰到篮筐后的“弹簧效应”然后把BallPhysics赋给篮球的Sphere ColliderRimPhysics赋给篮圈四个Cube的Box Collider。碰撞器这里有一个新手容易踩的坑篮筐不能用一个大Cube实心碰撞体来做不然球永远只能撞在筐边上根本穿不进去。所以篮圈必须由多个细长Cube围成一个空心矩形。细长Cube的推荐截面是0.06 x 0.06米长度大约1.2米按前、后、左、右四个方向拼接形成一个约0.8米见方的框。下方可以再放一个很薄的Cube当篮网底口但注意底口碰撞体不能把空心区域封死。最后是篮球的刚体设置。选中Basketball在Inspector里添加RigidbodyMass设1Drag和Angular Drag都保持默认0和0.05左右。最有用的一个参数是Collision Detection先保持默认Discrete等后面遇到穿模问题再改这样你能亲自体会到这个参数的意义比一开始就改掉要有收获得多。3. 投篮手感从鼠标拖拽到抛物线投射的完整链路3.1 拖拽投球 vs 点击自动投球我最早做的是“点击按钮自动投球”就是按一下空格球自动朝篮筐方向飞。逻辑很简单但玩起来非常无聊因为玩家完全没有参与感。后来改成拖拽投球按住鼠标左键向后下方拖拽松手时球朝鼠标移动的反方向飞出去。这个交互是从愤怒的小鸟那类抛物线游戏借鉴来的反馈直观玩家能感受到自己在控制力度和角度而不是只看一个自动动画。为什么不直接做成“鼠标点到哪球就抛到哪”呢因为那样的话投篮变成了“瞄准”而不是“校准力度”两次投不进之后玩家容易烦躁。拖拽方式里左右拖控制方向上下拖控制抛物线高度玩家有一种“我在调整出手”的感觉这种微操正是手感的重要来源。3.2 发射代码与“手感”的调参逻辑下面是我当时用的一段核心脚本挂在主相机或者一个空对象上。为了演示方便我直接在上面引用篮球的Rigidbodyusing UnityEngine; public class BallThrower : MonoBehaviour { public Rigidbody ballRb; public float launchSpeed 10f; public float backSpinForce 3f; private Vector3 startMousePos; private bool isDragging false; void Update() { if (Input.GetMouseButtonDown(0)) { startMousePos Input.mousePosition; isDragging true; } if (Input.GetMouseButtonUp(0) isDragging) { Vector3 endMousePos Input.mousePosition; Vector3 drag endMousePos - startMousePos; // 屏幕坐标Y向上所以取反后玩家往下拖球往上飞 Vector3 throwDir new Vector3(drag.x, -drag.y, 8f).normalized; ballRb.velocity throwDir * launchSpeed; ballRb.angularVelocity Vector3.left * backSpinForce; isDragging false; } } }这个脚本里有几个关键点值得展开说说。第一个是方向向量中那固定的“8f”是什么意思。因为篮球放在Z轴负方向篮筐在Z轴正方向所以方向向量的Z分量必须保证球会往篮筐方向飞。如果不加这8f只把鼠标拖拽的二维向量扩展成三维那么水平拖拽时Z分量可能是0球就会横着飞出去砸到旁边的地板上。固定一个Z方向的分量相当于给玩家一个“默认投向前方”的力X分量管左右偏转Y分量管抛物线弧高。这个设计不是物理真实但手感非常稳。第二个是launchSpeed的值。我给10这个值不是随便拍的。它表示球离开手时的初速度是10米/秒。配合Z方向8的固定分量实际到达篮筐位置大概需要1秒左右。玩家能从视觉上感知到球的飞行时间不至于快到看不清也不至于慢到像放慢动作。你可以把它调到8或12试试低于8球会显得很“飘”高于12大部分玩家会来不及调整出手点。第三个是backSpinForce。这给球施加一个绕X轴负方向旋转的角速度让球在飞行过程中像真实投篮那样向后旋转。没有旋转的球打到篮筐后反弹方向会很不可控有了后旋球碰到篮筐后沿时更容易“蹭”进去也就是常说的“颠进”。这个细节对进球率影响很大。4. 计分判定用Trigger实现不冤枉人的进球检测4.1 检测区怎么摆进球判定我一开始用的是碰撞检测也就是球碰到“篮圈”就得分。结果问题非常大球打到篮筐边缘弹飞也算得分球从底部穿筐也算得分。后来改成Trigger方案靠谱得多。具体操作是在层级结构里给BasketRoot再加一个子物体命名为ScoreZone。给它挂一个Sphere Collider把Is Trigger勾上。位置放在篮筐中心偏下一点半径设成0.35米左右比篮筐的空心区域稍微小一点。为什么用Sphere而不是Box因为篮球本身是球体球进入一个球形触发器区域时触发边界最自然不容易出现“半个身子已经穿过筐但还没触发”的错觉。半径0.35米是经验值比篮球半径0.3米略大保证只要球的中心点进了筐就能触发判定又不至于还没碰到篮筐就提前得分。4.2 防重复计分与方向校验如果只写一个OnTriggerEnter就加分数你会发现一个诡异的问题球停在篮筐里不掉下去时分数会一直加或者球落下来碰到Trigger又弹起来反复计分。所以必须做两件事方向校验和冷却。方向校验的思路是只承认“从上往下掉进筐”的球。在OnTriggerEnter里取球的刚体速度如果velocity.y小于0说明球正在下落才计分。这样一来球从筐底部被弹上来时即使触发了Trigger也不会被判分。private void OnTriggerEnter(Collider other) { if (other.CompareTag(Ball)) { Rigidbody rb other.GetComponentRigidbody(); if (rb ! null rb.velocity.y 0f !canScoreThisBall) { canScoreThisBall true; score; StartCoroutine(ResetScoreFlag()); } } }冷却我用了一个协程。进球后把canScoreThisBall置为true然后等待0.5秒后重置。这个0.5秒足够覆盖球掉出触发器的过渡时间又能阻止同一次进球被反复计数。你不用协程用一个普通的计时变量也行但协程写法更直观新手也容易看懂。还有一个隐藏细节篮球的Tag要设置成“Ball”。建一个自定义的Tag然后在进球判定的比较中用CompareTag。因为CompareTag比直接拿字符串比较效率高也避免拼写错误。很多教程图省事用if (other.name Basketball)一旦篮球克隆出多个实例名字带了(Clone)后缀判定立刻失效。用Tag是更稳的做法。5. 踩坑实录穿模、弹簧效应与“差一点”的真相5.1 快球穿筐物理采样频率的问题做到这步你以为投篮功能就完成了其实还有几个坑在等着。我最先遇到的是穿模抛出速度调到15之后球经常像鬼影一样直接从篮筐里穿过去碰都没碰到。原因在于Unity3d的物理引擎默认是离散碰撞检测每物理帧才计算一次碰撞球速太快时可能上一帧还在筐的左边下一帧就已经到了筐的右边碰撞被直接跳过。解决办法是把篮球Rigidbody的Collision Detection从Discrete改成Continuous Dynamic。这个模式会针对动态物体做连续扫描防止高速穿透。但注意一点Continuous Dynamic不是免费的性能开销比Discrete大。这个Demo场景里物体少无所谓如果你的场景里有几十个高速刚体就需要控制数量或者用Speculative模式替代。第二个办法是控制发射速度上限。我把launchSpeed上限卡在14超过这个值球速带来的问题会成倍增加而且玩家感知不到明显区别。5.2 弹簧效应物理材质与碰撞体厚度的博弈穿模解决之后又出现了一个更让人崩溃的问题球总是卡在篮筐边缘疯狂弹跳明明已经进了半个球却怎样都不肯掉下去。这个现象的根源是物理材质和碰撞体厚度的组合。最初我把BallPhysics的Bounciness设成了0.9球“弹性十足”碰到篮筐细长Cube的薄壁时会获得一个很大的反向反弹速度。又因为篮筐碰撞体很薄球每次反弹后都重新碰到同一边缘形成一种“抖抖抖就是进不去”的弹簧效应。调整方案是双管齐下。先把篮圈Cube的截面从0.04x0.04米加厚到0.08x0.08米给球一个更平滑的过渡边缘然后把Bounciness从0.9降到0.5左右。这样球碰到筐边时会有明显的减速和缓冲而不是像撞到弹簧一样被弹飞。你也可以在篮筐内部加一个朝下的弧形碰撞面引导球往下落但Demo阶段用加厚降低弹性的方案就够了。5.3 “永远差一点”其实是相机和辅助线的问题第三个坑是最搞心态的。有时候玩家明明觉得拖拽方向很正球却总是擦着篮筐边缘飞出去偏得不多但就是差一点。我一度以为是碰撞体积的精度问题排查到最后才发现问题出在相机视角上。我用的是正交相机Orthographic也就是说画面里没有近大远小距离感被严重压缩。球飞向篮筐时玩家无法判断它到底是偏左还是偏右自然容易“差之毫厘、谬以千里”。换成透视相机Perspective把Field of View设成60左右深度感立刻回来了。另外调试时强烈建议在场景视图里用Debug.DrawLine把拖拽方向和球的初速度画出来这样你一眼就能看出方向向量和实际飞行轨迹的偏差。我在发射脚本里加了一行Debug.DrawRay(ballRb.position, throwDir * 10f, Color.green, 2f);运行游戏后Scene视图里会显示一条绿色射线。如果射线方向没有对准篮筐那就说明方向计算有问题如果射线方向对准了但球还是不进则要检查球出手时的初始位置是否被其他物体干扰或者是相机位置导致视觉误差。6. 不用重新造轮子这个Demo还能长成什么样子6.1 计时连击让玩家有追分乐趣投篮Demo做到能投能计分已经算完成了一个核心循环。但如果你把它拿给朋友试玩大概率对方投两球就走人了。原因是没有目标压力。给它加一层“压力”其实很容易加一个60秒倒计时倒计时结束游戏结算。再加一个连击倍数连续命中不丢球分数翻倍。这样玩家会不自觉地在最后10秒疯狂出手游戏乐趣立刻上升一个档次。实现上就是用Canvas和TextMeshPro显示倒计时与分数在Update里做时间递减在命中回调里更新分数。连击系统则只是多一个consecutiveHits变量命中时累加落空时清零。这些都是Unity3d基础操作但它们能把一个“技术演示”变成一个“有可玩性的游戏”。6.2 移动端触控与音效这个Demo如果要发布到手机上把鼠标输入换成触控非常简单。Input.GetMouseButtonDown和Input.GetMouseButtonUp本身就能响应移动端的触摸事件Unity3d在这层做了兼容。你只需要注意触控的采样点可能要多点判定避免误触然后考虑把篮球数据变成一个Prefab每次投出前生成一颗新球。音效也是性价比极高的提升点。进球时播放一声“刷网”的音效打铁时播放砸框音效不需要多复杂的音频资源用Unity3d自带的AudioSource挂到摄像机上代码里命中时调一下PlayOneShot就行。玩家对声音的反馈比视觉更敏感这个细节能让Demo的完成度瞬间提升一个档次。6.3 动态篮筐与难度曲线如果还想继续扩展可以让篮筐定期左右移动或者把投篮距离从近到远分成几个点位每个点位分值不同。也可以做“随机篮筐出现”模式场上同时存在两个篮筐奖励分值不同玩家需要快速判断投哪个。这些扩展的共同点是核心的投篮物理和判定逻辑完全不用改只需要在合理的位置加一些随机数和UI逻辑。这恰恰说明这个Demo的架构是健康的——它用最简单的结构支撑起了后续所有可能性。我做完这个投篮Demo以后最大的感受是一个看似简单的游戏真正把“手感”调到让人舒服涉及的细节远超想象。从物理材质参数到相机角度从触发器半径到碰撞检测模式每一环都相互影响。Demo不是玩具它是你把Unity3d物理和交互原理吃透的一座桥。如果哪天你能让一个玩家连续投十分钟不觉得烦那说明你的引擎基本功已经过关了。本文还有配套的精品资源点击获取
返回列表