Unity如何实现BoxCollider Scene视图红、Game视图绿轮廓绘制
问题根因
这是一款运行时BoxCollider轮廓绘制辅助脚本,即便未选中挂载碰撞体的GameObject,也可在Scene视图、Game视图中显示碰撞体轮廓。脚本内置DrawStates枚举,包含DrawOnGizmosRuntime、DrawOnGameviewRuntime、DrawOnBoth三种绘制模式,预期选择DrawOnBoth模式时,Scene视图窗口内BoxCollider轮廓绘制为红色,Game视图窗口内轮廓绘制为绿色。

当前DrawOnBoth模式下双视图均显示绿色轮廓的核心原因:
OnDrawGizmos()绘制的红色线仅在Scene视图渲染,而LineRenderer生成的绿色线是场景内普通渲染对象,会同时显示在Scene视图和Game视图中,绿色LineRenderer在Scene视图中遮挡了下层的红色Gizmos线,最终两个视图都呈现绿色。- 原有状态切换逻辑存在漏洞,切换模式时未正确清理/显隐绘制对象、重置绘制标记,容易出现状态残留。
修改方案
- 新增运行时判断逻辑:编辑器环境下让LineRenderer仅在Game视图渲染,Scene视图渲染时自动隐藏LineRenderer,避免遮挡Gizmos绘制的红色轮廓。
- 补全模式切换时的清理逻辑:切换到纯Gizmos绘制模式时,正确销毁已生成的LineRenderer对象、重置绘制标记;切换到其他模式时按需生成LineRenderer。
- 编辑器相关代码加
UNITY_EDITOR宏包裹,避免打包时报错。
修改后的完整代码如下:
using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif public class DrawOnBoxCollider : MonoBehaviour { public enum DrawStates { DrawOnGizmosRuntime, DrawOnGameviewRuntime, DrawOnBoth } public DrawStates drawingStates; public Transform boxColliderToDrawOn; private List<LineRenderer> lines = new List<LineRenderer>(); private BoxCollider boxCollider = null; private bool drawOnGizmos = false; private LineRenderer line; private bool drawOnce = true; void Start() { if (boxColliderToDrawOn != null) { boxCollider = boxColliderToDrawOn.GetComponent<BoxCollider>(); } #if UNITY_EDITOR // 注册相机渲染前回调,控制LineRenderer在不同视图的显隐 Camera.onPreCull += OnCameraPreCull; #endif } void OnDestroy() { #if UNITY_EDITOR Camera.onPreCull -= OnCameraPreCull; #endif } #if UNITY_EDITOR private void OnCameraPreCull(Camera cam) { // 所有LineRenderer默认隐藏 bool lineEnable = false; // 非Scene视图相机(即Game视图相机)渲染时,根据当前模式决定是否显示LineRenderer if (SceneView.currentDrawingSceneView == null || SceneView.currentDrawingSceneView.camera != cam) { lineEnable = drawingStates is DrawStates.DrawOnGameviewRuntime or DrawStates.DrawOnBoth; } foreach (var lineRenderer in lines) { if (lineRenderer != null) { lineRenderer.enabled = lineEnable; } } } #endif void Update() { if (boxColliderToDrawOn == null) return; // 先重置状态 drawOnGizmos = false; bool needLineRenderer = false; switch (drawingStates) { case DrawStates.DrawOnGameviewRuntime: needLineRenderer = true; if (boxCollider == null) { boxCollider = boxColliderToDrawOn.GetComponent<BoxCollider>(); } if (drawOnce) { ClearExistingLines(); DrawOnGameViewRuntime(); drawOnce = false; } break; case DrawStates.DrawOnGizmosRuntime: drawOnGizmos = true; // 切换到该模式时清理已存在的LineRenderer,重置绘制标记 if (!drawOnce) { ClearExistingLines(); drawOnce = true; } if (boxCollider == null) { boxCollider = boxColliderToDrawOn.GetComponent<BoxCollider>(); } break; case DrawStates.DrawOnBoth: drawOnGizmos = true; needLineRenderer = true; if (boxCollider == null) { boxCollider = boxColliderToDrawOn.GetComponent<BoxCollider>(); } if(drawOnce) { ClearExistingLines(); DrawOnGameViewRuntime(); drawOnce = false; } break; } #if !UNITY_EDITOR // 非编辑器环境(打包后)直接根据模式控制LineRenderer显隐 foreach (var lineRenderer in lines) { if (lineRenderer != null) { lineRenderer.enabled = needLineRenderer; } } #endif } // 清理已生成的LineRenderer对象 private void ClearExistingLines() { foreach (var lineRenderer in lines) { if (lineRenderer != null) { Destroy(lineRenderer.gameObject); } } lines.Clear(); line = null; } private void DrawGizmosOnRunTime(Color color) { if (boxCollider != null && drawOnGizmos) { Gizmos.color = color; Matrix4x4 rotationMatrix = Matrix4x4.TRS(boxCollider.transform.position, boxCollider.transform.rotation, boxCollider.transform.lossyScale); Gizmos.matrix = rotationMatrix; Gizmos.DrawWireCube(boxCollider.center, boxCollider.size); } } private void OnDrawGizmos() { DrawGizmosOnRunTime(Color.red); } private void DrawOnGameViewRuntime() { Material material = new Material(Shader.Find("Unlit/Color")); Color color = Color.green; material.color = color; float width = 0.1f; Vector3 rightDir = boxCollider.transform.right.normalized; Vector3 forwardDir = boxCollider.transform.forward.normalized; Vector3 upDir = boxCollider.transform.up.normalized; Vector3 center = boxCollider.transform.position + boxCollider.center; Vector3 size = boxCollider.size; size.x *= boxCollider.transform.lossyScale.x; size.y *= boxCollider.transform.lossyScale.y; size.z *= boxCollider.transform.lossyScale.z; DrawLine(center + upDir * size.y / 2f + rightDir * size.x / 2f + forwardDir * size.z / 2f, center + upDir * size.y / 2f - rightDir * size.x / 2f + forwardDir * size.z / 2f, color, material, width); DrawLine(center - upDir * size.y / 2f + rightDir * size.x / 2f + forwardDir * size.z / 2f, center - upDir * size.y / 2f - rightDir * size.x / 2f + forwardDir * size.z / 2f, color, material, width); DrawLine(center + upDir * size.y / 2f + rightDir * size.x / 2f + forwardDir * size.z / 2f, center - upDir * size.y / 2f + rightDir * size.x / 2f + forwardDir * size.z / 2f, color, material, width); DrawLine(center + upDir * size.y / 2f - rightDir * size.x / 2f + forwardDir * size.z / 2f, center - upDir * size.y / 2f - rightDir * size.x / 2f + forwardDir * size.z / 2f, color, material, width); DrawLine(center + upDir * size.y / 2f + rightDir * size.x / 2f - forwardDir * size.z / 2f, center + upDir * size.y / 2f - rightDir * size.x / 2f - forwardDir * size.z / 2f, color, material, width); DrawLine(center - upDir * size.y / 2f + rightDir * size.x / 2f - forwardDir * size.z / 2f, center - upDir * size.y / 2f - rightDir * size.x / 2f - forwardDir * size.z / 2f, color, material, width); DrawLine(center + upDir * size.y / 2f + rightDir * size.x / 2f - forwardDir * size.z / 2f, center - upDir * size.y / 2f + rightDir * size.x / 2f - forwardDir * size.z / 2f, color, material, width); DrawLine(center + upDir * size.y / 2f - rightDir * size.x / 2f - forwardDir * size.z / 2f, center - upDir * size.y / 2f - rightDir * size.x / 2f - forwardDir * size.z / 2f, color, material, width); DrawLine(center + upDir * size.y / 2f + rightDir * size.x / 2f + forwardDir * size.z / 2f, center + upDir * size.y / 2f + rightDir * size.x / 2f - forwardDir * size.z / 2f, color, material, width); DrawLine(center - upDir * size.y / 2f + rightDir * size.x / 2f + forwardDir * size.z / 2f, center - upDir * size.y / 2f + rightDir * size.x / 2f - forwardDir * size.z / 2f, color, material, width); DrawLine(center + upDir * size.y / 2f - rightDir * size.x / 2f + forwardDir * size.z / 2f, center + upDir * size.y / 2f - rightDir * size.x / 2f - forwardDir * size.z / 2f, color, material, width); DrawLine(center - upDir * size.y / 2f - rightDir * size.x / 2f + forwardDir * size.z / 2f, center - upDir * size.y / 2f - rightDir * size.x / 2f - forwardDir * size.z / 2f, color, material, width); } private void DrawLine(Vector3 start, Vector3 end, Color color, Material material, float width = 0.01f) { line = new GameObject("Line_" + start.ToString() + "_" + end.ToString()).AddComponent<LineRenderer>(); line.name = "LineRenderer Draw Line"; line.material = material; line.startColor = color; line.endColor = color; line.startWidth = width; line.endWidth = width; line.positionCount = 2; line.useWorldSpace = true; line.SetPosition(0, start); line.SetPosition(1, end); line.transform.SetParent(transform); lines.Add(line); } public void SetLinesColor(Color color) { for (int i = 0; i < lines.Count; i++) { lines[i].material.color = color; lines[i].startColor = color; lines[i].endColor = color; } } }
效果说明
修改后选择DrawOnBoth模式时:
- Scene视图仅显示Gizmos绘制的红色BoxCollider轮廓
- Game视图仅显示LineRenderer绘制的绿色BoxCollider轮廓
- 切换其他两种绘制模式时,对应视图的轮廓显示也符合预期,不会出现残留对象。
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

