3

我创建了一个编辑器工具,它在一个圆圈中创建游戏对象,彼此间隔相等(下面的代码)。它有一个创建按钮,所以在创建之前调整参数。我希望看到在场景视图中动态进行的更改。如何使用滑块调整以下参数:

  1. 使用对象计数滑块创建或销毁游戏对象,同时调整以使对象保持相同的角度分开

  2. 通过更改半径滑块动态调整游戏对象

  3. 使用角度滑块旋转圆上的所有对象(如旋转轮子)

圈子生成

public class CircleSpawn : MonoBehaviour {
    public float radius;
    public int numOfItems;
    public GameObject clonedObject;
    public List<GameObject> spawnedObjects;
}

CircleSpawn编辑器

[CustomEditor(typeof(CircleSpawn))]
public class CircleSpawnEditor : Editor
{
    public override void OnInspectorGUI()
    {
        var tar = (CircleSpawn)target;

        //set its values
        tar.radius = EditorGUILayout.FloatField("Radius:", tar.radius);
        tar.numOfItems = EditorGUILayout.IntField("Number of Items:", tar.numOfItems);
        tar.clonedObject = (GameObject)EditorGUILayout.ObjectField(tar.clonedObject,
                   typeof(GameObject), true);

        //Inspector button for creating the objects in the Editor
        if (GUILayout.Button("Create"))
        {
            //clean up old objects
            if (tar.spawnedObjects != null)
            {
                foreach (var ob in tar.spawnedObjects)
                {
                    DestroyImmediate(ob);
                }
            }
            tar.spawnedObjects = new List<GameObject>();

            float angleBetween = 360.0f / tar.numOfItems;
            float angle = 0;
            for (int i = 0; i <= tar.numOfItems; i++)
            {
                //for each object, find a rotation and position
                var rot = Quaternion.Euler(0, 0, angle);
                var localPos = rot * Vector3.right * tar.radius;
                tar.spawnedObjects.Add(Instantiate(tar.clonedObject,
                tar.transform.position + localPos, rot));
                angle += angleBetween;
            }
        }
    }
}
4

2 回答 2

4

使用对象计数滑块创建或销毁游戏对象,同时调整以使对象保持相同的角度分开

在以下情况下使用IntSliderfornumOfItems重新创建对象:

numOfItems != spawnedObjects.Count

通过更改半径滑块动态调整游戏对象

使用Sliderfor radius,当它改变时,迭代spawnedObjects并移动它们:

pos = rot * Vector3.right * tar.radius

使用角度滑块旋转圆上的所有对象(如旋转轮子)

使用Sliderfor spin,当它改变时,迭代spawnedObjects并旋转它们:

rot = Quaternion.Euler(0, 0, tar.spin + angle)

圈子生成:

public class CircleSpawn : MonoBehaviour
{
    public float radius, radiusLast, spin, spinLast;
    public int numOfItems;
    public GameObject clonedObject;
    public List<GameObject> spawnedObjects;
}

CircleSpawn编辑器:

[CustomEditor(typeof(CircleSpawn))]
public class CircleSpawnEditor : Editor
{
    public override void OnInspectorGUI ()
    {
        var tar = (CircleSpawn)target;    
        EditorGUILayout.LabelField("Radius"); // Set as required
        tar.radius = EditorGUILayout.Slider(tar.radius, 0f, 100f);          
        EditorGUILayout.LabelField("Angle"); // Set as required
        tar.spin = EditorGUILayout.Slider(tar.spin, 0f, 360f);              
        EditorGUILayout.LabelField("Number of Items"); // Set as required
        tar.numOfItems = EditorGUILayout.IntSlider(tar.numOfItems, 0, 36);  
        EditorGUILayout.LabelField("Object");
        tar.clonedObject = (GameObject)EditorGUILayout.ObjectField(tar.clonedObject, 
        typeof(GameObject), true);

        float angle, angleBetween = 360.0f / tar.numOfItems;

        if (tar.spawnedObjects == null)
            tar.spawnedObjects = new List<GameObject>();

        // Solution #1    
        if (tar.spawnedObjects.Count != tar.numOfItems)
        {
            foreach (var ob in tar.spawnedObjects)
                DestroyImmediate(ob);

            tar.spawnedObjects.Clear();
            angle = 0f;

            for (int i = 0; i < tar.numOfItems; i++)
            {
                var rot = Quaternion.Euler(0f, 0f, tar.spin + angle);
                var localPos = rot * Vector3.right * tar.radius;    
                tar.spawnedObjects.Add(Instantiate(tar.clonedObject,
                tar.transform.position + localPos, rot));
                angle += angleBetween;
            }
        }

        // Solutions #2 & 3    
        if (!Mathf.Approximately(tar.spin, tar.spinLast) ||
            !Mathf.Approximately(tar.radius, tar.radiusLast))
        {
            tar.spinLast = tar.spin;
            tar.radiusLast = tar.radius;    
            angle = 0f;

            for (int i = 0; i < tar.numOfItems; i++)
            {
                var rot = Quaternion.Euler(0f, 0f, tar.spin + angle);
                var localPos = rot * Vector3.right * tar.radius;    
                tar.spawnedObjects[i].transform.position = 
                tar.transform.position + localPos;
                tar.spawnedObjects[i].transform.rotation = rot;
                angle += angleBetween;
            }
        }
    }
}
于 2018-04-19T14:31:19.507 回答
2

