Before 优化 机场

This commit is contained in:
CortexCore
2025-03-10 18:06:44 +08:00
parent 350e6d67b2
commit 1f4e20f512
178 changed files with 17534 additions and 821 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f6d9117845df4a049f092765c3987cfd
timeCreated: 1705478497

View File

@@ -0,0 +1,343 @@
// Designed by KINEMATION, 2024.
using KINEMATION.MotionWarping.Editor.Widgets;
using Kinemation.MotionWarping.Runtime.Core;
using Kinemation.MotionWarping.Runtime.Utility;
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Vector3 = UnityEngine.Vector3;
namespace KINEMATION.MotionWarping.Editor.Core
{
[CustomEditor(typeof(MotionWarpingAsset))]
public class MotionWarpingAssetEditor : UnityEditor.Editor
{
private const BindingFlags PrivateFieldBindingFlags =
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField;
private const BindingFlags PublicFieldBindingFlags =
BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField;
private const BindingFlags PublicPropertyBindingFlags =
BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty;
private const BindingFlags PublicMethodBindingFlags =
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod;
private static Type _animationClipEditorType;
private static Type _avatarPreviewType;
private static Type _timeControlType;
private MotionWarpingAsset _asset;
private UnityEditor.Editor _meshEditor;
private float _frameSlider = 0f;
private float _length = 0f;
private bool _manualPlaybackOverride;
private WarpWindowWidget _warpWindowWidget;
private WarpPosePreviewWidget _posePreviewer;
private string[] _toolbarOptions = new string[] { "Motion Warping", "Pose Preview", "Animation Settings"};
private int _selectedTab = 0;
public override bool HasPreviewGUI() => true;
private void OnAreaModified(int areaIndex)
{
float warpLength = _asset.GetLength();
var phase = _asset.warpPhases[areaIndex];
var size = _warpWindowWidget.GetAreaSize(areaIndex);
phase.startTime = (float) Math.Round(size.Item1 * warpLength, 4);
phase.endTime = (float) Math.Round(size.Item2 * warpLength, 4);
_asset.warpPhases[areaIndex] = phase;
}
private void StopLoop()
{
var avatarPreview = _animationClipEditorType.GetField("m_AvatarPreview", PrivateFieldBindingFlags)?.GetValue(_meshEditor);
if (avatarPreview == null) return;
var timeControl = _avatarPreviewType.GetField("timeControl", PublicFieldBindingFlags)?.GetValue(avatarPreview);
if (timeControl == null) return;
var stopTime = _timeControlType.GetProperty("playing", PublicPropertyBindingFlags);
if (stopTime == null) return;
stopTime.SetValue(timeControl, false);
}
private void UpdatePlayback()
{
if (!_manualPlaybackOverride) return;
var avatarPreview = _animationClipEditorType.GetField("m_AvatarPreview", PrivateFieldBindingFlags)
?.GetValue(_meshEditor);
if (avatarPreview == null) return;
var timeControl = _avatarPreviewType.GetField("timeControl", PublicFieldBindingFlags)
?.GetValue(avatarPreview);
if (timeControl == null) return;
var stopTime = _timeControlType.GetField("stopTime", PublicFieldBindingFlags);
if (stopTime == null) return;
stopTime.SetValue(timeControl, _length);
var timeProperty = _timeControlType.GetField("currentTime", PublicFieldBindingFlags);
if (timeProperty == null) return;
timeProperty.SetValue(timeControl, _frameSlider);
}
private void GenerateAreas()
{
float totalTime = _asset.GetLength();
if (Mathf.Approximately(totalTime, 0f)) return;
_warpWindowWidget.ClearPhases();
foreach (var phase in _asset.warpPhases)
{
_warpWindowWidget.AddWarpPhase(phase.startTime / totalTime, phase.endTime / totalTime);
}
}
private void GeneratePhases()
{
float totalTime = _asset.GetLength();
if (Mathf.Approximately(totalTime, 0f)) return;
_asset.warpPhases.Clear();
float timeStep = totalTime / _asset.phasesAmount;
for (int i = 0; i < _asset.phasesAmount; i++)
{
WarpPhase phase = new WarpPhase()
{
minRate = 0f,
maxRate = 1f,
startTime = timeStep * i,
endTime = timeStep * i + timeStep,
};
_asset.warpPhases.Add(phase);
}
}
private void ExtractCurves()
{
if (_asset == null || _asset.animation == null)
{
Debug.LogError("WarpingAsset or AnimationClip is null!");
return;
}
EditorCurveBinding[] tBindings = new EditorCurveBinding[3];
var curveBindings = AnimationUtility.GetCurveBindings(_asset.animation);
foreach (var binding in curveBindings)
{
if (_asset.animation.isHumanMotion)
{
if (binding.propertyName.ToLower().Contains("roott.x"))
{
tBindings[0] = binding;
}
else if (binding.propertyName.ToLower().Contains("roott.y"))
{
tBindings[1] = binding;
}
else if (binding.propertyName.ToLower().Contains("roott.z"))
{
tBindings[2] = binding;
}
}
else
{
if(!binding.path.ToLower().EndsWith("root")) continue;
if (binding.propertyName.ToLower().Contains("localposition.x"))
{
tBindings[0] = binding;
}
else if (binding.propertyName.ToLower().Contains("localposition.y"))
{
tBindings[1] = binding;
}
else if (binding.propertyName.ToLower().Contains("localposition.z"))
{
tBindings[2] = binding;
}
}
}
var curves = WarpingEditorUtility.ValidateCurves(_asset, 60f, tBindings);
_asset.rootX = curves.X;
_asset.rootY = curves.Y;
_asset.rootZ = curves.Z;
ComputeTotalRootMotion();
}
private void RenderMotionWarpingTab()
{
base.OnInspectorGUI();
if (_asset.animation == null)
{
EditorGUILayout.HelpBox("Specify the animation", MessageType.Warning);
return;
}
_length = _asset.animation.length;
_warpWindowWidget.Render();
var prevSlider = _frameSlider;
_frameSlider = _length * _warpWindowWidget.GetPlayback();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Generate Phases"))
{
GeneratePhases();
GenerateAreas();
}
if (GUILayout.Button("Extract Curves"))
{
ExtractCurves();
}
EditorGUILayout.EndHorizontal();
if (!Mathf.Approximately(prevSlider, _frameSlider) && !_manualPlaybackOverride)
{
_manualPlaybackOverride = true;
StopLoop();
}
else
{
_manualPlaybackOverride = false;
}
UpdatePlayback();
}
private void RenderAnimationSettingsTab()
{
if (_meshEditor == null) return;
_meshEditor.OnInspectorGUI();
}
private void ComputeTotalRootMotion()
{
if (_asset.animation == null) return;
float sampleRate = _asset.animation.frameRate;
if (Mathf.Approximately(sampleRate, 0f)) return;
for (int i = 0; i < _asset.warpPhases.Count; i++)
{
var phase = _asset.warpPhases[i];
float playback = phase.startTime;
Vector3 lastValue = _asset.GetVectorValue(playback);
phase.totalRootMotion = Vector3.zero;
while (playback <= phase.endTime)
{
// Accumulate the delta.
Vector3 value = _asset.GetVectorValue(playback);
Vector3 delta = value - lastValue;
phase.totalRootMotion.x += Mathf.Abs(delta.x);
phase.totalRootMotion.y += Mathf.Abs(delta.y);
phase.totalRootMotion.z += Mathf.Abs(delta.z);
lastValue = value;
playback += 1f / sampleRate;
}
_asset.warpPhases[i] = phase;
}
EditorUtility.SetDirty(_asset);
AssetDatabase.SaveAssets();
}
private void OnEnable()
{
_asset = (target) as MotionWarpingAsset;
if (_asset == null)
{
Debug.LogError("Target MotionWarpingAsset is null!");
return;
}
_meshEditor = CreateEditor(_asset.animation);
_animationClipEditorType = Type.GetType("UnityEditor.AnimationClipEditor,UnityEditor");
_avatarPreviewType = Type.GetType("UnityEditor.AvatarPreview,UnityEditor");
_timeControlType = Type.GetType("UnityEditor.TimeControl,UnityEditor");
_warpWindowWidget = new WarpWindowWidget();
_warpWindowWidget.OnAreaModified += OnAreaModified;
_posePreviewer = new WarpPosePreviewWidget(_asset);
GenerateAreas();
}
private void OnDisable()
{
ComputeTotalRootMotion();
_posePreviewer.RestorePose();
_meshEditor = null;
}
public override void OnInspectorGUI()
{
_selectedTab = GUILayout.Toolbar(_selectedTab, _toolbarOptions);
if (_selectedTab == 0)
{
RenderMotionWarpingTab();
return;
}
if (_selectedTab == 1)
{
_posePreviewer?.Render();
return;
}
RenderAnimationSettingsTab();
}
public override void OnPreviewGUI(Rect r, GUIStyle background)
{
if (_meshEditor == null || !_meshEditor.HasPreviewGUI()) return;
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
_meshEditor.OnPreviewSettings();
EditorGUILayout.EndHorizontal();
_meshEditor.OnInteractivePreviewGUI(r, GUIStyle.none);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d0d2c068e5aa4321893ea76aa6528f17
timeCreated: 1698238712

View File

@@ -0,0 +1,20 @@
// Designed by KINEMATION, 2024.
using Kinemation.MotionWarping.Runtime.Utility;
using UnityEditor;
using UnityEngine;
namespace KINEMATION.MotionWarping.Editor.Core
{
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
public class ReadOnlyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: db7fde80939c4b2e8ae89d39d25ecb82
timeCreated: 1696662790

View File

@@ -0,0 +1,58 @@
// Designed by KINEMATION, 2024.
using Kinemation.MotionWarping.Runtime.Core;
using Kinemation.MotionWarping.Runtime.Utility;
using UnityEditor;
using UnityEngine;
namespace KINEMATION.MotionWarping.Editor.Core
{
public class WarpingEditorUtility
{
public static Vector3 GetVectorValue(AnimationClip clip, EditorCurveBinding[] bindings, float time)
{
float tX = AnimationUtility.GetEditorCurve(clip, bindings[0]).Evaluate(time);
float tY = AnimationUtility.GetEditorCurve(clip, bindings[1]).Evaluate(time);
float tZ = AnimationUtility.GetEditorCurve(clip, bindings[2]).Evaluate(time);
return new Vector3(tX, tY, tZ);
}
public static WarpingCurve ValidateCurves(MotionWarpingAsset motionWarpingAsset, float sampleRate = 30f,
EditorCurveBinding[] tBindings = null)
{
WarpingCurve warpingCurve = new WarpingCurve();
if (tBindings == null) return warpingCurve;
warpingCurve.X = new AnimationCurve();
warpingCurve.Y = new AnimationCurve();
warpingCurve.Z = new AnimationCurve();
float playback = 0f, length = motionWarpingAsset.GetLength();
Vector3 refVector3 = GetVectorValue(motionWarpingAsset.animation, tBindings, playback);
Vector3 refT = new Vector3()
{
x = refVector3.x,
y = refVector3.y,
z = refVector3.z
};
while (playback <= length)
{
Vector3 root = GetVectorValue(motionWarpingAsset.animation, tBindings, playback);
Vector3 delta = root - refT;
warpingCurve.X.AddKey(playback, delta.x);
warpingCurve.Y.AddKey(playback, delta.y);
warpingCurve.Z.AddKey(playback, delta.z);
playback += 1f / sampleRate;
}
return warpingCurve;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 85fcb84c5a6c45f98c4c9202abcd7547
timeCreated: 1698770974

View File

@@ -0,0 +1,36 @@
{
"name": "MotionWarping.Editor",
"rootNamespace": "",
"references": [
"GUID:4c36dcf6d8eeb6d44ab73ef966534b46"
],
"includePlatforms": [],
"excludePlatforms": [
"Android",
"EmbeddedLinux",
"GameCoreScarlett",
"GameCoreXboxOne",
"iOS",
"LinuxStandalone64",
"CloudRendering",
"Lumin",
"macOSStandalone",
"PS4",
"PS5",
"Stadia",
"Switch",
"tvOS",
"WSA",
"WebGL",
"WindowsStandalone32",
"WindowsStandalone64",
"XboxOne"
],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6ac99bb1de1ddac4fbefd00ae5306a38
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6b6288be5f59459d8b3e249b1be7ee92
timeCreated: 1705417126

View File

@@ -0,0 +1,221 @@
// Designed by KINEMATION, 2024.
using Kinemation.MotionWarping.Runtime.Core;
using Kinemation.MotionWarping.Runtime.Utility;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
namespace KINEMATION.MotionWarping.Editor.Widgets
{
public class WarpPosePreviewWidget : IWarpWidgetInterface
{
private GameObject _warpTracerObject;
private Vector3 _basePos;
private Quaternion _baseRot;
private GameObject _sceneCharacter;
private AnimationClipPlayable _previewMotion;
private int _phaseSlider = 0;
private bool _preview = false;
private WarpPoint[] _warpPoints = null;
private Dictionary<string, (Vector3, Quaternion)> _cachedTransforms
= new Dictionary<string, (Vector3, Quaternion)>();
private PlayableGraph _playableGraph;
private MotionWarpingAsset _motionWarpingAsset;
public WarpPosePreviewWidget(MotionWarpingAsset asset)
{
_motionWarpingAsset = asset;
}
private void CacheTransforms()
{
_cachedTransforms.Clear();
Transform[] allChildren = _sceneCharacter.GetComponentsInChildren<Transform>();
foreach (Transform child in allChildren)
{
(Vector3, Quaternion) data;
data.Item1 = child.localPosition;
data.Item2 = child.localRotation;
_cachedTransforms[child.name] = data;
}
}
private void ApplyCachedTransforms()
{
if (_sceneCharacter == null) return;
Transform[] allChildren = _sceneCharacter.GetComponentsInChildren<Transform>();
foreach (Transform child in allChildren)
{
if (_cachedTransforms.ContainsKey(child.name))
{
child.localPosition = _cachedTransforms[child.name].Item1;
child.localRotation = _cachedTransforms[child.name].Item2;
}
}
}
public void RestorePose()
{
if (_playableGraph.IsValid())
{
_playableGraph.Destroy();
}
if (_previewMotion.IsValid())
{
_previewMotion.Destroy();
}
if (_preview)
{
ApplyCachedTransforms();
}
_cachedTransforms.Clear();
}
public void Render()
{
_sceneCharacter = (GameObject) EditorGUILayout.ObjectField("Character", _sceneCharacter,
typeof(GameObject), true);
if (_sceneCharacter == null)
{
EditorGUILayout.HelpBox("Missing Character Reference!", MessageType.Error);
return;
}
Animator animator = _sceneCharacter.GetComponentInChildren<Animator>();
if (animator == null)
{
EditorGUILayout.HelpBox("No Animator Found!", MessageType.Error);
return;
}
_warpTracerObject = (GameObject) EditorGUILayout.ObjectField("Source", _warpTracerObject,
typeof(GameObject), true);
if (_warpTracerObject == null)
{
EditorGUILayout.HelpBox("Select Warp Tracer game object.", MessageType.Warning);
return;
}
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
AnimationPlayableOutput playableOutput;
if (GUILayout.Button("Play"))
{
CacheTransforms();
_playableGraph = PlayableGraph.Create("WarpPosePreviewGraph");
playableOutput = AnimationPlayableOutput.Create(_playableGraph, "Animation",
animator);
_previewMotion = AnimationClipPlayable.Create(_playableGraph, _motionWarpingAsset.animation);
playableOutput.SetSourcePlayable(_previewMotion);
_preview = true;
animator.fireEvents = false;
animator.applyRootMotion = false;
if (_warpTracerObject.GetComponent<IWarpPointProvider>() is var tracer)
{
var result = tracer.Interact(_sceneCharacter);
_warpPoints = result.points;
}
_phaseSlider = -1;
}
if (GUILayout.Button("Stop"))
{
_preview = false;
animator.Rebind();
animator.fireEvents = true;
animator.applyRootMotion = true;
_phaseSlider = 0;
if (_playableGraph.IsValid())
{
_playableGraph.Stop();
_playableGraph.Destroy();
}
if (_previewMotion.IsValid())
{
_previewMotion.Destroy();
}
ApplyCachedTransforms();
}
EditorGUILayout.EndHorizontal();
if (!_preview) return;
if (_warpPoints == null || _warpPoints.Length != _motionWarpingAsset.warpPhases.Count)
{
string msg = "MotionWarping: WarpObject points are null or size does not match!";
EditorGUILayout.HelpBox(msg, MessageType.Error);
return;
}
int max = _motionWarpingAsset.warpPhases.Count - 1;
int prevSlider = _phaseSlider;
_phaseSlider = EditorGUILayout.IntSlider("Phase", _phaseSlider, 0, max);
if (_previewMotion.IsValid())
{
float time = _motionWarpingAsset.warpPhases[_phaseSlider].endTime;
_previewMotion.SetTime(time);
}
var phase = _motionWarpingAsset.warpPhases[_phaseSlider];
if (prevSlider != _phaseSlider)
{
_playableGraph.Evaluate();
_sceneCharacter.transform.position = _warpPoints[_phaseSlider].GetPosition();
_sceneCharacter.transform.rotation = _warpPoints[_phaseSlider].GetRotation();
_basePos = _sceneCharacter.transform.position;
_baseRot = _sceneCharacter.transform.rotation;
var pos = _sceneCharacter.transform.TransformPoint(phase.tOffset);
var rot = _sceneCharacter.transform.rotation * Quaternion.Euler(phase.rOffset);
_sceneCharacter.transform.position = pos;
_sceneCharacter.transform.rotation = rot;
}
var tDelta = _sceneCharacter.transform.position - _basePos;
var tLocalDelta = Quaternion.Inverse(_baseRot) * tDelta;
var rLocalDelta = Quaternion.Inverse(_baseRot) * _sceneCharacter.transform.rotation;
phase.tOffset = tLocalDelta;
phase.rOffset = rLocalDelta.eulerAngles;
_motionWarpingAsset.warpPhases[_phaseSlider] = phase;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a55d3fce016f48c4bb363262cf69bd3b
timeCreated: 1699005765

View File

@@ -0,0 +1,9 @@
// Designed by KINEMATION, 2024.
namespace KINEMATION.MotionWarping.Editor.Widgets
{
public interface IWarpWidgetInterface
{
public void Render();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 46f96b7c548b4e4199d76c2ee0f20d80
timeCreated: 1699005897

View File

@@ -0,0 +1,388 @@
// Designed by KINEMATION, 2024.
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace KINEMATION.MotionWarping.Editor.Widgets
{
public class WarpWindowWidget : IWarpWidgetInterface
{
class DraggableArea
{
private const float MinAreaWidth = 10f;
private const float BorderTolerance = 5f;
private const float BorderWidth = 2f;
public Rect Parent;
public float LocalStart;
public float LocalEnd;
private Color _color;
// -2: not hovered, -1: left border, 0: body, 1: right border.
public int GetHoveredPart(Vector2 mousePosition)
{
if (mousePosition.x >= GetRange().Item1 - BorderTolerance
&& mousePosition.x <= GetRange().Item1 + BorderTolerance)
{
return -1;
}
if (mousePosition.x >= GetRange().Item2 - BorderTolerance
&& mousePosition.x <= GetRange().Item2 + BorderTolerance)
{
return 1;
}
return 0;
}
public DraggableArea(float start, float end, Rect parent)
{
LocalStart = start;
LocalEnd = end;
_color = new Color(0f,0.5f,0.5f);
this.Parent = parent;
}
public float GetThickness()
{
float worldStart = Mathf.Lerp(Parent.xMin, Parent.xMax, LocalStart);
float worldEnd = Mathf.Lerp(Parent.xMin, Parent.xMax, LocalEnd);
return worldEnd - worldStart;
}
public Rect GetRect()
{
Rect rect = Parent;
rect.x = GetRange().Item1;
rect.width = GetThickness();
return rect;
}
public (float, float) GetRange()
{
float worldStart = Mathf.LerpUnclamped(Parent.xMin, Parent.xMax, LocalStart);
float worldEnd = Mathf.LerpUnclamped(Parent.xMin, Parent.xMax, LocalEnd);
return (worldStart, worldEnd);
}
public float GetLocal(float value)
{
if (Mathf.Approximately(Parent.xMin, Parent.xMax)) return 0f;
return (value - Parent.xMin) / (Parent.xMax - Parent.xMin);
}
public void RenderArea(float opacity)
{
// Draw body.
Rect areaRect = Parent;
areaRect.x = GetRange().Item1;
areaRect.width = GetThickness();
_color.a = opacity;
EditorGUI.DrawRect(areaRect, _color);
// Draw borders.
areaRect.x -= BorderWidth / 2f;
areaRect.width = BorderWidth;
EditorGUI.DrawRect(areaRect, new Color(1f, 1f, 1f, opacity));
areaRect.x = GetRange().Item2 - BorderWidth / 2f;
EditorGUI.DrawRect(areaRect, new Color(1f, 1f, 1f, opacity));
}
public bool Contains(Vector2 checkPosition)
{
bool x = checkPosition.x >= GetRange().Item1 && checkPosition.x <= GetRange().Item2;
bool y = checkPosition.y >= Parent.yMin && checkPosition.y <= Parent.yMax;
return x && y;
}
public void Resize(float mouseDelta, int part)
{
// Cache the values
float start = LocalStart;
float end = LocalEnd;
float left = GetRange().Item1;
float right = GetRange().Item2;
if (part == -1)
{
left -= part * mouseDelta;
}
if (part == 0)
{
left += mouseDelta;
right += mouseDelta;
}
if (part == 1)
{
right += mouseDelta;
}
LocalStart = GetLocal(left);
LocalEnd = GetLocal(right);
if (GetThickness() - BorderTolerance * 2f < MinAreaWidth)
{
LocalStart = start;
LocalEnd = end;
}
}
}
public delegate void WarpWindowCallback(int modifiedArea);
public WarpWindowCallback OnAreaModified;
private const float TimelineHeight = 20f;
private const float PlaybackTolerance = 8f;
private const float PlaybackWidth = 2f;
private Rect _timelineRect;
private List<DraggableArea> _draggableAreas = new List<DraggableArea>();
private DraggableArea _activeArea;
private int _resizeAction;
private bool _mousePressed;
private float _playbackPosition;
private bool _movingPlayback;
private bool IsAreaColliding(float proposedPosition, int areaIndex)
{
int areasCount = _draggableAreas.Count;
if (areasCount == 0)
{
return false;
}
int leftIndex = areaIndex - 1;
int rightIndex = areaIndex + 1;
// Check left border
var leftBorderCollision = proposedPosition <
(leftIndex < 0 ? _timelineRect.xMin : _draggableAreas[leftIndex].GetRange().Item2);
// Check right border
var rightBorderCollision = proposedPosition >
(rightIndex > areasCount - 1
? _timelineRect.xMax
: _draggableAreas[rightIndex].GetRange().Item1);
return leftBorderCollision || rightBorderCollision;
}
private void UpdateArea(int areaIndex, bool mouseAction)
{
Vector2 mousePosition = Event.current.mousePosition;
Vector2 mouseDelta = Event.current.delta;
DraggableArea area = _draggableAreas[areaIndex];
area.Parent = _timelineRect;
// Enable editing if the cursor is within the bounds.
if (mouseAction && _mousePressed && area.Contains(mousePosition))
{
_activeArea = area;
_resizeAction = area.GetHoveredPart(mousePosition);
Event.current.Use();
}
if (_activeArea != null && mouseAction && !_mousePressed)
{
_activeArea = null;
_resizeAction = -2;
Event.current.Use();
}
if (_activeArea == null || area != _activeArea)
{
area.RenderArea(1f);
return;
}
RenderCursorIcon(area, _resizeAction);
if (Event.current.type == EventType.MouseDrag)
{
// Cache the area current size
float areaStart = area.LocalStart;
float areaEnd = area.LocalEnd;
// Resize the area
area.Resize(mouseDelta.x, _resizeAction);
float start = area.GetRange().Item1;
float end = area.GetRange().Item2;
bool collideLeft = IsAreaColliding(start, areaIndex);
bool collideRight = IsAreaColliding(end, areaIndex);
// Check for any collisions
if (collideLeft || collideRight)
{
area.LocalStart = areaStart;
area.LocalEnd = areaEnd;
}
else
{
OnAreaModified?.Invoke(areaIndex);
}
Event.current.Use();
}
area.RenderArea(0.7f);
}
private void RenderCursorIcon(DraggableArea area, int hoveredPart)
{
if (hoveredPart == 0)
{
EditorGUIUtility.AddCursorRect(area.GetRect(), MouseCursor.Pan);
return;
}
EditorGUIUtility.AddCursorRect(area.GetRect(), MouseCursor.SlideArrow);
}
private void UpdateDraggableAreas()
{
bool prevMousePressed = _mousePressed;
if (Event.current.type == EventType.MouseDown)
{
_mousePressed = true;
}
if (Event.current.type == EventType.MouseUp)
{
_mousePressed = false;
}
for (int i = 0; i < _draggableAreas.Count; i++)
{
UpdateArea(i, prevMousePressed != _mousePressed);
}
}
public void ClearPhases()
{
_draggableAreas.Clear();
}
public void AddWarpPhase(float start, float end)
{
DraggableArea newArea = new DraggableArea(start, end, _timelineRect);
_draggableAreas.Add(newArea);
}
public (float, float) GetAreaSize(int areaIndex)
{
(float, float) size = (0f, 0f);
if (_draggableAreas.Count == 0 || areaIndex < 0 || areaIndex > _draggableAreas.Count - 1) return size;
var area = _draggableAreas[areaIndex];
size.Item1 = area.LocalStart;
size.Item2 = area.LocalEnd;
return size;
}
private void RenderPlayback(bool bAction = false)
{
float worldPlayback = Mathf.Lerp(_timelineRect.xMin, _timelineRect.xMax, _playbackPosition);
var playbackRect = _timelineRect;
playbackRect.y -= TimelineHeight;
Vector2 mousePosition = Event.current.mousePosition;
if (bAction && _mousePressed && playbackRect.Contains(mousePosition))
{
if (mousePosition.x >= worldPlayback - PlaybackTolerance
&& mousePosition.x <= worldPlayback + PlaybackTolerance)
{
_movingPlayback = true;
}
}
if (bAction && !_mousePressed)
{
_movingPlayback = false;
}
if (_movingPlayback)
{
playbackRect.height *= 2f;
EditorGUIUtility.AddCursorRect(playbackRect, MouseCursor.SlideArrow);
playbackRect.height /= 2f;
if (Event.current.type == EventType.MouseDrag)
{
worldPlayback += Event.current.delta.x;
_playbackPosition = Mathf.InverseLerp(_timelineRect.xMin, _timelineRect.xMax, worldPlayback);
Event.current.Use();
}
}
if(_activeArea != null && Event.current.shift)
{
if (_resizeAction is 0 or -1)
{
_playbackPosition = _activeArea.LocalStart;
}
if (_resizeAction == 1)
{
_playbackPosition = _activeArea.LocalEnd;
}
}
playbackRect.x = Mathf.Lerp(_timelineRect.xMin, _timelineRect.xMax, _playbackPosition);
playbackRect.x -= PlaybackWidth / 2f;
playbackRect.width = PlaybackWidth;
playbackRect.height = _timelineRect.yMax - playbackRect.y;
EditorGUI.DrawRect(playbackRect, Color.red);
}
public float GetPlayback()
{
return _playbackPosition;
}
public void Render()
{
float width = EditorGUIUtility.currentViewWidth;
EditorGUI.DrawRect(GUILayoutUtility.GetRect(width, TimelineHeight), new Color(0.15f, 0.15f, 0.15f));
var cacheRect = _timelineRect;
_timelineRect = GUILayoutUtility.GetRect(width, TimelineHeight);
if (Mathf.Approximately(_timelineRect.width, 1f))
{
_timelineRect = cacheRect;
}
EditorGUI.DrawRect(_timelineRect, new Color(0.15f, 0.15f, 0.15f));
bool action = _mousePressed;
UpdateDraggableAreas();
RenderPlayback(action != _mousePressed);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fc18a424cded448f88f5c3f619a1ffe6
timeCreated: 1698053326