c# - 如何调整一个克隆体的形状/尺寸以影响场景 View 中的所有其他克隆体

标签 c# unity3d unity-editor

我想通过调整一个来更改/调整场景 View 中多个克隆对象的形状/尺寸。该对象可以说是需要扩展的四边形或线条渲染器。例如,当一个游戏对象线渲染器在场景 View 中扩展(使用鼠标)时,所有其他克隆都会受到影响。我知道在克隆之前调整一个对象的形状/尺寸要简单得多,也可以在预制件上进行此更改并将其应用于所有对象,但我需要看到动态变化,因为它们恰好使我的设计过程更加有效的。 我还想在需要时打开和关闭此功能。请在 this question 中查看我是如何创建克隆的.

请注意,我不想在运行时实现这一点。

最佳答案

How can I adjust shape/dimensions of one clone to affect all other clones in the scene view

在对组件进行修改时同步属性。要使用现有代码库,我们还需要克隆修改后的对象,以确保在我们重新创建其他对象时它不会被破坏。

This object could be say a quad or a line renderer that needs to be extended

所以我们想知道任何 组件上的任何 属性何时被修改。对于自定义脚本,使用 OnValidate 是微不足道的,但是对于密封组件(如 LineRenderer),它有点棘手。幸运的是,由于我们使用的是编辑器,因此我们可以访问它的一些核心功能。

具体来说,我们可以挂接到编辑器的 Undo.postprocessModifications 事件,以便在对场景进行修改时获得回调。该委托(delegate)将为我们提供一个 UndoPropertyModification 数组,该数组将包含已修改组件属性的列表,我们可以使用这些属性通过 EditorUtility.CopySerializedIfDifferent 同步其他对象。


CircleSpawn

[ExecuteInEditMode]
public class CircleSpawn : MonoBehaviour
{
    public List<GameObject> Objects;
    public GameObject OriginalObject;
    public GameObject PreviousObject;
    public GameObject ActiveObject;
    public SpawnData Data;

    private void OnEnable ()
    {
        if (Objects == null) Objects = new List<GameObject>();

        // Register modification event
        Undo.postprocessModifications += OnPropertyModification;
    }

    private void OnDisable ()
    {
        // Deregister modification event
        Undo.postprocessModifications -= OnPropertyModification;
    }

    private UndoPropertyModification[] OnPropertyModification (
            UndoPropertyModification[] modifications)
    {
        // Iterate through modifications
        foreach (var mod in modifications)
        {
            var trg = mod.currentValue.target as Component;
            if (trg)
            {
                // Filter only those objects that we've created
                if (Objects.Contains(trg.gameObject))
                {
                    // Clone the object and make it 'active'
                    if (!ActiveObject.Equals(trg.gameObject))
                    {
                        SetActiveObj(Instantiate(trg.gameObject));
                        ActiveObject.name = OriginalObject.name;
                        ActiveObject.hideFlags =
                        HideFlags.DontSaveInBuild | HideFlags.HideInHierarchy;
                        ActiveObject.SetActive(false);
                    }

                    // Synchronize the other object properties
                    foreach (var obj in Objects)
                    {
                        var type = mod.currentValue.target.GetType();
                        var comp = obj.GetComponent(type);
                        if (comp == null)
                            comp = obj.AddComponent(type);

                        EditorUtility.CopySerializedIfDifferent(trg, comp);
                    }

                    UpdateTransforms();
                    break;
                }
            }
        }

        return modifications;
    }

    public void SetActiveObj (GameObject active)
    {
        // Destroy the active object
        if (!OriginalObject.Equals(ActiveObject) &&
             PreviousObject && !PreviousObject.Equals(ActiveObject))
             DestroyImmediate(ActiveObject);

        ActiveObject = active;
    }

    public void UpdateObjects ()
    {    
        // Destroy old objects
        foreach (var obj in Objects) DestroyImmediate(obj);

        Objects.Clear();

        var steps = 360.0f / Data.Count;
        var angle = 0f;

        // Instantiate new objects
        for (var i = 0; i < Data.Count; i++)
        {
            var rot = Quaternion.Euler(0f, 0f, Data.Angle + angle);
            var pos = rot * Vector3.right * Data.Radius;
            var obj = Instantiate(ActiveObject, transform.position + pos, rot);
            obj.SetActive(true);
            Objects.Add(obj);
            angle += steps;
        }
    }