您的代码中存在一些问题:

1 .注意如何创建了一个额外的预制件。基本上,创建的项目数numOfItems + 1不是numOfItems.

那是因为这条线:

for (int i = 0; i <= tar.numOfItems; i++)

那应该i < tar.numOfItems不是i <= tar.numOfItems;.

2 .您没有清除列表。销毁物品后,也用 清除列表tar.Clear();。如果您不这样做,您将获得包含空项目的列表,这些项目将继续增长。


要完成您的问题,您将需要一种方法来检测半径和numOfItems值何时发生变化。您需要类似于该Update功能的东西。如果您从而Editor不是从MonoBehaviour.

Update要在从 派生脚本时获得类似于函数事件的内容Editor,请订阅函数中的EditorApplication.update事件并在OnEnable函数中取消订阅它OnDisable。这应该可以处理您的#1#2问题

要同时旋转所有这些,请使用该transform.RotateAround功能。这应该可以处理您的#3问题。

我昨天花了一些时间看看这有多容易,并按照我上面提到的一切,下面是我想出的:


下面是代码。它可以改进。大多数事情都是可重复的,所以我尽可能多地将它们放在一个函数中,并enum根据滑块来确定要执行哪些事情。事情将代码缩短了几乎是原始大小的两倍。您不必直接使用此代码,但您可以使用它来了解我所做的事情,如果您愿意,可以制作您的代码:

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(CircleSpawn))]
public class CircleSpawnEditor : Editor
{
    private CircleSpawn targetObj;
    private static float previousRadius;
    private static int previousNumOfItems;
    private static float previousAngleIncrement;

    private static float angleIncrement = 0;

    public override void OnInspectorGUI()
    {
        var tar = (CircleSpawn)target;
        targetObj = tar;

        makeSlider("Radius", tar.radius, 0f, 10, 24, UpdateType.RADIUS);

        makeSlider("Number of Items", tar.numOfItems, 0f, 100, 24, UpdateType.RESPAWN);

        makeSlider("Angle", angleIncrement, 0f, 360, 24, UpdateType.ANGLE);

        //set its values
        tar.radius = EditorGUILayout.FloatField("Radius:", tar.radius);
        tar.numOfItems = EditorGUILayout.IntField("Number of Items:", tar.numOfItems);
        angleIncrement = EditorGUILayout.FloatField("Angle:", angleIncrement);

        tar.clonedObject = (GameObject)EditorGUILayout.ObjectField(tar.clonedObject,
                   typeof(GameObject), true);

        //Inspector button for creating the objects in the Editor
        if (GUILayout.Button("Create"))
        {
            RespawnNumOfItems();
        }
    }

    void makeSlider(string label, float value, float min, float max, int space, UpdateType updateType)
    {
        GUILayout.Space(2);
        GUILayout.Label(label);
        if (updateType == UpdateType.RADIUS)
            targetObj.radius = GUILayout.HorizontalSlider(targetObj.radius, min, max);
        if (updateType == UpdateType.RESPAWN)
            targetObj.numOfItems = (int)GUILayout.HorizontalSlider(targetObj.numOfItems, min, max);
        if (updateType == UpdateType.ANGLE)
            angleIncrement = GUILayout.HorizontalSlider(angleIncrement, min, max);

        GUILayout.BeginHorizontal();
        var defaultAlignment3 = GUI.skin.label.alignment;
        GUILayout.Label(min.ToString());
        GUI.skin.label.alignment = TextAnchor.UpperRight;
        GUILayout.Label(max.ToString());
        GUI.skin.label.alignment = defaultAlignment3;
        GUILayout.EndHorizontal();
        GUILayout.Space(space);
    }

    void OnEnable()
    {
        EditorApplication.update += Update;
    }
    void OnDisable()
    {
        EditorApplication.update -= Update;
    }

