Unity中Blender导入砖块无法反弹球方向,原生立方体正常
Unity 3D打砖块游戏:球碰撞砖块后速度不反弹的问题解决
问题背景
开发3D类打砖块游戏时,用Blender制作砖块模型并以.blend格式导入Unity,给砖块添加了未勾选Is Trigger的Box Collider;球采用带开启Is Trigger的Sphere Collider的简单球体,Rigidbody设置为连续碰撞检测。目前paddle和墙体的反弹功能正常,但存在以下问题:
- 球第一次碰撞砖块时,碰撞能被检测到、
Destroy()也能正常执行,但球的速度不会改变,直接穿过砖块 - 球回到paddle再次飞向砖块后,反弹功能恢复正常
后续排查发现:
- 给球的初始
velocity设置z分量而非仅x时,第一次碰撞就能正常反弹,但这不是合理解决方案 - 最终定位根源:球同时碰撞两块砖时,
OnTriggerEnter会被执行两次,导致velocity.x先取反再取反,最终恢复原值
球的控制代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Ball : MonoBehaviour { public float maxX; public float maxZ; private Vector3 velocity; private void OnTriggerEnter(Collider other) { float maxDist = 0.5f * other.transform.localScale.x + 0.5f * transform.localScale.z; float actualDist = transform.position.z - other.transform.position.z; float distNorm = actualDist / maxDist; if (other.CompareTag("Paddle")) { velocity.z = distNorm * maxZ; velocity.x *= -1; } else if (other.CompareTag("Side Wall")) { velocity.z *= -1; } else if (other.CompareTag("Wall")) { velocity.x *= -1; } else if (other.CompareTag("Brick")) { velocity.x *= -1; Destroy(other.gameObject); } } void Start() { velocity = new Vector3(-maxX, 0, 0); } void Update() { transform.position += velocity * Time.deltaTime; } }
砖块生成代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class BrickFactory : MonoBehaviour { public GameObject emptyBrick, yellowBrick, orangeBrick, greenBrick, cyanBrick, pinkBrick, purpleBrick, darkBlueBrick, redBrick; private GameObject[] brickTypes; private int[,] level1 = new int[25,10] { {8,8,8,8,8,8,8,8,8,8}, {8,8,8,8,8,8,8,8,8,8}, {7,7,7,7,7,7,7,7,7,7}, {7,7,7,7,7,7,7,7,7,7}, {6,6,6,6,6,6,6,6,6,6}, {6,6,6,6,6,6,6,6,6,6}, {5,5,5,5,5,5,5,5,5,5}, {5,5,5,5,5,5,5,5,5,5}, {4,4,4,4,4,4,4,4,4,4}, {4,4,4,4,4,4,4,4,4,4}, {3,3,3,3,3,3,3,3,3,3}, {3,3,3,3,3,3,3,3,3,3}, {2,2,2,2,2,2,2,2,2,2}, {2,2,2,2,2,2,2,2,2,2}, {1,1,1,1,1,1,1,1,1,1}, {6,6,6,6,6,6,6,6,6,6}, {5,5,5,5,5,5,5,5,5,5}, {5,5,5,5,5,5,5,5,5,5}, {4,4,4,4,4,4,4,4,4,4}, {4,4,4,4,4,4,4,4,4,4}, {3,3,3,3,3,3,3,3,3,3}, {3,3,3,3,3,3,3,3,3,3}, {2,2,2,2,2,2,2,2,2,2}, {2,2,2,2,2,2,2,2,2,2}, {1,1,1,1,1,1,1,1,1,1} }; void Start() { brickTypes = new GameObject[]{ emptyBrick, //empty 0 yellowBrick, //yellow 1 orangeBrick, //orange 2 greenBrick, //green 3 cyanBrick, //cyan 4 pinkBrick, //pink 5 purpleBrick, //purple 6 darkBlueBrick, //darkBlue 7 redBrick //red 8 }; float rowOffset = -7.877f; float columnOffset = -7.5f; for (int i = 0; i < 25; i++) { for (int j = 0; j < 10; j++) { if(level1[i, j] != 0) { GameObject newBrick = Instantiate(brickTypes[level1[i,j]], new Vector3(columnOffset, 1.28f, rowOffset), Quaternion.identity) as GameObject; newBrick.transform.Rotate(0, 0, 90); } rowOffset += 1.75f; } rowOffset = -7.877f; columnOffset += 0.6f; } } void Update() { } }
解决方法
针对同时碰撞多块砖导致速度取反抵消的问题,有几种实用处理方式:
1. 单次帧内只处理一次砖块碰撞
在Ball脚本中添加标记变量,限制每帧仅处理一次砖块碰撞的速度修改:
private bool hasHitBrickThisFrame = false; private int lastFrameChecked = -1; private void OnTriggerEnter(Collider other) { // 每帧重置碰撞标记 if (Time.frameCount != lastFrameChecked) { hasHitBrickThisFrame = false; lastFrameChecked = Time.frameCount; } float maxDist = 0.5f * other.transform.localScale.x + 0.5f * transform.localScale.z; float actualDist = transform.position.z - other.transform.position.z; float distNorm = actualDist / maxDist; if (other.CompareTag("Paddle")) { velocity.z = distNorm * maxZ; velocity.x *= -1; } else if (other.CompareTag("Side Wall")) { velocity.z *= -1; } else if (other.CompareTag("Wall")) { velocity.x *= -1; } else if (other.CompareTag("Brick")) { // 仅在当前帧未处理过砖块碰撞时修改速度 if (!hasHitBrickThisFrame) { velocity.x *= -1; hasHitBrickThisFrame = true; } Destroy(other.gameObject); } }
2. 基于碰撞法线计算反弹方向(更严谨)
放弃直接取反x速度,改用物理反射公式计算反弹方向,即使同时碰撞多块砖也能得到正确结果,还能适配带旋转角度的砖块:
else if (other.CompareTag("Brick")) { // 获取碰撞点的法线方向 Vector3 collisionNormal = other.ClosestPointOnBounds(transform.position) - transform.position; collisionNormal.Normalize(); // 用反射公式计算反弹速度 velocity = Vector3.Reflect(velocity, collisionNormal); Destroy(other.gameObject); }
3. 调整砖块布局避免同时碰撞
微调砖块生成时的偏移量,让砖块之间留出极小间隙(比如0.01f),避免球同时触发多个Trigger:
// 在BrickFactory的循环中修改增量 rowOffset += 1.75f + 0.01f; // ... columnOffset += 0.6f + 0.01f;
这种方式简单直接,但仅适合布局规则的场景。
Blender模型导入注意事项
虽然本次问题根源不在模型,但导入时仍需注意:
- 导入时勾选Generate Colliders,手动添加Collider时要确保其大小、位置与模型匹配
- 重置模型的Transform缩放(点击Inspector中Transform组件的Reset按钮),避免缩放异常导致Collider计算错误
- 确认模型轴心点位置正确,防止Collider偏移
内容的提问来源于stack exchange,提问作者Ernest P W
相关产品推荐
相关产品推荐

