You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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线,最终两个视图都呈现绿色。
  • 原有状态切换逻辑存在漏洞,切换模式时未正确清理/显隐绘制对象、重置绘制标记,容易出现状态残留。
修改方案
  1. 新增运行时判断逻辑:编辑器环境下让LineRenderer仅在Game视图渲染,Scene视图渲染时自动隐藏LineRenderer,避免遮挡Gizmos绘制的红色轮廓。
  2. 补全模式切换时的清理逻辑:切换到纯Gizmos绘制模式时,正确销毁已生成的LineRenderer对象、重置绘制标记;切换到其他模式时按需生成LineRenderer。
  3. 编辑器相关代码加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 10:51:27