    void Update()
    {
        if (targetObj != null)
        {
            float clampedNewRadius = Mathf.Clamp(targetObj.radius, 0f, float.MaxValue);

            //Check if Radius changed
            if (RadiusChanged())
            {
                //Debug.Log("Radius Changed: " + targetObj.radius);
                previousRadius = clampedNewRadius;
                targetObj.radius = clampedNewRadius;
                UpdateRadius();
            }


            int clampedNewNumOfItems = Mathf.Clamp(targetObj.numOfItems, 0, int.MaxValue);

            //Check if NumOfItems changed
            if (NumOfItemsChanged())
            {
                //Debug.Log("NumOfItems Changed: " + previousNumOfItems);
                previousNumOfItems = clampedNewNumOfItems;
                targetObj.numOfItems = clampedNewNumOfItems;
                RespawnNumOfItems();
            }

            float clampedAngle = Mathf.Clamp(angleIncrement, 0, int.MaxValue);

            //Check if Angle changed
            if (AngleChanged())
            {
                //Debug.Log("Angle Changed: " + previousAngleIncrement);
                UpdateAngle();
                previousAngleIncrement = clampedAngle;
                angleIncrement = clampedAngle;
            }
        }
    }

    private void UpdateAngle()
    {
        UpdateTransform(UpdateType.ANGLE);
    }

    private void RespawnNumOfItems()
    {
        if (targetObj.spawnedObjects == null)
            targetObj.spawnedObjects = new List<GameObject>();

        //clean up old objects
        if (targetObj.spawnedObjects != null)
        {
            // Debug.LogWarning("Destroyed");
            foreach (var ob in targetObj.spawnedObjects)
            {
                DestroyImmediate(ob);
            }
        }

        //Clear list
        targetObj.spawnedObjects.Clear();
        //Debug.LogWarning("Cleared List");

        UpdateTransform(UpdateType.RESPAWN);
    }

    private void UpdateRadius()
    {
        UpdateTransform(UpdateType.RADIUS);
    }

    void UpdateTransform(UpdateType updateType)
    {
        float angleBetween = 360.0f / targetObj.numOfItems;
        float angle = 0;

        if (targetObj != null)

            for (int i = 0; i < targetObj.numOfItems; i++)
            {
                //For each object, find a rotation and position
                var rot = Quaternion.Euler(0, 0, angle);
                var localPos = rot * Vector3.right * targetObj.radius;

                //Debug.LogWarning("Updated");

                if (updateType == UpdateType.RADIUS)
                {
                    //Make sure that loop is within range
                    if (targetObj.spawnedObjects != null && i < targetObj.spawnedObjects.Count)
                    {
                        GameObject obj = targetObj.spawnedObjects[i];
                        if (obj != null)
                        {
                            Transform trans = obj.transform;
                            trans.position = targetObj.transform.position + localPos;
                            trans.rotation = rot;
                        }
                    }
                }
                else if (updateType == UpdateType.RESPAWN)
                {
                    if (targetObj.clonedObject != null)
                    {
                        GameObject objToAdd = Instantiate(targetObj.clonedObject, Vector3.zero, Quaternion.identity);
                        objToAdd.transform.position = targetObj.transform.position + localPos;
                        objToAdd.transform.rotation = rot;
                        targetObj.spawnedObjects.Add(objToAdd);
                    }
                    else
                    {
                        // Debug.LogError("Please assign the clonedObject prefab in the Scene");
                    }
                }
                else if (updateType == UpdateType.ANGLE)
                {
                    //Make sure that loop is within range
                    if (targetObj.spawnedObjects != null && i < targetObj.spawnedObjects.Count)
                    {
                        GameObject obj = targetObj.spawnedObjects[i];
                        if (obj != null)
                        {
                            Transform trans = obj.transform;
                            Vector3 tagetPoint = targetObj.transform.position;
                            //Decide if we should rotate left or rigt
                            if (previousAngleIncrement > angleIncrement)
                                trans.RotateAround(tagetPoint, Vector3.forward, angleIncrement);
                            else
                                trans.RotateAround(tagetPoint, -Vector3.forward, angleIncrement);
                        }

                    }
                }
                if (updateType != UpdateType.ANGLE)
                    angle += angleBetween;
            }

        //Uncomment to test auto angle rotation over frame
        //testAngle();
    }

    void testAngle()
    {
        float speed = 0.005f;
        angleIncrement = (float)EditorApplication.timeSinceStartup * speed;
    }

    private bool RadiusChanged()
    {
        bool changed = !Mathf.Approximately(targetObj.radius, previousRadius)
            && !(targetObj.radius < 0);
        return changed;
    }

    private bool NumOfItemsChanged()
    {
        bool changed = (targetObj.numOfItems != previousNumOfItems)
            && !(targetObj.numOfItems < 0);
        return changed;
    }


    private bool AngleChanged()
    {
        bool changed = !Mathf.Approximately(angleIncrement, previousAngleIncrement)
           && !(angleIncrement < 0);
        return changed;
    }

    public enum UpdateType
    {
        RADIUS, RESPAWN, ANGLE
    }
}
于 2018-04-20T12:49:02.563 回答