如何调整一个克隆的形状/尺寸以影响场景视图中的所有其他克隆 [英] How can I adjust shape/dimensions of one clone to affect all other clones in the scene view
问题描述
我想通过调整一个来更改/调整场景视图中多个克隆对象的形状/尺寸.该对象可以说是需要扩展的四边形或线渲染器.例如,当一个游戏对象线条渲染器在场景视图中扩展(使用鼠标)时,所有其他克隆都将受到影响.我知道在克隆之前调整一个对象的形状/尺寸要简单得多,而且可以在预制件上进行此更改并将其应用于所有对象,但是我需要查看它们发生的动态变化,以使我的设计过程更加灵活.有效的. 我还想在需要时打开和关闭此功能.请在
I would like to change/adjust the shape/dimensions of several cloned objects within the scene view by adjusting one. This object could be say a quad or a line renderer that needs to be extended. For example as one game objects line renderer is extended (using the mouse) in the scene view, all other clones are affected. I know that it's a lot simpler to adjust shape/dimensions of one object before cloning it, also this change can be made on a prefab and applied to all, but I need to see the dynamic changes as they happen to make my design process more effective. I would also like to turn this functionality on and off, when the need arises. Please see how I created my clones in this question.
请注意,我不想在运行时实现这一目标.
Note that I don't want to achieve this at runtime.
推荐答案
如何调整一个克隆的形状/尺寸以影响其他所有克隆 在场景视图中克隆
How can I adjust shape/dimensions of one clone to affect all other clones in the scene view
在修改组件时同步属性.为了使用现有的代码库,我们还需要克隆修改后的对象,以确保在我们重新创建其他对象时不会将其销毁.
Synchronize properties when modifications to the component are made. To work with the existing code-base, we'll also need to clone the modified object to ensure that it's not destroyed when we re-create the other objects.
该对象可以是需要渲染为四边形或线条的渲染器 扩展
This object could be say a quad or a line renderer that needs to be extended
所以我们想知道 any 组件上的 any 属性何时被修改.使用自定义脚本,对于OnValidate
来说是微不足道的,但是对于像LineRenderer
这样的密封组件,这有点棘手.幸运的是,由于我们正在使用编辑器,因此可以使用其一些核心功能.
So we want to know when any property on any component has been modified. With custom scripts it's trivial with OnValidate
, but with a sealed component such LineRenderer
it's a little trickier. Fortunately, since we're working with the Editor we have access to some of its core features.
具体来说,只要对场景进行修改,我们就可以加入到Editor的Undo.postprocessModifications
事件中以获取回调.该委托将为我们提供一个UndoPropertyModification
数组,该数组将包含一个已修改组件属性的列表,可用于通过EditorUtility.CopySerializedIfDifferent
与其他对象进行同步.
Specifically, we can hook into the Editor's Undo.postprocessModifications
event to get a callback whenever modifications are made to the scene. This delegate will provide us with an UndoPropertyModification
array that will contain a list of modified component properties which we can use to synchronize the other objects via EditorUtility.CopySerializedIfDifferent
.
CircleSpawn
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
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
SpawnData
public class SpawnData : MonoBehaviour
{
public int Count;
public float Radius, LastRadius, Angle, LastAngle;
}
我已经对代码进行了一些重构,但是在大多数情况下,更改很少.
这篇关于如何调整一个克隆的形状/尺寸以影响场景视图中的所有其他克隆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!