尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Unity物理驱动翻转游戏:C#实现刀体旋转与碰撞判定

Unity物理驱动翻转游戏:C#实现刀体旋转与碰撞判定 简介这是一份基于Unity引擎开发的休闲游戏《Flippy Knife 翻转刀》完整C#项目源码面向Unity初学者与游戏开发实践者提供可运行、可调试、可二次开发的物理模拟类刀具投掷游戏范例。资源包共2000个文件涵盖46个核心C#脚本实现翻转逻辑、碰撞判定与UI交互、69个Prefab预制体含刀具、目标板与特效、121张PNG纹理与8个Shader着色器支撑Beresnev.Design风格的炫酷视觉表现以及动画控制器、物理材质和音频资源整体压缩包仅53.94MB轻量易导入。已有180人学习下载适合通过真实项目理解Unity物理系统、动画状态机、资源打包与跨平台构建流程。读者可直接运行体验7种游戏模式、120刀具收集系统与50成就徽章机制并基于现有结构快速扩展新关卡、武器或特效是掌握休闲游戏开发全流程的优质实践素材。1. Flippy Knife 翻转刀不是“扔飞刀”而是 Unity 里用 C# 控制物理旋转与碰撞判定的轻量级休闲游戏逻辑你打开一个叫 Flippy Knife 的游戏看到刀子在木板上翻滚、插中目标、弹跳反弹——它看起来简单但背后没有预设动画帧所有翻转动作都由 Unity 的刚体Rigidbody和 Collider 实时计算刀身每次触板的朝向、是否得分、是否弹飞全靠 C# 脚本在每帧中判断碰撞点法线、角速度、接触面角度。这不是贴图轮播或 Timeline 播放而是典型的「物理驱动型休闲游戏」核心是让玩家通过点击/滑动时机控制刀尖下落相位而系统必须在毫秒级响应中完成旋转积分、碰撞过滤、状态跃迁。适合刚学完 MonoBehaviour 生命周期、想动手做第一个可交互物理小游戏的 Unity C# 学习者也适合已有项目想复用「刀体姿态解析」逻辑的开发者——比如把同一套翻转判定移植到投掷类 AR 应用或教育类力学演示工具中。标题里的「源码」二字很关键它意味着你能直接看到OnCollisionEnter如何区分「刀尖命中」和「刀背擦过」Quaternion.LookRotation怎么把物理朝向转成 UI 反馈以及为什么Rigidbody.angularVelocity.magnitude比transform.rotation.eulerAngles.z更可靠。2. 用 Unity Physics 和 C# 实现刀体翻转的核心三要素刚体配置、碰撞检测与朝向解算Flippy Knife 的「翻」不是视觉动画而是物理引擎对刚体施加扭矩后的自然演化「转」不是 RotateAround而是角动量守恒下的自旋惯性。要让刀子真实翻滚必须绕开常见误区比如给刀模型加 Animator 组件、用transform.Rotate()强行转动或依赖固定帧率更新 rotation。正确路径是让 Unity 物理系统接管运动C# 脚本只负责「触发」和「解读」。2.1 刀体预制件Prefab的刚体与碰撞器配置要点刀模型通常为细长圆柱或带刀柄的胶囊体。关键配置不在 MeshRenderer而在其子物体或根节点上的 Rigidbody 和 Collider// 示例刀体脚本中初始化刚体常放在 Start() 中 void Start() { rb GetComponentRigidbody(); rb.mass 0.8f; // 质量影响翻转惯性0.5~1.2 是常见调试区间 rb.drag 0.1f; // 空气阻力避免翻转过快失控 rb.angularDrag 0.5f; // 角阻力防止撞板后无限打转 rb.interpolation RigidbodyInterpolation.Interpolate; // 启用插值消除网络或低帧率抖动 rb.collisionDetectionMode CollisionDetectionMode.ContinuousDynamic; // 对高速翻转刀必备 }注意Collider 必须是BoxCollider或CapsuleCollider不能用MeshCollider性能差且易漏判。若刀身有明显刀尖建议将 Collider 设为 capsule中心偏移至刀尖位置并勾选isTrigger false。Rigidbody.useGravity true是基础但需配合rb.constraints RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezePositionZ锁定水平位移只允许 Y 轴下落绕 Y 轴自旋——这是 Flippy Knife 类游戏最稳定的约束组合。2.2 基于碰撞法线的「有效命中」判定逻辑刀撞木板时Unity 会触发OnCollisionEnter(Collision collision)。但直接读collision.contacts[0].point不够它返回的是碰撞点世界坐标而我们需要知道「刀尖是否朝向木板法线方向」。标准做法是提取刀体当前朝向transform.forward再与木板表面法线collision.contacts[0].normal做点积void OnCollisionEnter(Collision collision) { if (!collision.gameObject.CompareTag(TargetBoard)) return; Vector3 contactNormal collision.contacts[0].normal; Vector3 knifeForward transform.forward; // 刀尖方向 float dot Vector3.Dot(knifeForward, contactNormal); // dot 0.7 表示刀尖几乎垂直指向木板视为有效插入 if (dot 0.7f) { ScoreManager.AddScore(10); PlayInsertSound(); FreezeRotation(); // 插入后锁定旋转避免弹跳 } // dot -0.5 表示刀背砸板触发弹飞逻辑 else if (dot -0.5f) { rb.AddForce(Vector3.up * 8f, ForceMode.Impulse); // 向上弹起 rb.angularVelocity Vector3.zero; // 清除角速度防止乱转 } }2.2.1 为什么不用transform.up或transform.right因为transform.forward对应刀模型 Z 轴建模时刀尖应沿 Z 方向而up/right在翻滚中易受父物体影响。transform.forward是局部坐标系中最稳定的「指向性」向量。若模型导入时轴向不对应在 Unity Editor 中右键模型 →Configure…→ 设置Axis Conversion确保 Z 为刀尖方向。2.2.2 碰撞点法线为何取contacts[0]而非平均Collision.contacts是数组但 Flippy Knife 单次撞击通常只有 1~2 个接触点。取[0]是性能最优解若需更高鲁棒性如刀身倾斜擦过木板边缘可用contacts.Average(c c.normal)但需先using System.Linq并注意 GC 开销。2.3 用 Quaternion 解析翻转相位从物理朝向到游戏状态玩家关心的不是欧拉角数值而是「刀尖当前朝上/朝下/朝左/朝右」。直接读eulerAngles会导致万向节死锁尤其当 x/z 接近 ±90° 时。正确方式是用Quaternion.ToEulerAngles()后归一化或更推荐——用Quaternion.AngleAxis构造参考朝向再比对// 获取刀尖相对木板的「翻转相位」0°朝上90°朝右180°朝下270°朝左 float GetFlipPhase() { Vector3 localForward transform.InverseTransformDirection(transform.forward); // 将世界 forward 投影到木板局部 XY 平面假设木板为水平面 Vector2 phaseVec new Vector2(localForward.x, localForward.z); return Mathf.Atan2(phaseVec.y, phaseVec.x) * Mathf.Rad2Deg 90f; // 标准化到 0~360 } // 在 Update() 中每帧调用用于 UI 显示旋转进度条或触发连击 void Update() { float phase GetFlipPhase(); if (phase 350f || phase 10f) { // 朝上临界区 ShowReadyIndicator(); } }提示GetFlipPhase()返回值范围是 -180°~180°加 90° 并Mathf.Repeat可映射到 0~360°。实际项目中建议缓存transform.InverseTransformDirection结果避免每帧重复计算。3. Flippy Knife 源码中 C# 脚本的典型结构与关键参数表一个可运行的 Flippy Knife 项目至少包含 4 个核心 C# 脚本KnifeController主控、BoardManager木板生成与回收、ScoreManager分数与连击、InputHandler触摸/鼠标输入。它们不靠 GameObject.Find 或单例模式硬耦合而是通过 Inspector 拖拽引用符合 Unity 最佳实践。3.1 KnifeController.cs 的完整骨架与参数说明using UnityEngine; public class KnifeController : MonoBehaviour { [Header(Physics Settings)] public float throwForce 12f; // 点击发射力度影响初速度 public float maxAngularVelocity 15f; // 限制最大翻转角速防失控 public LayerMask targetLayer; // 仅与指定 Layer 的木板交互 [Header(Audio VFX)] public AudioClip insertSound; public ParticleSystem hitParticles; private Rigidbody rb; private AudioSource audioSource; void Start() { rb GetComponentRigidbody(); audioSource GetComponentAudioSource(); rb.maxAngularVelocity maxAngularVelocity; } void Update() { if (Input.GetMouseButtonDown(0)) { ThrowKnife(); } } void ThrowKnife() { rb.velocity Vector3.zero; rb.angularVelocity Vector3.zero; rb.AddForce(transform.forward * throwForce, ForceMode.Impulse); } void OnCollisionEnter(Collision collision) { if ((1 collision.gameObject.layer targetLayer.value) 0) return; // 此处插入 2.2 节的命中判定逻辑 Vector3 contactNormal collision.contacts[0].normal; float dot Vector3.Dot(transform.forward, contactNormal); if (dot 0.7f) { // 插入逻辑 audioSource.PlayOneShot(insertSound); hitParticles.transform.position collision.contacts[0].point; hitParticles.Play(); gameObject.SetActive(false); // 插入后禁用交由 BoardManager 回收 } } }3.1.1 Inspector 中必调的 3 个参数及其调试策略参数名默认值调试作用典型取值区间调试方法throwForce12f控制刀飞出初速度决定翻转圈数8~18在 Editor 中拖动滑块观察刀从抛出到落地的翻转圈数是否匹配设计预期如 1.5 圈对应中等难度maxAngularVelocity15f防止高速翻转导致物理计算溢出或判定失真10~25设为 5 测试是否完全不转设为 50 观察是否出现穿模或弹跳异常找到「翻转流畅」与「判定稳定」的平衡点targetLayerDefault隔离碰撞对象避免误触 UI 或背景自定义 TargetBoard Layer在 Project Settings → Tags and Layers 中新建 Layer将所有木板 GameObject Assign 此 Layer3.2 BoardManager 的对象池实现避免 Instantiate/Destroy 频繁 GC木板需动态生成与销毁。直接Instantiate()会引发内存抖动尤其 WebGL 平台。标准做法是预加载 5~8 个木板实例到 List启用/禁用而非创建/销毁public class BoardManager : MonoBehaviour { public GameObject boardPrefab; public int poolSize 6; private ListGameObject boardPool new ListGameObject(); void Start() { for (int i 0; i poolSize; i) { GameObject board Instantiate(boardPrefab); board.SetActive(false); boardPool.Add(board); } } public GameObject GetBoard() { foreach (var board in boardPool) { if (!board.activeInHierarchy) { board.SetActive(true); return board; } } // 池满时扩展谨慎使用 GameObject newBoard Instantiate(boardPrefab); boardPool.Add(newBoard); return newBoard; } public void ReturnBoard(GameObject board) { board.SetActive(false); } }注意boardPrefab的 Root 必须挂载RigidbodyisKinematic true和BoxColliderisTrigger false否则GetBoard()返回的对象无法参与物理碰撞。4. Flippy Knife 在 Unity 中的常见失效场景与 C# 层排错方案即使代码逻辑正确Flippy Knife 也可能在特定条件下失效刀穿过木板、多次点击无反应、翻转方向反常、WebGL 发布后卡顿。这些问题根源不在 C# 语法而在 Unity 物理步长、输入采样时机或平台特性。4.1 「刀穿板」问题物理更新频率与碰撞检测模式不匹配现象刀高速下落时直接穿过木板OnCollisionEnter完全不触发。原因Unity 默认 Fixed Timestep 为 0.02s50Hz若刀下落速度 5m/s单帧位移可能超过木板厚度导致碰撞被跳过。解决方案分三步提升物理更新频率Edit → Project Settings → Time → Fixed Timestep 改为0.01100Hz启用连续碰撞检测刀体 Rigidbody → Collision Detection Mode →Continuous Dynamic加厚木板 Collider将木板 BoxCollider 的Y Size从 0.1 提高到 0.3提供足够检测厚度。验证命令在KnifeController.OnCollisionEnter开头加Debug.Log($Hit at {collision.contacts[0].point});若日志无输出则确认上述三项已生效。4.2 「点击无反应」Input 系统版本冲突与触摸采样丢失现象PC 端鼠标点击正常移动端触摸无响应或快速连点只触发一次。原因Unity 2019.4 默认启用新 Input System但 Flippy Knife 源码多基于旧Input.GetMouseButtonDown(0)。若项目已导入新 Input System 包旧 API 会被禁用。修复步骤方案 A推荐删掉Packages\com.unity.inputsystem在 Package Manager 中关闭新 Input System方案 B保留新系统改写输入逻辑// 需添加 using UnityEngine.InputSystem; private PlayerInput playerInput; void Awake() { playerInput GetComponentPlayerInput(); playerInput.onActionTriggered OnActionTriggered; } void OnActionTriggered(InputAction.CallbackContext context) { if (context.action.name Click context.phase InputActionPhase.Started) { ThrowKnife(); } }提示PlayerInput组件需在 Inspector 中 Assign Action Map如 Player并绑定ClickAction 到鼠标左键/触摸屏点击。4.3 WebGL 平台下Rigidbody.AddForce失效IDBFS 写入失败的间接影响现象WebGL 构建后刀抛出后立即下坠无翻转rb.angularVelocity始终为(0,0,0)。原因WebGL 使用 IDBFSIndexedDB 文件系统模拟本地存储但Rigidbody初始化依赖底层物理库加载。若构建时未启用Decompression或Compression Format设置不当物理模块可能未完整载入。解决路径Build Settings → Player Settings → Publishing Settings → Compression Format → 改为Gzip或BrotliScripting Backend →IL2CPPWebGL 必须Api Compatibility Level →.NET Standard 2.1关键检查浏览器 Console 是否报Failed to load physics module若有需重装 Unity WebGL 模块Hub → Installs → ⋯ → Add Modules。5. 将 Flippy Knife 的 C# 翻转逻辑复用到其他 Unity 项目中的 3 种进阶技巧Flippy Knife 的价值不仅在于游戏本身更在于其封装良好的物理判定模块。你可以剥离KnifeController中与「刀」强相关的部分提取出通用组件适配飞镖、回旋镖、甚至机械臂末端执行器的姿态校验。5.1 提取FlipPhaseDetector组件解耦朝向解析逻辑新建脚本FlipPhaseDetector.cs专注解决「任意物体绕某轴翻转时如何量化其相位角」using UnityEngine; [RequireComponent(typeof(Rigidbody))] public class FlipPhaseDetector : MonoBehaviour { public Transform referenceAxis; // 参考轴如木板表面法线方向 public bool isVerticalAxis true; // true: 绕 Y 轴翻转false: 绕自定义轴 [Range(0, 360)] public float phaseOffset 0f; // 相位零点偏移 private Rigidbody rb; void Start() { rb GetComponentRigidbody(); } public float GetCurrentPhase() { Vector3 forward transform.forward; Vector3 axis isVerticalAxis ? Vector3.up : referenceAxis.up; // 将 forward 投影到垂直于 axis 的平面 Vector3 projected Vector3.ProjectOnPlane(forward, axis); float angle Vector3.SignedAngle(projected, Vector3.forward, axis); return (angle phaseOffset 360) % 360; } }5.1.1 在 Pico4 开发中复用此组件Pico4 VR 应用常需判断手柄控制器朝向。将FlipPhaseDetector挂在手柄模型上referenceAxis指向用户视点方向Camera.transform.forwardisVerticalAxis false即可实时获取手柄在用户前方平面内的旋转相位用于手势识别或菜单导航。5.2 用 C# 的Vector3.Reflect替代硬编码弹跳逻辑原 Flippy Knife 中弹飞用rb.AddForce(Vector3.up * 8f)但真实物理应遵循反射定律。替换为// 在 OnCollisionEnter 中替换弹飞分支 else if (dot -0.5f) { Vector3 reflection Vector3.Reflect(rb.velocity, contactNormal); rb.velocity reflection * 0.7f; // 0.7 为能量衰减系数 rb.angularVelocity * 0.8f; // 角速度同步衰减 }Vector3.Reflect(incident, normal)自动计算入射方向关于法线的镜像比手动构造向量更鲁棒且天然支持任意倾斜角度的木板。5.3 为 Unity GameAssembly.dll 优化减少GetComponentsInChildren调用大型项目中KnifeController若频繁调用GetComponentsInChildrenParticleSystem()会拖慢GameAssembly.dll加载。改为在Awake()中缓存private ParticleSystem[] particleSystems; void Awake() { particleSystems GetComponentsInChildrenParticleSystem(true); } void PlayHitEffect(Vector3 position) { foreach (var ps in particleSystems) { if (ps.gameObject.activeInHierarchy) { ps.transform.position position; ps.Play(); break; // 只播第一个可用粒子系统 } } }此写法将GetComponentsInChildren从每帧调用降为单次初始化显著降低 IL2CPP 编译后GameAssembly.dll的符号查找开销对 WebGL 和 Android 构建尤其关键。本文还有配套的精品资源点击获取
返回列表