在虚拟仿真软件开发过程中,Unity 需要展示 DXF 文件的内容。此工具可以将 DXF 文件转换为 Prefab,方便在 Unity 中使用。

注:本文只是介绍此工具后续的扩展内容,觉得工具不错的请给工具的作者点Star

工具来源: GitHub - Unity3D DXF Viewer

GitHub工具原示例图:


1. DXF 转换

此部分内容可参考 GitHub 上的工具使用说明,此处不作详细介绍。


2. 移除预制体多余脚本

DXF 转换后生成的 Prefab 可能会携带较多脚本,运行容易引发错误,因此需要手动移除。

以下是一个简单的移除工具示例:

[MenuItem("Tools/Remove Scripts Editor")]
private static void ShowWindow()
{
    GetWindow<RemoveScripts>("Remove Scripts");
}

private void OnGUI()
{
    if (!GUILayout.Button("Remove Scripts")) return;
    if (Selection.activeGameObject != null)
    {
        RemoveScriptsFromChildren(Selection.activeGameObject);
    }
    else
    {
        Debug.LogWarning("Please select a GameObject.");
    }
}

private void RemoveScriptsFromChildren(GameObject parent)
{
    RemoveComponents<GoLayer>(parent);
    RemoveComponents<GoLine>(parent);
    RemoveComponents<GoArc>(parent);
    RemoveComponents<GoCircle>(parent);
    RemoveComponents<GoEllipse>(parent);
    RemoveComponents<GoText>(parent);
    RemoveComponents<GoInsert>(parent);
    RemoveComponents<GoLwpolyLine>(parent);

    PrefabUtility.ApplyPrefabInstance(parent, InteractionMode.AutomatedAction);
    SceneView.RepaintAll();
    Debug.Log("清除完成");
}

private void RemoveComponents<T>(GameObject parent) where T : Component
{
    T[] scripts = parent.GetComponentsInChildren<T>();
    foreach (var script in scripts)
    {
        DestroyImmediate(script, true);
    }
}

3. 运行时动态修改 Layer 分组的材质颜色

3.1 Layer 分组

在导出 DXF 文件时,需要提前对不同部分进行分组(生成的预制体子物体分散,生成之后再分组工作量巨大),以便在 Unity 中进行材质颜色控制。

3.2 控制某个组的材质颜色(示例:Layer 组挂载Colorful,以便动态更改颜色)

Colorful代码示例:(半透明材质修改部分仅作展示)

public class Colorful : MonoBehaviour
{
    public enum Property
    {
        None = 0,
        Color = 1 << 1,
        Position = 1 << 2,
        Material = 1 << 3,
        All = Color | Position | Material
    }

    [SerializeField] private bool bringToFront = false;

    private readonly Dictionary<Material, Color> _rawColorMap = new Dictionary<Material, Color>();
    private readonly Dictionary<Transform, Vector3> _rawPosMap = new Dictionary<Transform, Vector3>();
    private readonly Dictionary<Renderer, List<Material>> _rendererMap = new Dictionary<Renderer, List<Material>>();
    private Color _currentColor = Color.white;


    private bool initialized = false;
    private bool colorChanged = false;
    private bool bringedFront = false;


    public void Initialize()
    {
        _rawColorMap.Clear();
        _rawPosMap.Clear();
        _rendererMap.Clear();

        Stack<GameObject> stack = new Stack<GameObject>();
        stack.Push(this.gameObject);

        while (stack.Count > 0)
        {
            GameObject current = stack.Pop();
            _rawPosMap.Add(current.transform, current.transform.position);
            Renderer r = current.GetComponent<Renderer>();
            if (r != null)
            {
                Material[] materials = r.materials;
                _rendererMap.Add(r, new List<Material>(materials));

                foreach (var t in materials)
                {
                    _rawColorMap.Add(t, t.color);
                }
            }

            for (int i = current.transform.childCount - 1; i >= 0; --i)
            {
                stack.Push(current.transform.GetChild(i).gameObject);
            }
        }
    }

    public void ChangeColor(Color color, bool exceptAlpha = false)
    {
        if (!initialized)
        {
            Initialize();
            initialized = true;
        }

        using (var itr = _rawColorMap.GetEnumerator())
        {
            while (itr.MoveNext())
            {
                if (bringToFront)
                {
                    bringedFront = true;
                    itr.Current.Key.SetFloat("Render Queue", 2002);
                }
                else
                {
                    bringedFront = false;
                }

                if (exceptAlpha)
                {
                    float a = itr.Current.Key.color.a;
                    color.a = a;
                }

                if (color.a < 1)
                {
                    //此部分内容自定义实现,仅作示例
                    itr.Current.Key.SetFloat("_Mode", 3);
                    itr.Current.Key.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                    itr.Current.Key.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                    itr.Current.Key.SetInt("_ZWrite", 1);
                    itr.Current.Key.DisableKeyword("_ALPHATEST_ON");
                    itr.Current.Key.EnableKeyword("_ALPHABLEND_ON");
                    itr.Current.Key.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    itr.Current.Key.renderQueue = 3000;
                }

                itr.Current.Key.color = color;
            }
        }

        _currentColor = color;
        colorChanged = true;
    }

