/************************************************************************************************************************/ /// <inheritdoc/> public override void OnEnable(int index) { base.OnEnable(index); AnimancerUtilities.NewIfNull(ref _NewBindingPaths); if (Animation == null) { _NewBindingPaths.Clear(); } _OldBindingPathsDisplay = CreateReorderableStringList(OldBindingPaths, "Old Binding Paths"); _NewBindingPathsDisplay = CreateReorderableStringList(_NewBindingPaths, "New Binding Paths", (area, i) => { var color = GUI.color; var path = _NewBindingPaths[i]; if (path != OldBindingPaths[i]) { GUI.color = new Color(0.15f, 0.7f, 0.15f, 1); } path = EditorGUI.TextField(area, path); GUI.color = color; return(path); }); }
/************************************************************************************************************************/ /// <summary>Snaps the `seconds` value to the nearest multiple of the <see cref="AnimationClip.frameRate"/>.</summary> public void SnapToFrameRate(SerializableEventSequenceDrawer.Context context, ref float seconds) { if (AnimancerUtilities.TryGetFrameRate(context.TransitionContext.Transition, out var frameRate)) { seconds = AnimancerUtilities.Round(seconds, 1f / frameRate); } }
/************************************************************************************************************************/ private void OnEnable() { titleContent = new GUIContent(Name); Instance = this; AnimancerUtilities.NewIfNull(ref _ModifySprites); AnimancerUtilities.NewIfNull(ref _RenameSprites); AnimancerUtilities.NewIfNull(ref _GenerateSpriteAnimations); AnimancerUtilities.NewIfNull(ref _RemapSpriteAnimation); AnimancerUtilities.NewIfNull(ref _RemapAnimationBindings); _Panels = new Panel[] { _ModifySprites, _RenameSprites, _GenerateSpriteAnimations, _RemapSpriteAnimation, _RemapAnimationBindings, }; _PanelNames = new string[_Panels.Length]; for (int i = 0; i < _Panels.Length; i++) { var panel = _Panels[i]; panel.OnEnable(i); _PanelNames[i] = panel.Name; } Undo.undoRedoPerformed += Repaint; OnSelectionChange(); }
/************************************************************************************************************************/ #if UNITY_EDITOR /************************************************************************************************************************/ /// <summary>[Editor-Only] /// Sets the character's starting sprite in Edit Mode so you can see it while working in the scene. /// </summary> /// <remarks> /// Called by the Unity Editor in Edit Mode whenever an instance of this script is loaded or a value is changed /// in the Inspector. /// </remarks> private void OnValidate() { if (_Idles == null) { return; } AnimancerUtilities.EditModePlay(_Animancer, _Idles.GetClip(_Facing), true); }
/// <summary> /// Returns a <see cref="GUIContent"/> with the specified parameters. The same instance is returned by /// every subsequent call. /// </summary> public static GUIContent TempContent(string text = null, string tooltip = null, bool narrowText = true) { AnimancerUtilities.NewIfNull(ref _TempContent); if (narrowText) { text = GetNarrowText(text); } _TempContent.text = text; _TempContent.tooltip = tooltip; return(_TempContent); }
/************************************************************************************************************************/ /// <inheritdoc/> public override void OnEnable(int index) { base.OnEnable(index); AnimancerUtilities.NewIfNull(ref _NewSprites); if (Animation == null) { _NewSprites.Clear(); } _OldSpriteDisplay = CreateReorderableObjectList(OldSprites, "Old Sprites"); _NewSpriteDisplay = CreateReorderableObjectList(_NewSprites, "New Sprites"); }
/// <summary>Returns a disposable <see cref="DrawerContext"/> representing the specified parameters.</summary> /// <remarks> /// Instances are stored in a <see cref="LazyStack{T}"/> and the current one can be accessed via /// <see cref="Context"/>. /// </remarks> public static IDisposable Get(SerializedProperty transitionProperty) { var context = Stack.Increment(); context.Property = transitionProperty; context.Transition = transitionProperty.GetValue <ITransitionDetailed>(); AnimancerUtilities.TryGetLength(context.Transition, out var length); context.MaximumDuration = length; EditorGUI.BeginChangeCheck(); return(context); }
/************************************************************************************************************************/ private void Awake() { AnimancerUtilities.Assert(Instance == null, $"The {nameof(GameManagerFSM)}.{nameof(Instance)} is already assigned."); Instance = this; StateMachine.ForceSetState(_Introduction); // Make sure both game managers aren't active at the same time. // This wouldn't normally be necessary because a real game won't have two managers for the same thing. if (FindObjectOfType <GameManagerEnum>() != null)// Won't find inactive objects. { Debug.LogError( $"Both the {nameof(GameManagerEnum)} and {nameof(GameManagerFSM)} are active. Exit Play Mode and disable one of them."); } }
/************************************************************************************************************************/ #region Initialisation /************************************************************************************************************************/ public SimpleLean(AnimancerPlayable animancer, Vector3 axis, NativeArray <TransformStreamHandle> leanBones) { var animator = animancer.Component.Animator; _Job = new Job { root = animator.BindStreamTransform(animator.transform), bones = leanBones, axis = axis, angle = AnimancerUtilities.CreateNativeReference <float>(), }; CreatePlayable(animancer); animancer.Disposables.Add(this); }
/// <summary> /// Returns a list of <see cref="Sprite"/>s which will be passed into /// <see cref="GenerateAnimationsBySpriteName(List{Sprite})"/> by <see cref="EditorApplication.delayCall"/>. /// </summary> private static List <Sprite> GetCachedSpritesToGenerateAnimations() { AnimancerUtilities.NewIfNull(ref _CachedSprites); // Delay the call in case multiple objects are selected. if (_CachedSprites.Count == 0) { EditorApplication.delayCall += () => { GenerateAnimationsBySpriteName(_CachedSprites); _CachedSprites.Clear(); }; } return(_CachedSprites); }
/************************************************************************************************************************/ /// <summary>Initializes the `transition` based on the `state`.</summary> private static void GetDetailsFromState(AnimatorState state, ITransitionDetailed transition) { if (state == null || transition == null) { return; } transition.Speed = state.speed; var isForwards = state.speed >= 0; var defaultEndTime = AnimancerEvent.Sequence.GetDefaultNormalizedEndTime(state.speed); var endTime = defaultEndTime; var exitTransitions = state.transitions; for (int i = 0; i < exitTransitions.Length; i++) { var exitTransition = exitTransitions[i]; if (exitTransition.hasExitTime) { if (isForwards) { if (endTime > exitTransition.exitTime) { endTime = exitTransition.exitTime; } } else { if (endTime < exitTransition.exitTime) { endTime = exitTransition.exitTime; } } } } if (endTime != defaultEndTime && AnimancerUtilities.TryGetWrappedObject(transition, out IHasEvents events)) { if (events.SerializedEvents == null) { events.SerializedEvents = new AnimancerEvent.Sequence.Serializable(); } events.SerializedEvents.SetNormalizedEndTime(endTime); } }
/// <inheritdoc/> protected override void PrepareToApply() { if (!AnimancerUtilities.NewIfNull(ref _SpriteToName)) { _SpriteToName.Clear(); } var sprites = Sprites; for (int i = 0; i < sprites.Count; i++) { _SpriteToName.Add(sprites[i], Names[i]); } // Renaming selected Sprites will lose the selection without triggering OnSelectionChanged. EditorApplication.delayCall += OnSelectionChanged; }
/************************************************************************************************************************/ public static List <ExampleGroup> Gather(DefaultAsset rootDirectoryAsset, out string examplesDirectory) { if (rootDirectoryAsset == null) { examplesDirectory = null; return(null); } examplesDirectory = AssetDatabase.GetAssetPath(rootDirectoryAsset); if (string.IsNullOrEmpty(examplesDirectory)) { return(null); } var directories = Directory.GetDirectories(examplesDirectory); var examples = new List <ExampleGroup>(); List <SceneAsset> scenes = null; for (int i = 0; i < directories.Length; i++) { var directory = directories[i]; var files = Directory.GetFiles(directory, "*.unity", SearchOption.AllDirectories); for (int j = 0; j < files.Length; j++) { var scene = AssetDatabase.LoadAssetAtPath <SceneAsset>(files[j]); if (scene != null) { AnimancerUtilities.NewIfNull(ref scenes); scenes.Add(scene); } } if (scenes != null) { examples.Add(new ExampleGroup(examplesDirectory, directory, scenes)); scenes = null; } } examplesDirectory = Path.GetDirectoryName(examplesDirectory); return(examples); }
/************************************************************************************************************************/ /// <summary>Creates an <see cref="AnimancerTransition"/> from the `blendTree`.</summary> private static Object GenerateTransition(AnimatorState state, BlendTree blendTree) { var asset = CreateTransition(blendTree); if (asset == null) { return(null); } if (state != null) { asset.name = state.name; } AnimancerUtilities.TryGetWrappedObject(asset, out ITransitionDetailed detailed); GetDetailsFromState(state, detailed); SaveTransition(blendTree, asset); return(asset); }
/************************************************************************************************************************/ private static void WrapEventTime(Context context, ref float normalizedTime) { var transition = context.TransitionContext?.Transition; if (transition != null && transition.IsLooping) { if (normalizedTime == 0) { return; } else if (normalizedTime % 1 == 0) { normalizedTime = AnimancerEvent.AlmostOne; } else { normalizedTime = AnimancerUtilities.Wrap01(normalizedTime); } } }
/// <summary> /// Fills the `clips` with any <see cref="AnimationClip"/>s referenced by components in the same hierarchy as /// the `gameObject`. See <see cref="ICharacterRoot"/> for details. /// </summary> public static void GatherFromGameObject(GameObject gameObject, ref AnimationClip[] clips, bool sort) { var gatherer = GatherFromGameObject(gameObject); if (gatherer == null) { return; } using (ObjectPool.Disposable.AcquireSet <AnimationClip>(out var clipSet)) { gatherer.GatherAnimationClips(clipSet); AnimancerUtilities.SetLength(ref clips, clipSet.Count); clipSet.CopyTo(clips); } if (sort) { Array.Sort(clips, (a, b) => a.name.CompareTo(b.name)); } }
/************************************************************************************************************************/ /// <summary>[Editor-Only] Draws the GUI for this attribute.</summary> public void OnGUI(Rect area, GUIContent label, ref float value) { var context = TransitionDrawer.Context; if (context == null) { value = DoSpecialFloatField(area, label, value, DisplayConverters[UnitIndex]); goto Return; } var length = context.MaximumDuration; if (length <= 0) { length = float.NaN; } AnimancerUtilities.TryGetFrameRate(context.Transition, out var frameRate); var multipliers = CalculateMultipliers(length, frameRate); if (multipliers == null) { EditorGUI.LabelField(area, label.text, $"Invalid {nameof(Validate)}.{nameof(Validate.Value)}"); goto Return; } DoPreviewTimeButton(ref area, ref value, context.Transition, multipliers); IsOptional = !float.IsNaN(nextDefaultValue); DefaultValue = nextDefaultValue; DoFieldGUI(area, label, ref value); Return: nextDefaultValue = float.NaN; }
/************************************************************************************************************************/ private static Delegate[] GetInvocationListIfMulticast(MulticastDelegate del) => AnimancerUtilities.TryGetInvocationListNonAlloc(del, out var delegates) ? delegates : del.GetInvocationList();
/************************************************************************************************************************/ /// <summary>[Editor-Only] Draws this attribute's fields.</summary> protected void DoFieldGUI(Rect area, GUIContent label, ref float value) { var isMultiLine = area.height > LineHeight; area.height = LineHeight; DoOptionalBeforeGUI(IsOptional, area, out var toggleArea, out var guiWasEnabled, out var previousLabelWidth); Rect allFieldArea; if (isMultiLine) { EditorGUI.LabelField(area, label); label = null; AnimancerGUI.NextVerticalArea(ref area); EditorGUI.indentLevel++; allFieldArea = EditorGUI.IndentedRect(area); EditorGUI.indentLevel--; } else { var labelXMax = area.x + EditorGUIUtility.labelWidth; allFieldArea = new Rect(labelXMax, area.y, area.xMax - labelXMax, area.height); } // Count the number of active fields. var count = 0; var last = 0; for (int i = 0; i < Multipliers.Length; i++) { if (!float.IsNaN(Multipliers[i])) { count++; last = i; } } var width = (allFieldArea.width - StandardSpacing * (count - 1)) / count; var fieldArea = new Rect(allFieldArea.x, allFieldArea.y, width, allFieldArea.height); var displayValue = GetDisplayValue(value, DefaultValue); // Draw the active fields. for (int i = 0; i < Multipliers.Length; i++) { var multiplier = Multipliers[i]; if (float.IsNaN(multiplier)) { continue; } if (label != null) { fieldArea.xMin = area.xMin; } else if (i < last) { fieldArea.width = width; fieldArea.xMax = AnimancerUtilities.Round(fieldArea.xMax); } else { fieldArea.xMax = area.xMax; } EditorGUI.BeginChangeCheck(); var fieldValue = displayValue * multiplier; fieldValue = DoSpecialFloatField(fieldArea, label, fieldValue, DisplayConverters[i]); label = null; if (EditorGUI.EndChangeCheck()) { value = fieldValue / multiplier; } fieldArea.x += fieldArea.width + StandardSpacing; } DoOptionalAfterGUI(IsOptional, toggleArea, ref value, DefaultValue, guiWasEnabled, previousLabelWidth); Validate.ValueRule(ref value, Rule); }
/************************************************************************************************************************/ /// <summary> /// 如果您将此类型派生的第二个脚本添加到相同的对象,它将更改现有组件的类型,从而允许您轻松地在 <see cref="IdleAndWalk"/> /// 和 <see cref="IdleAndWalkAndRun"/>之间进行交换. /// </summary> protected virtual void Reset() { AnimancerUtilities.IfMultiComponentThenChangeType(this); //如果有多个组件,则改变类型. }
/// <summary>[Internal] Sets the prefix for <see cref="GetSerializedProperty"/>.</summary> internal static void SetBasePropertyPath <T>(ref T group, string propertyPath) where T : Group, new() { AnimancerUtilities.NewIfNull(ref group); group._BasePropertyPath = propertyPath + "."; }
/************************************************************************************************************************/ /// <inheritdoc/> public override void OnEnable(int index) { base.OnEnable(index); AnimancerUtilities.NewIfNull(ref _Textures); _TexturesDisplay = CreateReorderableObjectList(_Textures, "Textures", true); }
/************************************************************************************************************************/ /// <summary>Draws the ruler GUI and handles input events for the specified `context`.</summary> private void DoGUI(SerializableEventSequenceDrawer.Context context, out float addEventNormalizedTime) { if (context.Property.hasMultipleDifferentValues) { GUI.Label(_Area, "Multi-editing is not supported"); addEventNormalizedTime = float.NaN; return; } var transition = context.TransitionContext.Transition; _Speed = transition.Speed; if (float.IsNaN(_Speed)) { _Speed = 1; } _Duration = context.TransitionContext.MaximumDuration; if (_Duration <= 0) { _Duration = 1; } GatherEventTimes(context); _StartTime = GetStartTime(transition.NormalizedStartTime, _Speed, _Duration); _FadeInEnd = _StartTime + transition.FadeDuration * _Speed; _FadeOutEnd = GetFadeOutEnd(_Speed, _EndTime, _Duration); _MinTime = Mathf.Min(0, _StartTime); _MinTime = Mathf.Min(_MinTime, _FadeOutEnd); _MinTime = Mathf.Min(_MinTime, EventTimes[0]); _MaxTime = Mathf.Max(_StartTime, _FadeOutEnd); if (EventTimes.Count >= 2) { _MaxTime = Mathf.Max(_MaxTime, EventTimes[EventTimes.Count - 2]); } if (_MaxTime < _Duration) { _MaxTime = _Duration; } _SecondsToPixels = _Area.width / (_MaxTime - _MinTime); DoFadeHighlightGUI(); if (AnimancerUtilities.TryGetWrappedObject(transition, out ITransitionGUI gui)) { gui.OnTimelineBackgroundGUI(); } DoEventsGUI(context, out addEventNormalizedTime); DoRulerGUI(); if (_Speed > 0) { if (_StartTime >= _EndTime) { GUI.Label(_Area, "Start Time is not before End Time"); } } else if (_Speed < 0) { if (_StartTime <= _EndTime) { GUI.Label(_Area, "Start Time is not after End Time"); } } if (gui != null) { gui.OnTimelineForegroundGUI(); } }
/************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Conversions /************************************************************************************************************************/ /// <summary>Converts a number of seconds to a horizontal pixel position along the ruler.</summary> /// <remarks>The value is rounded to the nearest integer.</remarks> public float SecondsToPixels(float seconds) => AnimancerUtilities.Round((seconds - _MinTime) * _SecondsToPixels);
/************************************************************************************************************************/ /// <summary> /// If you add a second script derived from this type to the same object, it will instead change the type of /// the existing component, allowing you to easily swap between any components that inherit from /// <see cref="GolfHitController"/> without losing the values of their serialized fields. /// </summary> protected void Reset() { AnimancerUtilities.IfMultiComponentThenChangeType(this); }
/// <summary>Draws the time field for the event at the specified `index`.</summary> public static void DoTimeGUI(ref Rect area, Context context, int index, bool autoSort, string timeLabel, float defaultTime, bool isEndEvent) { EditorGUI.BeginChangeCheck(); area.height = EventTimeAttribute.GetPropertyHeight(null, null); var timeArea = area; AnimancerGUI.NextVerticalArea(ref area); float normalizedTime; using (ObjectPool.Disposable.AcquireContent(out var label, timeLabel, isEndEvent ? Strings.Tooltips.EndTime : Strings.Tooltips.CallbackTime)) { var length = context.TransitionContext?.MaximumDuration ?? float.NaN; if (index < context.Times.Count) { var timeProperty = context.Times.GetElement(index); if (timeProperty == null)// Multi-selection screwed up the property retrieval. { EditorGUI.BeginChangeCheck(); label = EditorGUI.BeginProperty(timeArea, label, context.Times.Property); if (isEndEvent) { AnimationTimeAttribute.nextDefaultValue = defaultTime; } normalizedTime = float.NaN; EventTimeAttribute.OnGUI(timeArea, label, ref normalizedTime); EditorGUI.EndProperty(); if (EditorGUI.EndChangeCheck()) { context.Times.Count = context.Times.Count; timeProperty = context.Times.GetElement(index); timeProperty.floatValue = normalizedTime; SyncEventTimeChange(context, index, normalizedTime); } } else// Event time property was correctly retrieved. { var wasEditingTextField = EditorGUIUtility.editingTextField; if (!wasEditingTextField) { _PreviousTime = float.NaN; } EditorGUI.BeginChangeCheck(); label = EditorGUI.BeginProperty(timeArea, label, timeProperty); if (isEndEvent) { AnimationTimeAttribute.nextDefaultValue = defaultTime; } normalizedTime = timeProperty.floatValue; EventTimeAttribute.OnGUI(timeArea, label, ref normalizedTime); EditorGUI.EndProperty(); if (AnimancerGUI.TryUseClickEvent(timeArea, 2)) { normalizedTime = float.NaN; } var isEditingTextField = EditorGUIUtility.editingTextField; if (EditorGUI.EndChangeCheck() || (wasEditingTextField && !isEditingTextField)) { if (float.IsNaN(normalizedTime)) { RemoveEvent(context, index); AnimancerGUI.Deselect(); } else if (isEndEvent) { timeProperty.floatValue = normalizedTime; SyncEventTimeChange(context, index, normalizedTime); } else if (!autoSort && isEditingTextField) { _PreviousTime = normalizedTime; } else { if (!float.IsNaN(_PreviousTime)) { if (Event.current.keyCode != KeyCode.Escape) { normalizedTime = _PreviousTime; AnimancerGUI.Deselect(); } _PreviousTime = float.NaN; } WrapEventTime(context, ref normalizedTime); timeProperty.floatValue = normalizedTime; SyncEventTimeChange(context, index, normalizedTime); if (autoSort) { SortEvents(context); } } GUI.changed = true; } } } else// Dummy End Event (when there are no event times). { AnimancerUtilities.Assert(index == 0, "Dummy end event index != 0"); EditorGUI.BeginChangeCheck(); EditorGUI.BeginProperty(timeArea, GUIContent.none, context.Times.Property); AnimationTimeAttribute.nextDefaultValue = defaultTime; normalizedTime = float.NaN; EventTimeAttribute.OnGUI(timeArea, label, ref normalizedTime); EditorGUI.EndProperty(); if (EditorGUI.EndChangeCheck() && !float.IsNaN(normalizedTime)) { context.Times.Count = 1; var timeProperty = context.Times.GetElement(0); timeProperty.floatValue = normalizedTime; SyncEventTimeChange(context, 0, normalizedTime); } } } if (EditorGUI.EndChangeCheck()) { var eventType = Event.current.type; if (eventType == EventType.Layout) { return; } if (eventType == EventType.Used) { normalizedTime = UnitsAttribute.GetDisplayValue(normalizedTime, defaultTime); TransitionPreviewWindow.PreviewNormalizedTime = normalizedTime; } GUIUtility.ExitGUI(); } }