3D房屋预制体无论是否重叠均无法显示透明红/绿色的问题求助
建筑放置项目中房屋预制体材质切换异常问题解决
问题描述
我正在开发一个建筑放置项目,所有物体都带有Rigidbody和Box Collider组件,仅房屋预制体包含子物体。当前脚本在Unity默认3D物体(胶囊体、球体、立方体)上运行正常——拖动这些物体与已放置的房屋重叠时,物体会变为透明红色;移至地面时变为透明绿色。但房屋预制体无论是否与其他物体重叠,都无法切换至透明红/绿色材质。
当前使用脚本
BuildingManager 脚本
public class BuildingManager : MonoBehaviour { private GameObject pendingObject; [SerializeField] private Material[] materials; public bool canPlace; void Update() { if (pendingObject != null) { UpDateMaterials(); pendingObject.transform.position = pos; if (Input.GetMouseButtonDown(0) && canPlace) { PlaceObject(); } } } public void PlaceObject() { pendingObject.GetComponent<MeshRenderer>().material = materials[2]; pendingObject = null; } public void UpDateMaterials() { if (canPlace) { pendingObject.GetComponent<MeshRenderer>().material = materials[0]; } if (!canPlace) { pendingObject.GetComponent<MeshRenderer>().material = materials[1]; } } }
CheckPlacement 脚本
public class CheckPlacement : MonoBehaviour { BuildingManager buildingManager; void Start() { buildingManager = GameObject.Find("BuildingManager").GetComponent<BuildingManager>(); } private void OnTriggerEnter(Collider other) { if (other.gameObject.CompareTag("Building")) { Debug.Log("Can't Place Building"); buildingManager.canPlace = false; } } private void OnTriggerExit(Collider other) { if (other.gameObject.CompareTag("Building")) { Debug.Log("Can Place Building"); buildingManager.canPlace = true; } } }
问题原因分析
- MeshRenderer位置问题:房屋预制体的MeshRenderer通常挂载在子物体上,而当前脚本仅尝试获取根物体的
MeshRenderer,根物体无此组件,导致材质切换代码无效。 - 触发检测覆盖不全:仅依赖
OnTriggerEnter和OnTriggerExit,若待放置物体长时间处于重叠状态,可能出现状态异常。 - 初始化缺失:未在放置房屋前初始化
canPlace状态,可能导致初始状态错误。
解决方案
1. 遍历所有子物体修改材质
修改BuildingManager中的方法,遍历待放置物体的所有MeshRenderer(包括子物体)进行材质设置:
public class BuildingManager : MonoBehaviour { private GameObject pendingObject; [SerializeField] private Material[] materials; public bool canPlace; void Update() { if (pendingObject != null) { UpDateMaterials(); pendingObject.transform.position = pos; if (Input.GetMouseButtonDown(0) && canPlace) { PlaceObject(); } } } public void PlaceObject() { SetMaterialForAllRenderers(materials[2]); pendingObject = null; } public void UpDateMaterials() { if (canPlace) { SetMaterialForAllRenderers(materials[0]); } else { SetMaterialForAllRenderers(materials[1]); } } // 新增方法:遍历所有MeshRenderer设置材质 private void SetMaterialForAllRenderers(Material targetMat) { MeshRenderer[] renderers = pendingObject.GetComponentsInChildren<MeshRenderer>(true); foreach (var renderer in renderers) { renderer.material = targetMat; } } // 新增:设置待放置物体时初始化状态 public void SetPendingObject(GameObject obj) { pendingObject = obj; canPlace = true; } }
2. 完善触发检测逻辑
修改CheckPlacement脚本,添加OnTriggerStay确保状态实时更新:
public class CheckPlacement : MonoBehaviour { BuildingManager buildingManager; void Start() { buildingManager = GameObject.Find("BuildingManager").GetComponent<BuildingManager>(); } private void OnTriggerEnter(Collider other) { if (other.gameObject.CompareTag("Building")) { Debug.Log("Can't Place Building"); buildingManager.canPlace = false; } } private void OnTriggerExit(Collider other) { if (other.gameObject.CompareTag("Building")) { Debug.Log("Can Place Building"); buildingManager.canPlace = true; } } // 新增:持续检测重叠状态 private void OnTriggerStay(Collider other) { if (other.gameObject.CompareTag("Building")) { buildingManager.canPlace = false; } } }
3. 检查组件挂载与设置
- 将CheckPlacement脚本挂载到待放置房屋预制体的根物体上。
- 确保根物体的Box Collider勾选
IsTrigger(触发检测需要)。 - 待放置物体的Rigidbody设置为
Kinematic(避免物理引擎干扰拖动)。 - 所有已放置的建筑物体标签设置为"Building"。
内容的提问来源于stack exchange,提问作者Kıvanç Cantimur
相关产品推荐
相关产品推荐