    public void UpdateTransforms ()
    {
        var steps = 360.0f / Objects.Count;
        var angle = 0f;

        // Set transforms based on Angle and Radius
        for (var i = 0; i < Objects.Count; i++)
        {
            var rot = Quaternion.Euler(0f, 0f, Data.Angle + angle);
            var pos = rot * Vector3.right * Data.Radius;
            Objects[i].transform.position =
            transform.position + pos;
            Objects[i].transform.rotation = rot;
            angle += steps;
        }
    }
}

CircleSpawnEditor

[CustomEditor(typeof(CircleSpawn))]
public class CircleSpawnEditor : Editor
{
    public override void OnInspectorGUI ()
    {
        GUI.enabled = !EditorApplication.isPlaying;
        var spawner = (CircleSpawn)target;

        // Draw object field
        EditorGUILayout.LabelField("Object");
        spawner.OriginalObject = (GameObject)EditorGUILayout.ObjectField(
        spawner.OriginalObject, typeof(GameObject), true);
        if (!spawner.OriginalObject) return;

        // Restore original object
        if (GUILayout.Button("Revert") || !spawner.ActiveObject ||
           !spawner.OriginalObject.Equals(spawner.PreviousObject))
        {
            // Store data reference
            spawner.Data = spawner.OriginalObject.GetComponent<SpawnData>();
            if (!spawner.Data) return;

            spawner.SetActiveObj(spawner.OriginalObject);
            spawner.PreviousObject = spawner.OriginalObject;
            spawner.UpdateObjects();
        }

        // Draw numeric sliders
        EditorGUILayout.LabelField("Radius"); // Set as required
        spawner.Data.Radius = EditorGUILayout.Slider(spawner.Data.Radius, 0f, 100f);
        EditorGUILayout.LabelField("Angle"); // Set as required
        spawner.Data.Angle = EditorGUILayout.Slider(spawner.Data.Angle, 0f, 360f);
        EditorGUILayout.LabelField("Count"); // Set as required
        spawner.Data.Count = EditorGUILayout.IntSlider(spawner.Data.Count, 0, 36);  

        // Update objects on Count slider change
        if (spawner.Data.Count != spawner.Objects.Count)
            spawner.UpdateObjects();

        // Update transforms on Angle or Radius slider change
        if (!Mathf.Approximately(spawner.Data.Angle, spawner.Data.LastAngle) ||
            !Mathf.Approximately(spawner.Data.Radius, spawner.Data.LastRadius))
        {
            spawner.Data.LastAngle = spawner.Data.Angle;
            spawner.Data.LastRadius = spawner.Data.Radius;
            spawner.UpdateTransforms();
        }
    }
}

SpawnData

public class SpawnData : MonoBehaviour
{
    public int Count;
    public float Radius, LastRadius, Angle, LastAngle;
}

我对代码进行了一些重构,但在大多数情况下,变化很小

关于c# - 如何调整一个克隆体的形状/尺寸以影响场景 View 中的所有其他克隆体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50167887/

相关文章:

c# - Html.Partial 搜索 aspx 而不是 cshtml

c# - 在 Unity 中设置 transform.up 时会发生什么

unity3d - Unity脚本在编辑器中运行,但无法构建

c# - Expression<Func<T,bool>> 在泛型方法中创建时添加不需要的 Convert

c# - 我在我的解决方案中添加了一个新的 XML 文件,但是当我按 F5 对其进行测试时,我在 Debug 文件夹中找不到 .xml 文件

c# - 识别图像中的对象

c# - 统一确定动态生成的网格的 uv 坐标

android - 将 Unity3d View 集成到 Android Activity 中

c# - 自定义检查器将值恢复为 Unity 中 Play 上之前的值

android - 如果我将代码块写在 "UNITY_EDITOR"中,Unity 是否会将代码块包含在 apk 中