我想通过调整一个来改变/调整场景视图中几个克隆对象的形状/尺寸。这个对象可以说是需要扩展的四元组或线条渲染器。例如,在场景视图中扩展(使用鼠标)一个游戏对象线渲染器时,所有其他克隆都会受到影响。我知道在克隆它之前调整一个对象的形状/尺寸要简单得多,也可以在预制件上进行此更改并应用于所有对象,但我需要看到动态更改,因为它们恰好使我的设计过程更有效。 我还想在需要时打开和关闭此功能。请查看我在this question中创建克隆的方式。
请注意,我不想在运行时实现此目的。
答案 0 :(得分:6)
如何调整一个克隆的形状/尺寸以影响所有其他克隆 场景视图中的克隆
在对组件进行修改时同步属性。要使用现有的代码库,我们还需要克隆修改后的对象,以确保在重新创建其他对象时不会销毁它。
这个对象可以说是需要的四元组或线条渲染器 扩展
因此,我们想知道 任何组件上的任何属性何时被修改。使用自定义脚本时,使用OnValidate
是微不足道的,但是使用密封组件LineRenderer
这有点麻烦。幸运的是,由于我们正在使用编辑器,因此我们可以访问它的一些核心功能。
具体来说,我们可以挂钩编辑器的Undo.postprocessModifications
事件,以便在对场景进行修改时获得回调。该委托将为我们提供一个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;
}
我对代码进行了一些重构,但在大多数情况下,更改很少