first commit

This commit is contained in:
lethanhsonvsp
2025-11-17 15:16:36 +07:00
commit a40d0921eb
17012 changed files with 2652386 additions and 0 deletions

View File

@@ -0,0 +1,352 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Splines;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace UnityEditor.Splines
{
[CustomEditor(typeof(SplineAnimate))]
[CanEditMultipleObjects]
class SplineAnimateEditor : UnityEditor.Editor
{
List<VisualElement> m_Roots = new ();
List<Slider> m_ProgressSliders = new ();
List<FloatField> m_ElapsedTimeFields = new ();
List<EnumField> m_ObjectForwardFields = new ();
List<EnumField> m_ObjectUpFields = new ();
SerializedProperty m_MethodProperty;
SerializedProperty m_ObjectForwardProperty;
SerializedProperty m_ObjectUpProperty;
SerializedProperty m_StartOffsetProperty;
SerializedObject m_TransformSO;
SplineAnimate m_SplineAnimate;
const string k_UxmlPath = "Packages/com.unity.splines/Editor/Resources/UI/UXML/splineanimate-inspector.uxml";
static VisualTreeAsset s_TreeAsset;
static StyleSheet s_ThemeStyleSheet;
SplineAnimate[] m_Components;
void OnEnable()
{
m_SplineAnimate = target as SplineAnimate;
if (m_SplineAnimate == null)
return;
m_SplineAnimate.Updated += OnSplineAnimateUpdated;
try {
m_MethodProperty = serializedObject.FindProperty("m_Method");
m_ObjectForwardProperty = serializedObject.FindProperty("m_ObjectForwardAxis");
m_ObjectUpProperty = serializedObject.FindProperty("m_ObjectUpAxis");
m_StartOffsetProperty = serializedObject.FindProperty("m_StartOffset");
}
catch (Exception)
{
return;
}
m_TransformSO = new SerializedObject(m_SplineAnimate.transform);
m_Components = targets.Select(x => x as SplineAnimate).Where(y => y != null).ToArray();
foreach (var animate in m_Components)
{
if (animate.Container != null)
animate.RecalculateAnimationParameters();
}
m_Roots.Clear();
m_ObjectForwardFields.Clear();
m_ObjectUpFields.Clear();
m_ProgressSliders.Clear();
m_ElapsedTimeFields.Clear();
EditorApplication.update += OnEditorUpdate;
Spline.Changed += OnSplineChange;
SplineContainer.SplineAdded += OnContainerSplineSetModified;
SplineContainer.SplineRemoved += OnContainerSplineSetModified;
}
void OnDisable()
{
if(m_SplineAnimate != null)
m_SplineAnimate.Updated -= OnSplineAnimateUpdated;
if (!EditorApplication.isPlaying)
{
foreach (var animate in m_Components)
{
if (animate.Container != null)
{
animate.RecalculateAnimationParameters();
animate.Restart(false);
}
}
}
EditorApplication.update -= OnEditorUpdate;
Spline.Changed -= OnSplineChange;
SplineContainer.SplineAdded -= OnContainerSplineSetModified;
SplineContainer.SplineRemoved -= OnContainerSplineSetModified;
}
void OnEditorUpdate()
{
if (!EditorApplication.isPlaying)
{
if (m_SplineAnimate.Container != null && m_SplineAnimate.IsPlaying)
{
m_SplineAnimate.Update();
RefreshProgressFields();
}
}
else if(m_SplineAnimate.IsPlaying)
RefreshProgressFields();
}
void OnSplineChange(Spline spline, int knotIndex, SplineModification modificationType)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var animate in m_Components)
{
if (animate.Container != null && animate.Container.Splines.Contains(spline))
animate.RecalculateAnimationParameters();
}
}
void OnContainerSplineSetModified(SplineContainer container, int spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var animate in m_Components)
{
if (animate.Container == container)
animate.RecalculateAnimationParameters();
}
}
public override VisualElement CreateInspectorGUI()
{
var root = new VisualElement();
if (s_TreeAsset == null)
s_TreeAsset = (VisualTreeAsset)AssetDatabase.LoadAssetAtPath(k_UxmlPath, typeof(VisualTreeAsset));
s_TreeAsset.CloneTree(root);
if (s_ThemeStyleSheet == null)
s_ThemeStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>($"Packages/com.unity.splines/Editor/Stylesheets/SplineAnimateInspector{(EditorGUIUtility.isProSkin ? "Dark" : "Light")}.uss");
root.styleSheets.Add(s_ThemeStyleSheet);
var methodField = root.Q<PropertyField>("method");
methodField.RegisterValueChangeCallback((_) => { RefreshMethodParamFields((SplineAnimate.Method)m_MethodProperty.enumValueIndex); });
RefreshMethodParamFields((SplineAnimate.Method)m_MethodProperty.enumValueIndex);
var objectForwardField = root.Q<EnumField>("object-forward");
objectForwardField.RegisterValueChangedCallback((evt) => OnObjectAxisFieldChange(evt, m_ObjectForwardProperty, m_ObjectUpProperty));
var objectUpField = root.Q<EnumField>("object-up");
objectUpField.RegisterValueChangedCallback((evt) => OnObjectAxisFieldChange(evt, m_ObjectUpProperty, m_ObjectForwardProperty));
var playButton = root.Q<Button>("play");
playButton.SetEnabled(!EditorApplication.isPlaying);
playButton.clicked += OnPlayClicked;
var pauseButton = root.Q<Button>("pause");
pauseButton.SetEnabled(!EditorApplication.isPlaying);
pauseButton.clicked += OnPauseClicked;
var resetButton = root.Q<Button>("reset");
resetButton.SetEnabled(!EditorApplication.isPlaying);
resetButton.clicked += OnResetClicked;
var progressSlider = root.Q<Slider>("normalized-progress");
progressSlider.SetEnabled(!EditorApplication.isPlaying);
progressSlider.RegisterValueChangedCallback((evt) => OnProgressSliderChange(evt.newValue));
var elapsedTimeField = root.Q<FloatField>("elapsed-time");
elapsedTimeField.SetEnabled(!EditorApplication.isPlaying);
elapsedTimeField.RegisterValueChangedCallback((evt) => OnElapsedTimeFieldChange(evt.newValue));
var startOffsetField = root.Q<PropertyField>("start-offset");
startOffsetField.RegisterValueChangeCallback((evt) =>
{
m_SplineAnimate.StartOffset = m_StartOffsetProperty.floatValue;
if (!EditorApplication.isPlayingOrWillChangePlaymode)
{
m_SplineAnimate.Restart(false);
OnElapsedTimeFieldChange(elapsedTimeField.value);
}
});
m_Roots.Add(root);
m_ProgressSliders.Add(progressSlider);
m_ElapsedTimeFields.Add(elapsedTimeField);
m_ObjectForwardFields.Add(objectForwardField);
m_ObjectUpFields.Add(objectUpField);
return root;
}
void RefreshMethodParamFields(SplineAnimate.Method method)
{
foreach (var root in m_Roots)
{
var durationField = root.Q<PropertyField>("duration");
var maxSpeedField = root.Q<PropertyField>("max-speed");
if (method == (int)SplineAnimate.Method.Time)
{
durationField.style.display = DisplayStyle.Flex;
maxSpeedField.style.display = DisplayStyle.None;
}
else
{
durationField.style.display = DisplayStyle.None;
maxSpeedField.style.display = DisplayStyle.Flex;
}
}
}
void RefreshProgressFields()
{
for (int i = 0; i < m_ProgressSliders.Count && i < m_ElapsedTimeFields.Count; ++i)
{
var progressSlider = m_ProgressSliders[i];
var elapsedTimeField = m_ElapsedTimeFields[i];
if (progressSlider == null || elapsedTimeField == null)
continue;
progressSlider.SetValueWithoutNotify(m_SplineAnimate.GetLoopInterpolation(false));
elapsedTimeField.SetValueWithoutNotify(m_SplineAnimate.ElapsedTime);
}
}
void OnProgressSliderChange(float progress)
{
m_SplineAnimate.Pause();
m_SplineAnimate.NormalizedTime = progress;
RefreshProgressFields();
}
void OnElapsedTimeFieldChange(float elapsedTime)
{
m_SplineAnimate.Pause();
m_SplineAnimate.ElapsedTime = elapsedTime;
RefreshProgressFields();
}
void OnObjectAxisFieldChange(ChangeEvent<Enum> changeEvent, SerializedProperty axisProp, SerializedProperty otherAxisProp)
{
if (changeEvent.newValue == null)
return;
var newValue = (SplineAnimate.AlignAxis)changeEvent.newValue;
var previousValue = (SplineAnimate.AlignAxis)changeEvent.previousValue;
// Swap axes if the picked value matches that of the other axis field
if (newValue == (SplineAnimate.AlignAxis)otherAxisProp.enumValueIndex)
{
otherAxisProp.enumValueIndex = (int)previousValue;
serializedObject.ApplyModifiedProperties();
}
// Prevent the user from configuring object's forward and up as opposite axes
if (((int) newValue) % 3 == otherAxisProp.enumValueIndex % 3)
{
axisProp.enumValueIndex = (int)previousValue;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
}
foreach (var objectForwardField in m_ObjectForwardFields)
objectForwardField.SetValueWithoutNotify((SplineComponent.AlignAxis)m_ObjectForwardProperty.enumValueIndex);
foreach (var objectUpField in m_ObjectUpFields)
objectUpField.SetValueWithoutNotify((SplineComponent.AlignAxis)m_ObjectUpProperty.enumValueIndex);
}
void OnPlayClicked()
{
if (!m_SplineAnimate.IsPlaying)
{
m_SplineAnimate.RecalculateAnimationParameters();
if (m_SplineAnimate.NormalizedTime == 1f)
m_SplineAnimate.Restart(true);
else
m_SplineAnimate.Play();
}
}
void OnPauseClicked()
{
m_SplineAnimate.Pause();
}
void OnResetClicked()
{
m_SplineAnimate.RecalculateAnimationParameters();
m_SplineAnimate.Restart(false);
RefreshProgressFields();
}
void OnSplineAnimateUpdated(Vector3 position, Quaternion rotation)
{
if (m_SplineAnimate == null)
return;
if (!EditorApplication.isPlaying)
{
m_TransformSO.Update();
var localPosition = position;
var localRotation = rotation;
if (m_SplineAnimate.transform.parent != null)
{
localPosition = m_SplineAnimate.transform.parent.worldToLocalMatrix.MultiplyPoint3x4(position);
localRotation = Quaternion.Inverse(m_SplineAnimate.transform.parent.rotation) * localRotation;
}
m_TransformSO.FindProperty("m_LocalPosition").vector3Value = localPosition;
m_TransformSO.FindProperty("m_LocalRotation").quaternionValue = localRotation;
m_TransformSO.ApplyModifiedProperties();
}
}
[DrawGizmo(GizmoType.Selected | GizmoType.Active)]
static void DrawSplineAnimateGizmos(SplineAnimate splineAnimate, GizmoType gizmoType)
{
if (splineAnimate.Container == null)
return;
const float k_OffsetGizmoSize = 0.15f;
splineAnimate.Container.Evaluate(splineAnimate.StartOffsetT, out var offsetPos, out var forward, out var up);
#if UNITY_2022_2_OR_NEWER
using (new Handles.DrawingScope(Handles.elementColor))
#else
using (new Handles.DrawingScope(SplineHandleUtility.knotColor))
#endif
if (Vector3.Magnitude(forward) <= Mathf.Epsilon)
{
if (splineAnimate.StartOffsetT < 1f)
forward = splineAnimate.Container.EvaluateTangent(Mathf.Min(1f, splineAnimate.StartOffsetT + 0.01f));
else
forward = splineAnimate.Container.EvaluateTangent(splineAnimate.StartOffsetT - 0.01f);
}
Handles.ConeHandleCap(-1, offsetPos, Quaternion.LookRotation(Vector3.Normalize(forward), up), k_OffsetGizmoSize * HandleUtility.GetHandleSize(offsetPos), EventType.Repaint);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7292ebc78c5d44eaa80048f8fd7d5fe6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
using System;
using UnityEngine;
using UnityEditor;
class SplineComponentEditor : Editor
{
static GUIStyle s_FoldoutStyle;
internal static readonly string k_Helpbox = L10n.Tr("Instantiated Objects need a SplineContainer target to be created.");
protected bool Foldout(bool foldout, GUIContent content)
{
return Foldout(foldout, content, false);
}
public static bool Foldout(bool foldout, GUIContent content, bool toggleOnLabelClick)
{
if (s_FoldoutStyle == null)
{
s_FoldoutStyle = new GUIStyle(EditorStyles.foldout);
s_FoldoutStyle.fontStyle = FontStyle.Bold;
}
return EditorGUILayout.Foldout(foldout, content, toggleOnLabelClick, s_FoldoutStyle);
}
internal struct LabelWidthScope : IDisposable
{
float previousWidth;
public LabelWidthScope(float width)
{
previousWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = width;
}
public void Dispose()
{
EditorGUIUtility.labelWidth = previousWidth;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9103eb7dcd041455886438d75625d19c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,104 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
// Multi-object selection is not supported
[CustomEditor(typeof(SplineContainer))]
class SplineContainerEditor : UnityEditor.Editor
{
SerializedProperty m_SplineProperty;
SerializedProperty splinesProperty => m_SplineProperty ??= serializedObject.FindProperty("m_Splines");
static GUIStyle s_HelpLabelStyle;
static GUIStyle HelpLabelStyle
{
get
{
if (s_HelpLabelStyle == null)
{
s_HelpLabelStyle = new GUIStyle(EditorStyles.helpBox);
s_HelpLabelStyle.padding = new RectOffset(2, 2, 2, 2);
}
return s_HelpLabelStyle;
}
}
static GUIContent m_HelpLabelContent;
const string k_HelpBoxIconPath = "SplineEditMode-Info";
static GUIContent m_HelpLabelContentIcon;
const string k_ComponentMessage = "Use the Spline Edit Mode in the Scene Tools Overlay to edit this Spline.";
public void OnEnable()
{
m_HelpLabelContent = EditorGUIUtility.TrTextContent(k_ComponentMessage);
m_HelpLabelContentIcon = new GUIContent(PathIcons.GetIcon(k_HelpBoxIconPath));
Undo.undoRedoPerformed += UndoRedoPerformed;
}
public void OnDisable()
{
Undo.undoRedoPerformed -= UndoRedoPerformed;
}
void UndoRedoPerformed()
{
foreach (var t in targets)
{
var container = t as SplineContainer;
if (container != null)
{
container.ClearCaches();
foreach (var spline in container.Splines)
spline.SetDirty(SplineModification.Default);
}
}
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// [SPLB-132] Reverting to custom helpbox as the default helpbox style as a trouble to handle custom icons
// when using a screen with PixelPerPoints different than 1. This is done in trunk by setting the
// Texture2d.pixelsPerPoints which is an internal property than cannot be access from here.
EditorGUILayout.BeginHorizontal(HelpLabelStyle);
EditorGUIUtility.SetIconSize(new Vector2(32f, 32f));
EditorGUILayout.LabelField(m_HelpLabelContentIcon,
GUILayout.Width(34), GUILayout.MinHeight(34), GUILayout.ExpandHeight(true));
EditorGUIUtility.SetIconSize(Vector2.zero);
EditorGUILayout.LabelField(m_HelpLabelContent,
new GUIStyle(EditorStyles.label){wordWrap = HelpLabelStyle.wordWrap, fontSize = HelpLabelStyle.fontSize, padding = new RectOffset(-2, 0, 0, 0)},
GUILayout.ExpandHeight(true));
EditorGUILayout.EndHorizontal();
SplineReorderableList.Get(splinesProperty).DoLayoutList();
serializedObject.ApplyModifiedProperties();
}
bool HasFrameBounds()
{
foreach (var o in targets)
{
var target = (SplineContainer) o;
foreach (var spline in target.Splines)
if (spline.Count > 0)
return true;
}
return false;
}
Bounds OnGetFrameBounds()
{
List<SplineInfo> splines = new List<SplineInfo>();
EditorSplineUtility.GetSplinesFromTargets(targets, splines);
return EditorSplineUtility.GetBounds(splines);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b17d23c06da64c139386c53a0b59281
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,304 @@
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Splines;
using UnityEngine.Splines.ExtrusionShapes;
namespace UnityEditor.Splines
{
[CustomEditor(typeof(SplineExtrude))]
[CanEditMultipleObjects]
class SplineExtrudeEditor : SplineComponentEditor
{
SerializedProperty m_Container;
SerializedProperty m_RebuildOnSplineChange;
SerializedProperty m_RebuildFrequency;
SerializedProperty m_SegmentsPerUnit;
SerializedProperty m_Capped;
SerializedProperty m_Radius;
SerializedProperty m_Range;
SerializedProperty m_Shape;
SerializedProperty m_UpdateColliders;
SerializedProperty m_FlipNormals;
static readonly GUIContent k_RangeContent = new GUIContent(L10n.Tr("Range"), L10n.Tr("The section of the Spline to extrude."));
static readonly GUIContent k_AdvancedContent = new GUIContent(L10n.Tr("Advanced"), L10n.Tr("Advanced Spline Extrude settings."));
static readonly GUIContent k_PercentageContent = new GUIContent(L10n.Tr("Percentage"), L10n.Tr("The section of the Spline to extrude in percentages."));
static readonly GUIContent k_ShapeContent = new GUIContent(L10n.Tr("Shape Extrude"), L10n.Tr("Shape Extrude settings."));
static readonly GUIContent k_ShapeSettings = EditorGUIUtility.TrTextContent("Settings");
static readonly GUIContent k_GeometryContent = new GUIContent(L10n.Tr("Geometry"), L10n.Tr("Mesh Geometry settings."));
static readonly string k_SourceSplineContainer = L10n.Tr("Source Spline Container");
static readonly string k_CapEnds = L10n.Tr("Cap Ends");
static readonly string k_AutoRefreshGeneration = L10n.Tr("Auto Refresh Generation");
static readonly string k_To = L10n.Tr("to");
static readonly string k_From = L10n.Tr("from");
SplineExtrude[] m_Components;
bool m_AnyMissingMesh;
protected void OnEnable()
{
m_Container = serializedObject.FindProperty("m_Container");
m_RebuildOnSplineChange = serializedObject.FindProperty("m_RebuildOnSplineChange");
m_RebuildFrequency = serializedObject.FindProperty("m_RebuildFrequency");
m_SegmentsPerUnit = serializedObject.FindProperty("m_SegmentsPerUnit");
m_Capped = serializedObject.FindProperty("m_Capped");
m_Radius = serializedObject.FindProperty("m_Radius");
m_Range = serializedObject.FindProperty("m_Range");
m_UpdateColliders = serializedObject.FindProperty("m_UpdateColliders");
m_Shape = serializedObject.FindProperty("m_Shape");
m_FlipNormals = serializedObject.FindProperty("m_FlipNormals");
m_Components = targets.Select(x => x as SplineExtrude).Where(y => y != null).ToArray();
m_AnyMissingMesh = false;
EditorSplineUtility.AfterSplineWasModified += OnSplineModified;
SplineContainer.SplineAdded += OnContainerSplineSetModified;
SplineContainer.SplineRemoved += OnContainerSplineSetModified;
}
void OnDisable()
{
EditorSplineUtility.AfterSplineWasModified -= OnSplineModified;
SplineContainer.SplineAdded -= OnContainerSplineSetModified;
SplineContainer.SplineRemoved -= OnContainerSplineSetModified;
}
void OnSplineModified(Spline spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var extrude in m_Components)
{
if (extrude.Container != null && extrude.Splines.Contains(spline))
extrude.Rebuild();
}
}
void OnContainerSplineSetModified(SplineContainer container, int spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var extrude in m_Components)
{
if (extrude.Container == container)
extrude.Rebuild();
}
}
void SetShapeType(ShapeType type)
{
foreach (var extrude in m_Components)
{
if (ShapeTypeUtility.GetShapeType(extrude.Shape) == type)
continue;
Undo.RecordObject(extrude, "Set Extrude Shape");
extrude.Shape = ShapeTypeUtility.CreateShape(type);
m_Shape.isExpanded = true;
}
}
bool CanCapEnds()
{
foreach (var extrude in m_Components)
{
if (!extrude.CanCapEnds)
return false;
}
return true;
}
void SetRebuildOnSplineChange(bool value)
{
foreach (var extrude in m_Components)
{
Undo.RecordObject(extrude, "Set Rebuild on Spline Change.");
extrude.RebuildOnSplineChange = value;
}
}
void CreateMeshAssets(SplineExtrude[] components)
{
foreach (var extrude in components)
{
if (!extrude.TryGetComponent<MeshFilter>(out var filter) || filter.sharedMesh == null)
filter.sharedMesh = extrude.CreateMeshAsset();
}
m_AnyMissingMesh = false;
}
void Rebuild()
{
foreach (var extrude in m_Components)
extrude.Rebuild();
}
public override void OnInspectorGUI()
{
serializedObject.Update();
m_AnyMissingMesh = m_Components.Any(x => x.TryGetComponent<MeshFilter>(out var filter) && filter.sharedMesh == null);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Container, new GUIContent(k_SourceSplineContainer, m_Container.tooltip));
if (m_Container.objectReferenceValue == null)
EditorGUILayout.HelpBox(k_Helpbox, MessageType.Warning);
// shape section
m_Shape.isExpanded = Foldout(m_Shape.isExpanded, k_ShapeContent, true);
if (m_Shape.isExpanded)
{
EditorGUI.indentLevel++;
EditorGUI.showMixedValue = m_Shape.hasMultipleDifferentValues;
EditorGUI.BeginChangeCheck();
var shapeType = ShapeTypeUtility.GetShapeType(m_Shape.managedReferenceValue);
shapeType = (ShapeType)EditorGUILayout.EnumPopup(L10n.Tr("Type"), shapeType);
if (EditorGUI.EndChangeCheck())
SetShapeType(shapeType);
EditorGUI.showMixedValue = false;
if (m_Shape.hasVisibleChildren)
EditorGUILayout.PropertyField(m_Shape, k_ShapeSettings, true);
EditorGUI.indentLevel--;
}
// https://unityeditordesignsystem.unity.com/patterns/content-organization recommends 8px spacing for
// vertical groups. padding already adds 4 so just nudge that up for a total of 8
EditorGUILayout.Space(4);
// geometry section
m_Radius.isExpanded = Foldout(m_Radius.isExpanded, k_GeometryContent, true);
if (m_Radius.isExpanded)
{
EditorGUI.indentLevel++;
EditorGUILayout.BeginHorizontal();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_RebuildOnSplineChange, new GUIContent(k_AutoRefreshGeneration, m_RebuildOnSplineChange.tooltip));
if (m_RebuildOnSplineChange.boolValue)
{
EditorGUI.BeginDisabledGroup(!m_RebuildOnSplineChange.boolValue);
using (new LabelWidthScope(80f))
EditorGUILayout.PropertyField(m_RebuildFrequency, new GUIContent() { text = L10n.Tr("Frequency") });
EditorGUI.EndDisabledGroup();
}
else
{
if (GUILayout.Button(new GUIContent(L10n.Tr("Regenerate"))))
Rebuild();
}
if (EditorGUI.EndChangeCheck() && !m_RebuildOnSplineChange.boolValue)
{
// This is needed to set m_RebuildRequested to the appropriate value.
SetRebuildOnSplineChange(m_RebuildOnSplineChange.boolValue);
}
EditorGUILayout.EndHorizontal();
if (m_AnyMissingMesh)
{
GUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(" ");
if (GUILayout.Button("Create Mesh Asset"))
CreateMeshAssets(m_Components);
GUILayout.EndHorizontal();
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Radius);
if (EditorGUI.EndChangeCheck())
m_Radius.floatValue = Mathf.Clamp(m_Radius.floatValue, .00001f, 1000f);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_SegmentsPerUnit);
if (EditorGUI.EndChangeCheck())
m_SegmentsPerUnit.floatValue = Mathf.Clamp(m_SegmentsPerUnit.floatValue, .00001f, 4096f);
var canCapEnds = CanCapEnds();
using (new EditorGUI.DisabledScope(!canCapEnds))
{
EditorGUILayout.PropertyField(m_Capped, new GUIContent(k_CapEnds, m_Capped.tooltip));
if (m_Capped.boolValue && !canCapEnds)
m_Capped.boolValue = false;
}
EditorGUILayout.PropertyField(m_FlipNormals);
EditorGUI.indentLevel--;
}
// advanced section
EditorGUILayout.Space(4);
m_Range.isExpanded = Foldout(m_Range.isExpanded, k_AdvancedContent, true);
if (m_Range.isExpanded)
{
EditorGUI.indentLevel++;
EditorGUI.showMixedValue = m_Range.hasMultipleDifferentValues;
var range = m_Range.vector2Value;
EditorGUI.BeginChangeCheck();
EditorGUILayout.MinMaxSlider(k_RangeContent, ref range.x, ref range.y, 0f, 1f);
if (EditorGUI.EndChangeCheck())
m_Range.vector2Value = range;
EditorGUI.indentLevel++;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(k_PercentageContent);
EditorGUI.indentLevel--;
EditorGUI.indentLevel--;
EditorGUI.BeginChangeCheck();
var newRange = new Vector2(range.x, range.y);
using (new LabelWidthScope(30f))
newRange.x = EditorGUILayout.FloatField(k_From, range.x * 100f) / 100f;
using (new LabelWidthScope(15f))
newRange.y = EditorGUILayout.FloatField(k_To, range.y * 100f) / 100f;
if (EditorGUI.EndChangeCheck())
{
newRange.x = Mathf.Min(Mathf.Clamp(newRange.x, 0f, 1f), range.y);
newRange.y = Mathf.Max(newRange.x, Mathf.Clamp(newRange.y, 0f, 1f));
m_Range.vector2Value = newRange;
}
EditorGUI.indentLevel++;
EditorGUILayout.EndHorizontal();
EditorGUI.showMixedValue = false;
EditorGUILayout.PropertyField(m_UpdateColliders);
EditorGUI.indentLevel--;
}
serializedObject.ApplyModifiedProperties();
if (EditorGUI.EndChangeCheck())
{
foreach (var extrude in m_Components)
extrude.Rebuild();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8ae5aa24d97048439e76e9392765c364
timeCreated: 1637689699

View File

@@ -0,0 +1,661 @@
using System.Linq;
using UnityEditor;
using UnityEditor.Splines;
using UnityEngine;
using UnityEngine.Splines;
class SplineInstantiateGizmoDrawer
{
[DrawGizmo(GizmoType.Selected | GizmoType.Active)]
static void DrawSplineInstantiateGizmos(SplineInstantiate scr, GizmoType gizmoType)
{
var instances = scr.instances;
foreach(var instance in instances)
{
var pos = instance.transform.position;
Handles.color = Color.red;
Handles.DrawAAPolyLine(3f,new []{ pos, pos + 0.25f * instance.transform.right });
Handles.color = Color.green;
Handles.DrawAAPolyLine(3f,new []{pos, pos + 0.25f * instance.transform.up});
Handles.color = Color.blue;
Handles.DrawAAPolyLine(3f,new []{pos, pos + 0.25f * instance.transform.forward});
}
}
}
[CustomPropertyDrawer (typeof(SplineInstantiate.InstantiableItem))]
class InstantiableItemDrawer : PropertyDrawer
{
static readonly string k_ProbabilityTooltip = L10n.Tr("Probability for that element to appear.");
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
var prefabProperty = property.FindPropertyRelative(nameof(SplineInstantiate.InstantiableItem.Prefab));
var probaProperty = property.FindPropertyRelative(nameof(SplineInstantiate.InstantiableItem.Probability));
var headerLine = ReserveSpace(EditorGUIUtility.singleLineHeight, ref rect);
using(new SplineInstantiateEditor.LabelWidthScope(0f))
EditorGUI.ObjectField(ReserveLineSpace(headerLine.width - 100, ref headerLine), prefabProperty, new GUIContent(""));
ReserveLineSpace(10, ref headerLine);
EditorGUI.LabelField(ReserveLineSpace(15, ref headerLine), new GUIContent("%", k_ProbabilityTooltip));
probaProperty.floatValue = EditorGUI.FloatField(ReserveLineSpace(60, ref headerLine), probaProperty.floatValue);
}
static Rect ReserveSpace(float height, ref Rect total)
{
Rect current = total;
current.height = height;
total.y += height;
return current;
}
static Rect ReserveLineSpace(float width, ref Rect total)
{
Rect current = total;
current.width = width;
total.x += width;
return current;
}
}
[CustomPropertyDrawer (typeof(SplineInstantiate.AlignAxis))]
class ItemAxisDrawer : PropertyDrawer
{
static int s_LastUpAxis;
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
{
var enumValue = property.intValue;
if(property.name == "m_Up")
{
property.intValue = (int)( (SplineInstantiate.AlignAxis)EditorGUI.EnumPopup(rect, label, (SplineInstantiate.AlignAxis)enumValue));
s_LastUpAxis = property.intValue;
}
else
{
property.intValue = (int)((SplineInstantiate.AlignAxis)EditorGUI.EnumPopup(rect, label, (SplineInstantiate.AlignAxis)enumValue,
(item) =>
{
int axisItem = (int)(SplineInstantiate.AlignAxis)item;
return !(axisItem == s_LastUpAxis || axisItem == (s_LastUpAxis + 3) % 6);
}));
}
}
}
[CustomEditor(typeof(SplineInstantiate),false)]
[CanEditMultipleObjects]
class SplineInstantiateEditor : SplineComponentEditor
{
enum SpawnType
{
Exact,
Random
}
SerializedProperty m_SplineContainer;
SerializedProperty m_ItemsToInstantiate;
SerializedProperty m_InstantiateMethod;
SerializedProperty m_Seed;
SerializedProperty m_Space;
SerializedProperty m_UpAxis;
SerializedProperty m_ForwardAxis;
SerializedProperty m_Spacing;
SerializedProperty m_PositionOffset;
SerializedProperty m_RotationOffset;
SerializedProperty m_ScaleOffset;
SerializedProperty m_AutoRefresh;
static readonly string[] k_SpacingTypesLabels = new []
{
L10n.Tr("Count"),
L10n.Tr("Spacing (Spline)"),
L10n.Tr("Spacing (Linear)")
};
//Setup Section
static readonly string k_Setup = L10n.Tr("Instantiated Object Setup");
static readonly string k_ObjectUp = L10n.Tr("Up Axis");
static readonly string k_ObjectUpTooltip = L10n.Tr("Object axis to use as Up Direction when instantiating on the Spline (default is Y).");
static readonly string k_ObjectForward = L10n.Tr("Forward Axis");
static readonly string k_ObjectForwardTooltip = L10n.Tr("Object axis to use as Forward Direction when instantiating on the Spline (default is Z).");
static readonly string k_AlignTo = L10n.Tr("Align To");
static readonly string k_AlignToTooltip = L10n.Tr("Define the space to use to orientate the instantiated object.");
static readonly string k_Instantiation = L10n.Tr("Instantiation");
static readonly string k_Method = L10n.Tr("Instantiate Method");
static readonly string k_MethodTooltip = L10n.Tr("How instances are generated along the spline.");
static readonly string k_Max = L10n.Tr("Max");
static readonly string k_Min = L10n.Tr("Min");
SpawnType m_SpacingType;
//Offsets
static readonly string k_PositionOffset = L10n.Tr("Position Offset");
static readonly string k_PositionOffsetTooltip = L10n.Tr("Whether or not to use a position offset.");
static readonly string k_RotationOffset = L10n.Tr("Rotation Offset");
static readonly string k_RotationOffsetTooltip = L10n.Tr("Whether or not to use a rotation offset.");
static readonly string k_ScaleOffset = L10n.Tr("Scale Offset");
static readonly string k_ScaleOffsetTooltip = L10n.Tr("Whether or not to use a scale offset.");
//Generation
static readonly string k_Generation = L10n.Tr("Generation");
static readonly string k_AutoRefresh = L10n.Tr("Auto Refresh Generation");
static readonly string k_AutoRefreshTooltip = L10n.Tr("Automatically refresh the instances when the spline or the values are changed.");
static readonly string k_Seed = L10n.Tr("Randomization Seed");
static readonly string k_SeedTooltip = L10n.Tr("Value used to initialize the pseudorandom number generator of the instances.");
static readonly string k_Randomize = L10n.Tr("Randomize");
static readonly string k_RandomizeTooltip = L10n.Tr("Compute a new randomization of the instances along the spline.");
static readonly string k_Regenerate = L10n.Tr("Regenerate");
static readonly string k_RegenerateTooltip = L10n.Tr("Regenerate the instances along the spline.");
static readonly string k_Clear = L10n.Tr("Clear");
static readonly string k_ClearTooltip = L10n.Tr("Clear the instances along the spline.");
static readonly string k_Bake = L10n.Tr("Bake Instances");
static readonly string k_BakeTooltip = L10n.Tr("Bake the instances in the SceneView for custom edition and destroy that SplineInstantiate component.");
bool m_PositionFoldout;
bool m_RotationFoldout;
bool m_ScaleFoldout;
enum OffsetType
{
Exact,
Random
};
SplineInstantiate[] m_Components;
SplineInstantiate[] components
{
get
{
//in case of multiple selection where some objects do not have a SplineInstantiate component, m_Components might be null
if (m_Components == null)
m_Components = targets.Select(x => x as SplineInstantiate).Where(y => y != null).ToArray();
return m_Components;
}
}
protected void OnEnable()
{
Spline.Changed += OnSplineChanged;
EditorSplineUtility.AfterSplineWasModified += OnSplineModified;
SplineContainer.SplineAdded += OnContainerSplineSetModified;
SplineContainer.SplineRemoved += OnContainerSplineSetModified;
}
bool Initialize()
{
if (m_Components != null && m_Components.Length > 0)
return true;
m_SplineContainer = serializedObject.FindProperty("m_Container");
m_ItemsToInstantiate = serializedObject.FindProperty("m_ItemsToInstantiate");
m_InstantiateMethod = serializedObject.FindProperty("m_Method");
m_Space = serializedObject.FindProperty("m_Space");
m_UpAxis = serializedObject.FindProperty("m_Up");
m_ForwardAxis = serializedObject.FindProperty("m_Forward");
m_Spacing = serializedObject.FindProperty("m_Spacing");
m_PositionOffset = serializedObject.FindProperty("m_PositionOffset");
m_RotationOffset = serializedObject.FindProperty("m_RotationOffset");
m_ScaleOffset = serializedObject.FindProperty("m_ScaleOffset");
m_Seed = serializedObject.FindProperty("m_Seed");
m_AutoRefresh = serializedObject.FindProperty("m_AutoRefresh");
if (m_Spacing != null)
m_SpacingType = Mathf.Approximately(m_Spacing.vector2Value.x, m_Spacing.vector2Value.y) ? SpawnType.Exact : SpawnType.Random;
else
m_SpacingType = SpawnType.Exact;
m_Components = targets.Select(x => x as SplineInstantiate).Where(y => y != null).ToArray();
return m_Components != null && m_Components.Length > 0;
}
void OnDisable()
{
m_Components = null;
Spline.Changed -= OnSplineChanged;
EditorSplineUtility.AfterSplineWasModified -= OnSplineModified;
SplineContainer.SplineAdded -= OnContainerSplineSetModified;
SplineContainer.SplineRemoved -= OnContainerSplineSetModified;
}
void OnSplineModified(Spline spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var instantiate in components)
{
if(instantiate == null)
continue;
if (instantiate.Container != null && instantiate.Container.Splines.Contains(spline))
instantiate.SetSplineDirty(spline);
}
}
void OnSplineChanged(Spline spline, int knotIndex, SplineModification modification)
{
OnSplineModified(spline);
}
void OnContainerSplineSetModified(SplineContainer container, int spline)
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return;
foreach (var instantiate in components)
{
if (instantiate.Container == container)
instantiate.UpdateInstances();
}
}
public override void OnInspectorGUI()
{
if(!Initialize())
return;
serializedObject.Update();
var dirtyInstances = false;
var updateInstances = false;
EditorGUILayout.PropertyField(m_SplineContainer);
if(m_SplineContainer.objectReferenceValue == null)
EditorGUILayout.HelpBox(k_Helpbox, MessageType.Warning);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_ItemsToInstantiate);
dirtyInstances = EditorGUI.EndChangeCheck();
DoSetupSection();
dirtyInstances |= DoInstantiateSection();
updateInstances |= DisplayOffsets();
EditorGUILayout.LabelField(k_Generation, EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Seed, new GUIContent(k_Seed, k_SeedTooltip));
var newSeed = EditorGUI.EndChangeCheck();
dirtyInstances |= newSeed;
updateInstances |= newSeed;
EditorGUILayout.PropertyField(m_AutoRefresh, new GUIContent(k_AutoRefresh, k_AutoRefreshTooltip));
EditorGUI.indentLevel--;
serializedObject.ApplyModifiedProperties();
EditorGUILayout.Separator();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.Space();
if (GUILayout.Button(new GUIContent(k_Randomize, k_RandomizeTooltip), GUILayout.MaxWidth(100f)))
{
Undo.SetCurrentGroupName("Change SplineInstantiate Seed");
var group = Undo.GetCurrentGroup();
foreach (var splineInstantiate in m_Components)
{
Undo.RecordObject(splineInstantiate, $"Change SplineInstantiate Seed for {splineInstantiate.gameObject.name}");
splineInstantiate.Randomize();
}
Undo.CollapseUndoOperations(group);
updateInstances = true;
}
var isInstancesCountGreaterThanZero = false;
foreach (var splineInstantiate in m_Components)
{
if (splineInstantiate.instances.Count > 0)
{
isInstancesCountGreaterThanZero = true;
break;
}
}
if (GUILayout.Button(new GUIContent(k_Regenerate, k_RegenerateTooltip), GUILayout.MaxWidth(100f)))
updateInstances = true;
GUI.enabled = isInstancesCountGreaterThanZero;
if (GUILayout.Button(new GUIContent(k_Clear, k_ClearTooltip), GUILayout.MaxWidth(100f)))
{
Undo.SetCurrentGroupName("Clear SplineInstantiate");
var group = Undo.GetCurrentGroup();
foreach (var splineInstantiate in m_Components)
{
Undo.RecordObject(splineInstantiate, $"Clear SplineInstantiate for {splineInstantiate.gameObject.name}");
splineInstantiate.Clear();
}
Undo.CollapseUndoOperations(group);
}
if (GUILayout.Button(new GUIContent(k_Bake, k_BakeTooltip), GUILayout.MaxWidth(100f)))
{
foreach (var splineInstantiate in m_Components)
BakeInstances(splineInstantiate);
}
GUI.enabled = true;
EditorGUILayout.Space();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Separator();
foreach (var splineInstantiate in m_Components)
{
if (dirtyInstances)
splineInstantiate.SetDirty();
if (updateInstances)
splineInstantiate.UpdateInstances();
}
if (dirtyInstances || updateInstances)
SceneView.RepaintAll();
}
void DoSetupSection()
{
EditorGUILayout.LabelField(k_Setup, EditorStyles.boldLabel);
GUILayout.Space(5f);
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_UpAxis, new GUIContent(k_ObjectUp, k_ObjectUpTooltip));
EditorGUILayout.PropertyField(m_ForwardAxis, new GUIContent(k_ObjectForward, k_ObjectForwardTooltip));
if(EditorGUI.EndChangeCheck())
{
//Insuring axis integrity
if(m_ForwardAxis.intValue == m_UpAxis.intValue || m_ForwardAxis.intValue == ( m_UpAxis.intValue + 3 ) % 6)
m_ForwardAxis.intValue = ( m_ForwardAxis.intValue + 1 ) % 6;
}
EditorGUILayout.PropertyField(m_Space, new GUIContent(k_AlignTo, k_AlignToTooltip));
EditorGUI.indentLevel--;
}
bool DoInstantiateSection()
{
var dirty = false;
Vector2 spacingV2 = m_Spacing.vector2Value;
EditorGUILayout.LabelField(k_Instantiation, EditorStyles.boldLabel);
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_InstantiateMethod, new GUIContent(k_Method, k_MethodTooltip), EditorStyles.boldFont );
if(EditorGUI.EndChangeCheck())
{
if(m_SpacingType == SpawnType.Random && m_InstantiateMethod.intValue == (int)SplineInstantiate.Method.LinearDistance)
m_Spacing.vector2Value = new Vector2(spacingV2.x, float.NaN);
dirty = true;
}
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(new GUIContent(k_SpacingTypesLabels[m_InstantiateMethod.intValue]));
EditorGUI.indentLevel--;
GUILayout.Space(2f);
EditorGUI.BeginChangeCheck();
float spacingX = m_Spacing.vector2Value.x;
var isExact = m_SpacingType == SpawnType.Exact;
if(isExact || m_InstantiateMethod.intValue != (int)SplineInstantiate.Method.LinearDistance)
{
using(new LabelWidthScope(30f))
spacingX = (SplineInstantiate.Method)m_InstantiateMethod.intValue == SplineInstantiate.Method.InstanceCount ?
EditorGUILayout.IntField(new GUIContent(isExact ? string.Empty : k_Min), (int)m_Spacing.vector2Value.x, GUILayout.MinWidth(50f)) :
EditorGUILayout.FloatField(new GUIContent(isExact ? L10n.Tr("Dist") : k_Min), m_Spacing.vector2Value.x, GUILayout.MinWidth(50f));
}
if(isExact)
{
spacingV2 = new Vector2(spacingX, spacingX);
}
else if(m_InstantiateMethod.intValue != (int)SplineInstantiate.Method.LinearDistance)
{
using(new LabelWidthScope(30f))
{
var spacingY = (SplineInstantiate.Method)m_InstantiateMethod.intValue == SplineInstantiate.Method.InstanceCount ?
EditorGUILayout.IntField(new GUIContent(k_Max), (int)m_Spacing.vector2Value.y, GUILayout.MinWidth(50f)) :
EditorGUILayout.FloatField(new GUIContent(k_Max), m_Spacing.vector2Value.y, GUILayout.MinWidth(50f));
if(spacingX > m_Spacing.vector2Value.y)
spacingY = spacingX;
else if(spacingY < m_Spacing.vector2Value.x)
spacingX = spacingY;
spacingV2 = new Vector2(spacingX, spacingY);
}
}
if(EditorGUI.EndChangeCheck())
m_Spacing.vector2Value = spacingV2;
EditorGUI.BeginChangeCheck();
if(m_InstantiateMethod.intValue != (int)SplineInstantiate.Method.LinearDistance)
m_SpacingType = (SpawnType)EditorGUILayout.EnumPopup(m_SpacingType, GUILayout.MinWidth(30f));
else
m_SpacingType = (SpawnType)EditorGUILayout.Popup(m_SpacingType == SpawnType.Exact ? 0 : 1,
new []{"Exact", "Auto"}, GUILayout.MinWidth(30f));
if(EditorGUI.EndChangeCheck())
{
if(m_SpacingType == SpawnType.Exact)
m_Spacing.vector2Value = new Vector2(spacingV2.x, spacingV2.x);
else if(m_InstantiateMethod.intValue == (int)SplineInstantiate.Method.LinearDistance)
m_Spacing.vector2Value = new Vector2(spacingV2.x, float.NaN);
dirty = true;
}
EditorGUILayout.EndHorizontal();
return dirty;
}
bool DoOffsetProperties(
SerializedProperty offsetProperty, GUIContent content, bool foldoutValue, out bool newFoldoutValue)
{
bool changed = false;
newFoldoutValue = foldoutValue;
EditorGUILayout.BeginHorizontal();
using(new LabelWidthScope(0f))
{
var setupProperty = offsetProperty.FindPropertyRelative("setup");
var setup = (SplineInstantiate.Vector3Offset.Setup)setupProperty.intValue;
var hasOffset = ( setup & SplineInstantiate.Vector3Offset.Setup.HasOffset ) != 0;
EditorGUI.BeginChangeCheck();
hasOffset = EditorGUILayout.Toggle(hasOffset, GUILayout.MaxWidth(20f));
if(EditorGUI.EndChangeCheck())
{
if(hasOffset)
setup |= SplineInstantiate.Vector3Offset.Setup.HasOffset;
else
setup &= ~SplineInstantiate.Vector3Offset.Setup.HasOffset;
setupProperty.intValue = (int)setup;
changed = true;
}
EditorGUILayout.Space(10f);
using(new EditorGUI.DisabledScope(!hasOffset))
{
newFoldoutValue = Foldout(foldoutValue, content, hasOffset) && hasOffset;
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
if(newFoldoutValue)
{
EditorGUILayout.BeginHorizontal();
var hasCustomSpace = ( setup & SplineInstantiate.Vector3Offset.Setup.HasCustomSpace ) != 0;
EditorGUI.BeginChangeCheck();
var space = m_Space.intValue < 1 ? "Spline Element" : m_Space.intValue == 1 ? "Spline Object" : "World";
hasCustomSpace = EditorGUILayout.Toggle(new GUIContent("Override space", L10n.Tr("Override current space (" + space + ")")), hasCustomSpace);
if(EditorGUI.EndChangeCheck())
{
if(hasCustomSpace)
setup |= SplineInstantiate.Vector3Offset.Setup.HasCustomSpace;
else
setup &= ~SplineInstantiate.Vector3Offset.Setup.HasCustomSpace;
setupProperty.intValue = (int)setup;
changed = true;
}
var spaceProperty = offsetProperty.FindPropertyRelative("space");
using(new EditorGUI.DisabledScope(!hasCustomSpace))
{
var type = (SplineInstantiate.OffsetSpace)spaceProperty.intValue;
EditorGUI.BeginChangeCheck();
type = (SplineInstantiate.OffsetSpace)EditorGUILayout.EnumPopup(type);
if(EditorGUI.EndChangeCheck())
{
spaceProperty.intValue = (int)type;
changed = true;
}
}
EditorGUILayout.EndHorizontal();
var minProperty = offsetProperty.FindPropertyRelative("min");
var maxProperty = offsetProperty.FindPropertyRelative("max");
var minPropertyValue = minProperty.vector3Value;
var maxPropertyValue = maxProperty.vector3Value;
float min, max;
SerializedProperty randomProperty;
for(int i = 0; i < 3; i++)
{
string label = i == 0 ? "X" : i == 1 ? "Y" : "Z";
EditorGUILayout.BeginHorizontal();
using(new LabelWidthScope(30f))
EditorGUILayout.LabelField(label);
randomProperty = offsetProperty.FindPropertyRelative("random"+label);
GUILayout.FlexibleSpace();
if(randomProperty.boolValue)
{
EditorGUI.BeginChangeCheck();
using(new LabelWidthScope(30f))
{
min = EditorGUILayout.FloatField("from", minPropertyValue[i], GUILayout.MinWidth(95f), GUILayout.MaxWidth(95f));
max = EditorGUILayout.FloatField(" to", maxPropertyValue[i], GUILayout.MinWidth(95f), GUILayout.MaxWidth(95f));
}
if(EditorGUI.EndChangeCheck())
{
if(min > maxPropertyValue[i])
maxPropertyValue[i] = min;
if(max < minPropertyValue[i])
minPropertyValue[i] = max;
minPropertyValue[i] = min;
maxPropertyValue[i] = max;
minProperty.vector3Value = minPropertyValue;
maxProperty.vector3Value = maxPropertyValue;
changed = true;
}
}
else
{
EditorGUI.BeginChangeCheck();
using(new LabelWidthScope(30f))
min = EditorGUILayout.FloatField("is ", minPropertyValue[i], GUILayout.MinWidth(193f), GUILayout.MaxWidth(193f));
if(EditorGUI.EndChangeCheck())
{
minPropertyValue[i] = min;
if(min > maxPropertyValue[i])
maxPropertyValue[i] = min;
minProperty.vector3Value = minPropertyValue;
maxProperty.vector3Value = maxPropertyValue;
changed = true;
}
}
EditorGUI.BeginChangeCheck();
var isOffsetRandom = randomProperty.boolValue ? OffsetType.Random : OffsetType.Exact;
using(new LabelWidthScope(0f))
isOffsetRandom = (OffsetType)EditorGUILayout.EnumPopup(isOffsetRandom,GUILayout.MinWidth(100f), GUILayout.MaxWidth(200f));
if(EditorGUI.EndChangeCheck())
{
randomProperty.boolValue = isOffsetRandom == OffsetType.Random;
changed = true;
}
EditorGUILayout.EndHorizontal();
}
}
}
}
return changed;
}
bool DisplayOffsets()
{
var updateNeeded = DoOffsetProperties(m_PositionOffset, new GUIContent(k_PositionOffset, k_PositionOffsetTooltip), m_PositionFoldout, out m_PositionFoldout);
updateNeeded |= DoOffsetProperties(m_RotationOffset, new GUIContent(k_RotationOffset, k_RotationOffsetTooltip), m_RotationFoldout, out m_RotationFoldout);
updateNeeded |= DoOffsetProperties(m_ScaleOffset, new GUIContent(k_ScaleOffset, k_ScaleOffsetTooltip), m_ScaleFoldout, out m_ScaleFoldout);
return updateNeeded;
}
/// <summary>
/// Bake the instances into the scene and destroy this SplineInstantiate component.
/// Making changes to the spline after baking will not affect the instances anymore.
/// </summary>
void BakeInstances(SplineInstantiate splineInstantiate)
{
Undo.SetCurrentGroupName("Baking SplineInstantiate instances");
var group = Undo.GetCurrentGroup();
splineInstantiate.UpdateInstances();
for (int i = 0; i < splineInstantiate.instances.Count; ++i)
{
var newInstance = splineInstantiate.instances[i];
newInstance.name = "Instance-" + i;
newInstance.hideFlags = HideFlags.None;
newInstance.transform.SetParent(splineInstantiate.gameObject.transform, true);
Undo.RegisterCreatedObjectUndo(newInstance, "Baking instance");
}
splineInstantiate.instances.Clear();
if(splineInstantiate.InstancesRoot != null)
Undo.DestroyObjectImmediate(splineInstantiate.InstancesRoot);
Undo.DestroyObjectImmediate(splineInstantiate);
Undo.CollapseUndoOperations(group);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c574a2f698921d1458f6dfba4b5e1229
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: