Unity2022物理系统入门:用刚体碰撞实现俄罗斯方块小游戏
Unity2022物理系统实战用刚体碰撞打造俄罗斯方块游戏在游戏开发中物理引擎是实现真实交互效果的核心技术之一。Unity2022的物理系统经过多次迭代优化为开发者提供了更强大的工具链。本文将带您从零开始利用刚体碰撞等物理特性开发一个具有真实物理反馈的俄罗斯方块游戏。1. 物理系统基础配置在开始开发前我们需要先了解Unity物理系统的基本设置。打开Unity2022后通过Edit Project Settings Physics进入物理引擎配置界面// 物理系统关键参数示例 Physics.gravity new Vector3(0, -9.81f, 0); // 设置重力加速度 Physics.defaultSolverIterations 6; // 碰撞检测迭代次数 Physics.queriesHitBackfaces true; // 允许检测背面碰撞刚体组件是物理系统的核心它为游戏对象添加物理特性属性说明推荐值Mass质量1.0Drag空气阻力0.1Angular Drag旋转阻力0.05Use Gravity启用重力trueIs Kinematic运动学控制false提示俄罗斯方块的下落方块需要启用重力而已经落地的方块则应设为运动学刚体2. 方块预制体与碰撞体设计创建标准的俄罗斯方块需要设计7种不同形状的预制体。每种预制体由多个小方块组成// 创建L型方块示例 void CreateLPiece() { GameObject lPiece new GameObject(L_Piece); Rigidbody rb lPiece.AddComponentRigidbody(); rb.constraints RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; // 添加4个小方块 for(int i0; i4; i) { GameObject cube GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.SetParent(lPiece.transform); cube.transform.localPosition new Vector3(...); BoxCollider collider cube.GetComponentBoxCollider(); collider.material Resources.LoadPhysicMaterial(Bouncy); } }碰撞体编辑技巧使用复合碰撞体(Compound Collider)确保精确碰撞检测调整Contact Offset参数优化碰撞检测灵敏度为不同材质设置不同的物理材质参数3. 方块控制与物理交互实现方块的物理控制需要平衡玩家输入与物理模拟public class TetrisPieceController : MonoBehaviour { [SerializeField] float moveForce 10f; [SerializeField] float rotateTorque 5f; [SerializeField] float fastFallMultiplier 2f; private Rigidbody rb; private bool isGrounded; void Awake() { rb GetComponentRigidbody(); } void Update() { HandleInput(); CheckGroundStatus(); } void HandleInput() { float moveInput Input.GetAxis(Horizontal); rb.AddForce(Vector3.right * moveInput * moveForce); if(Input.GetKeyDown(KeyCode.UpArrow)) { rb.AddTorque(Vector3.forward * rotateTorque, ForceMode.Impulse); } if(Input.GetKey(KeyCode.DownArrow)) { rb.AddForce(Vector3.down * fastFallMultiplier, ForceMode.Acceleration); } } void CheckGroundStatus() { RaycastHit hit; isGrounded Physics.Raycast(transform.position, Vector3.down, out hit, 0.1f); } }物理参数调优建议移动力(moveForce)需要与方块质量匹配旋转扭矩(rotateTorque)过大容易导致方块失控快速下落时适当增加重力缩放系数4. 碰撞检测与行消除实现行消除需要检测完整的行并处理物理效果void CheckCompletedRows() { // 将游戏区域划分为20行 for(int row0; row20; row) { Vector3 rayStart new Vector3(-5, row 0.5f, 0); RaycastHit[] hits Physics.RaycastAll(rayStart, Vector3.right, 10); if(hits.Length 10) { // 假设游戏区域宽度为10单位 StartCoroutine(DestroyRow(hits, row)); } } } IEnumerator DestroyRow(RaycastHit[] blocks, int row) { // 1. 禁用碰撞和物理 foreach(var hit in blocks) { Rigidbody rb hit.collider.GetComponentRigidbody(); rb.isKinematic true; Collider col hit.collider.GetComponentCollider(); col.enabled false; } // 2. 播放消除动画 yield return new WaitForSeconds(0.3f); // 3. 销毁方块 foreach(var hit in blocks) { Destroy(hit.collider.gameObject); } // 4. 上方方块下落 DropUpperRows(row); }高级物理效果实现为消除行添加爆炸力效果AddExplosionForce使用关节组件(Joint)实现方块粘连效果通过物理材质实现方块间的弹性碰撞5. 性能优化与高级技巧确保物理游戏流畅运行的关键优化策略1. 对象池技术public class BlockPool : MonoBehaviour { [SerializeField] GameObject blockPrefab; [SerializeField] int poolSize 200; private QueueGameObject pool new QueueGameObject(); void Start() { for(int i0; ipoolSize; i) { GameObject block Instantiate(blockPrefab); block.SetActive(false); pool.Enqueue(block); } } public GameObject GetBlock() { if(pool.Count 0) { GameObject block pool.Dequeue(); block.SetActive(true); return block; } return Instantiate(blockPrefab); } public void ReturnBlock(GameObject block) { block.SetActive(false); pool.Enqueue(block); } }2. 碰撞层优化设置LayerCollisionMatrix减少不必要的碰撞检测将静态方块移至特定层(如StaticBlocks)动态方块使用另一层(如ActiveBlocks)3. 物理模拟控制// 游戏暂停时停止物理模拟 void OnApplicationPause(bool pauseStatus) { Physics.autoSimulation !pauseStatus; } // 调整固定时间步长提高性能 Time.fixedDeltaTime 0.02f; // 默认值6. 视觉效果增强结合物理系统创造更生动的游戏体验1. 碰撞粒子效果void OnCollisionEnter(Collision collision) { ContactPoint contact collision.contacts[0]; Quaternion rot Quaternion.FromToRotation(Vector3.up, contact.normal); Vector3 pos contact.point; GameObject effect Instantiate(impactEffect, pos, rot); effect.GetComponentParticleSystem().Play(); }2. 物理声音反馈[RequireComponent(typeof(AudioSource))] public class BlockImpactSound : MonoBehaviour { [SerializeField] AudioClip[] impactSounds; [SerializeField] float velocityThreshold 2f; private AudioSource audioSource; void Awake() { audioSource GetComponentudioSource(); } void OnCollisionEnter(Collision collision) { if(collision.relativeVelocity.magnitude velocityThreshold) { int index Random.Range(0, impactSounds.Length); audioSource.PlayOneShot(impactSounds[index]); } } }3. 屏幕震动效果IEnumerator ShakeCamera(float duration, float magnitude) { Vector3 originalPos Camera.main.transform.localPosition; float elapsed 0f; while(elapsed duration) { float x Random.Range(-1f, 1f) * magnitude; float y Random.Range(-1f, 1f) * magnitude; Camera.main.transform.localPosition new Vector3(x, y, originalPos.z); elapsed Time.deltaTime; yield return null; } Camera.main.transform.localPosition originalPos; }7. 调试与问题解决开发过程中常见的物理问题及解决方案1. 方块穿透问题增加碰撞检测迭代次数Physics.defaultSolverIterations减小固定时间步长Time.fixedDeltaTime 0.01f使用连续碰撞检测rigidbody.collisionDetectionMode CollisionDetectionMode.Continuous2. 性能优化检查表[ ] 是否使用了合适的碰撞体类型(Box Mesh)[ ] 是否设置了合理的物理层碰撞矩阵[ ] 是否对静态物体启用了static标记[ ] 是否使用了对象池管理方块实例3. 物理调试可视化void OnDrawGizmos() { // 绘制重力方向 Gizmos.color Color.red; Gizmos.DrawLine(transform.position, transform.position Physics.gravity); // 绘制碰撞体边界 Collider col GetComponentCollider(); if(col ! null) { Gizmos.color Color.green; Gizmos.DrawWireCube(col.bounds.center, col.bounds.size); } }在Unity编辑器中通过Window Analysis Physics Debugger可以查看详细的物理系统状态包括碰撞体、刚体等信息。