    public void ChangeMaterial(Material mat)
    {
        if (!initialized)
        {
            Initialize();
            initialized = true;
        }

        using var itr = _rendererMap.GetEnumerator();
        while (itr.MoveNext())
        {
            Material[] mats = new Material[itr.Current.Value.Count];
            for (int i = 0; i < mats.Length; ++i)
            {
                mats[i] = mat;
            }

            itr.Current.Key.materials = mats;
        }
    }

    public void ResetColor(Property property = Property.All)
    {
        if (!initialized)
        {
            return;
        }

        if ((property & Property.Color) == Property.Color)
        {
            using var itr = _rawColorMap.GetEnumerator();
            while (itr.MoveNext())
            {
                if (bringedFront)
                {
                    itr.Current.Key.SetFloat("Render Queue", 2000);
                    bringedFront = false;
                }

                itr.Current.Key.color = itr.Current.Value;
                
                //itr.Current.Key.SetColor("_EmissionColor",Color.white);
                //itr.Current.Key.DisableKeyword("_EMISSION");

                //此部分内容自定义实现,仅作示例
                itr.Current.Key.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                itr.Current.Key.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                itr.Current.Key.SetInt("_ZWrite", 1);
                itr.Current.Key.DisableKeyword("_ALPHATEST_ON");
                itr.Current.Key.DisableKeyword("_ALPHABLEND_ON");
                itr.Current.Key.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                itr.Current.Key.renderQueue = 2000;
            }
        }

        if ((property & Property.Position) == Property.Position)
        {
            using var itr = _rawPosMap.GetEnumerator();
            while (itr.MoveNext())
            {
                itr.Current.Key.position = itr.Current.Value;
            }
        }

        if ((property & Property.Material) == Property.Material)
        {
            using var itr = _rendererMap.GetEnumerator();
            while (itr.MoveNext())
            {
                itr.Current.Key.materials = itr.Current.Value.ToArray();
            }
        }
    }
};

4. 在 UI 界面内查看生成的Prefab(DXF),并实现拖拽与缩放

4.1 拖拽控制 代码示例

public void DragDraw(BaseEventData e)
{
    PointerEventData data = e as PointerEventData;
    if (data.button == PointerEventData.InputButton.Middle)
    {
        Camera renderCamera = m_drawRenderer.renderCamera;
        Transform trans = renderCamera.transform;
        Vector3 delta = new Vector3(-data.delta.x, -data.delta.y) * renderCamera.orthographicSize * dragSpeed;
        trans.Translate(delta, Space.World);
    }
}

4.2 缩放控制 代码示例(基于鼠标位置、需要注意RawImage的锚点设置)

public void ScrollDraw(BaseEventData e)
{
    PointerEventData data = e as PointerEventData;
    Camera renderCamera = m_drawRenderer.renderCamera;
            
    var aspectRatio = m_rawImage.texture.width / (float)m_rawImage.texture.height;//获得宽高的缩放比
    var orthographicSize = renderCamera.orthographicSize;
    float scaleSize = orthographicSize;//暂存相机的初始 orthographicSize
    if (data != null) orthographicSize -= (data.scrollDelta.y * orthographicSize * zoomSpeed); 
    renderCamera.orthographicSize = orthographicSize;
    scaleSize = orthographicSize - scaleSize;//得到缩放的大小
    //注意:RawImage的锚点需要居中
    if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_rawImage.rectTransform, Input.mousePosition,
        null, out var localPoint))//得到鼠标在RawImage上映射的坐标值【RawImage中心点(描点)的坐标为(0,0)】
    {
        Vector2 rectSize = m_rawImage.rectTransform.rect.size;
        rectSize *= 0.5f;
        //鼠标相对RawImage中心点的偏移距离除以RawImage的宽高和乘以宽高缩放比 计算出摄像机需要移动的X、Y值
        Vector2 moveScale = new Vector2((localPoint.x / rectSize.x) * aspectRatio, localPoint.y / rectSize.y);
        moveScale *= -scaleSize;
        renderCamera.transform.position += new Vector3(moveScale.x, moveScale.y, 0);
	}
}

结论

使用该工具可以方便地在 Unity 中显示 DXF 文件,并支持动态修改颜色、材质以及在 UI 中实现拖拽和缩放操作。希望本文对你的开发有所帮助!

 工具来源: GitHub - Unity3D DXF Viewer

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