diff --git a/.vs/RimWorld-Animation-Studio/v16/.suo b/.vs/RimWorld-Animation-Studio/v16/.suo index c33e2b74..d438df66 100644 Binary files a/.vs/RimWorld-Animation-Studio/v16/.suo and b/.vs/RimWorld-Animation-Studio/v16/.suo differ diff --git a/Assets/Resources/Prefabs/AnimationTimeline.prefab b/Assets/Resources/Prefabs/AnimationTimeline.prefab index b826a57c..e3d16f72 100644 --- a/Assets/Resources/Prefabs/AnimationTimeline.prefab +++ b/Assets/Resources/Prefabs/AnimationTimeline.prefab @@ -244,7 +244,7 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: - - m_Target: {fileID: 0} + - m_Target: {fileID: 7551137198864639414} m_MethodName: OnMoveTimeline m_Mode: 3 m_Arguments: @@ -452,7 +452,7 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: - - m_Target: {fileID: 0} + - m_Target: {fileID: 7551137198864639414} m_MethodName: OnMoveTimeline m_Mode: 3 m_Arguments: diff --git a/Assets/Scenes/MainScene.unity b/Assets/Scenes/MainScene.unity index a26dbc4f..327dbd5f 100644 --- a/Assets/Scenes/MainScene.unity +++ b/Assets/Scenes/MainScene.unity @@ -8284,6 +8284,7 @@ RectTransform: - {fileID: 935483303140078260} - {fileID: 2085905668} - {fileID: 1100016168} + - {fileID: 866545185} m_Father: {fileID: 1335076903} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -9683,8 +9684,8 @@ MonoBehaviour: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 1, g: 0.5, b: 0.5, a: 1} - m_RaycastTarget: 1 - m_Maskable: 1 + m_RaycastTarget: 0 + m_Maskable: 0 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] @@ -11127,6 +11128,80 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 688648704} m_CullTransparentMesh: 0 +--- !u!1 &693097314 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 693097315} + - component: {fileID: 693097317} + - component: {fileID: 693097316} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &693097315 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 693097314} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1289465654} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &693097316 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 693097314} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &693097317 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 693097314} + m_CullTransparentMesh: 0 --- !u!1 &698711480 GameObject: m_ObjectHideFlags: 0 @@ -13394,6 +13469,80 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 865383678} m_CullTransparentMesh: 0 +--- !u!1 &866545184 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 866545185} + - component: {fileID: 866545187} + - component: {fileID: 866545186} + m_Layer: 5 + m_Name: KeyframeSelector + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &866545185 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 866545184} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 531246357} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0, y: 0} +--- !u!114 &866545186 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 866545184} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.33333334} + m_RaycastTarget: 0 + m_Maskable: 0 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: be42fa87f84235b48a1b2f81baae2c7d, type: 3} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &866545187 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 866545184} + m_CullTransparentMesh: 0 --- !u!1 &868090572 GameObject: m_ObjectHideFlags: 0 @@ -19536,6 +19685,7 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: + - {fileID: 693097315} - {fileID: 1628817485} m_Father: {fileID: 138865804} m_RootOrder: 0 @@ -20446,6 +20596,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: dialogBoxes: {fileID: 682235885} + keyframeSelector: {fileID: 866545184} --- !u!1 &1344551391 GameObject: m_ObjectHideFlags: 0 @@ -24973,7 +25124,7 @@ RectTransform: m_Children: - {fileID: 1437263271} m_Father: {fileID: 1289465654} - m_RootOrder: 0 + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} diff --git a/Assets/Scripts/GUI/AnimationTimeline.cs b/Assets/Scripts/GUI/AnimationTimeline.cs index 4fb5a68f..6ea2bb04 100644 --- a/Assets/Scripts/GUI/AnimationTimeline.cs +++ b/Assets/Scripts/GUI/AnimationTimeline.cs @@ -5,16 +5,20 @@ using System.Text; using System.Threading.Tasks; using UnityEngine; using UnityEngine.UI; +using UnityEngine.EventSystems; namespace RimWorldAnimationStudio { - public class AnimationTimeline : MonoBehaviour + public class AnimationTimeline : MonoBehaviour, IPointerClickHandler { public int actorID; public KeyframeSlider keyframeSliderPrefab; + private Transform anchorTransform; + public void Initialize(int actorID) { + anchorTransform = transform.parent; this.actorID = actorID; Reset(); @@ -71,8 +75,8 @@ namespace RimWorldAnimationStudio public void OnMoveTimeline(int delta) { - int? siblingIndex = transform.parent.GetComponentsInChildren()?.ToList()?.IndexOf(this); - int? siblingCount = transform.parent.GetComponentsInChildren()?.ToList()?.Count(); + int? siblingIndex = anchorTransform.parent.GetComponentsInChildren()?.ToList()?.IndexOf(this); + int? siblingCount = anchorTransform.parent.GetComponentsInChildren()?.ToList()?.Count(); if (siblingIndex != null && siblingCount != null && MoveAnimationTimeline(siblingIndex.Value, delta)) { @@ -102,5 +106,11 @@ namespace RimWorldAnimationStudio return true; } + + public void OnPointerClick(PointerEventData eventData) + { + Workspace.actorID = actorID; + Workspace.keyframeID.Clear(); + } } } diff --git a/Assets/Scripts/Managers/AnimationController.cs b/Assets/Scripts/Managers/AnimationController.cs index 7456b3b7..15e041d3 100644 --- a/Assets/Scripts/Managers/AnimationController.cs +++ b/Assets/Scripts/Managers/AnimationController.cs @@ -382,6 +382,62 @@ namespace RimWorldAnimationStudio } public void PastePawnKeyframes() + { + List actorsInvolved = Workspace.copiedKeyframes.Select(x => x.actorID)?.ToList(); + actorsInvolved = actorsInvolved?.Distinct()?.ToList(); + + foreach (int i in actorsInvolved) { Debug.Log("Actor: " + i); } + + if (actorsInvolved.NullOrEmpty()) { Debug.Log("Cannot paste keyframes - there were no copied keyframes to paste"); return; } + if (actorsInvolved.Count > 1 && actorsInvolved.Contains(Workspace.actorID) == false) { Debug.Log("Cannot paste keyframes - keyframes copied across multiple timelines can only be pasted back into these source timelines"); return; } + + int earliestTick = actorsInvolved.Count == 1 ? Workspace.Instance.GetEarliestAtTickInCopiedKeyframes(actorsInvolved[0]) : Workspace.Instance.GetEarliestAtTickInCopiedKeyframes(Workspace.actorID); + if (earliestTick < 1) { Debug.Log("Unknown error occured during keyframe paste operation"); return; } + + foreach (PawnKeyframe copiedKeyframe in Workspace.copiedKeyframes) + { + int tickToPasteAt = stageTick + (copiedKeyframe.atTick.Value - earliestTick); + + if (tickToPasteAt < 1) continue; + if (tickToPasteAt > Workspace.StageWindowSize) + { + if (stretchkeyframesToggle.isOn) + { ResizeStageWindowSize(tickToPasteAt); } + + else continue; + } + + int targetActorID = actorsInvolved.Count == 1 ? Workspace.actorID : copiedKeyframe.actorID; + + if (Workspace.Instance.DoesPawnKeyframeExistAtTick(Workspace.stageID, targetActorID, tickToPasteAt)) + { + PawnKeyframe oldKeyframe = Workspace.animationDef.animationStages[Workspace.stageID].animationClips[targetActorID].keyframes.First(x => x.atTick == tickToPasteAt); + RemovePawnKeyframe(targetActorID, oldKeyframe.keyframeID, true); + } + + PawnKeyframe clonedKeyframe = copiedKeyframe.Copy(); + clonedKeyframe.GenerateKeyframeID(targetActorID); + clonedKeyframe.atTick = tickToPasteAt; + + PawnAnimationClip clip = Workspace.animationDef.animationStages[Workspace.stageID].animationClips[targetActorID]; + PawnKeyframe nextKeyframe = clip.keyframes.FirstOrDefault(x => x.atTick > stageTick); + + if (nextKeyframe != null) + { clip.keyframes.Insert(clip.keyframes.IndexOf(nextKeyframe), clonedKeyframe); } + + else + { clip.keyframes.Add(clonedKeyframe); } + + clip.BuildSimpleCurves(); + + animationTimelines.GetComponentsInChildren()[clonedKeyframe.actorID].AddPawnKeyFrame(clonedKeyframe.keyframeID); + } + + Workspace.Instance.RecordEvent("Keyframe pasted"); + } + + + /*public void PastePawnKeyframes() { foreach (PawnKeyframe keyframe in Workspace.copiedKeyframes) { @@ -413,7 +469,7 @@ namespace RimWorldAnimationStudio } Workspace.Instance.RecordEvent("Keyframe pasted"); - } + }*/ public void RemovePawnKeyframe() { @@ -507,13 +563,7 @@ namespace RimWorldAnimationStudio } } - animationClipLengthField.text = newStageWindowSize.ToString(); - - Workspace.animationDef.animationStages[Workspace.stageID].stageWindowSize = newStageWindowSize; - Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks = newStageWindowSize * int.Parse(cyclesNormalField.text); - Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicksQuick = newStageWindowSize * int.Parse(cyclesFastField.text); - - Workspace.Instance.RecordEvent("Stage length"); + ResizeStageWindowSize(newStageWindowSize); } public void StretchKeyframes(int newStageWindowSize) @@ -532,6 +582,17 @@ namespace RimWorldAnimationStudio } } + public void ResizeStageWindowSize(int newStageWindowSize) + { + animationClipLengthField.text = newStageWindowSize.ToString(); + + Workspace.animationDef.animationStages[Workspace.stageID].stageWindowSize = newStageWindowSize; + Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks = newStageWindowSize * int.Parse(cyclesNormalField.text); + Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicksQuick = newStageWindowSize * int.Parse(cyclesFastField.text); + + Workspace.Instance.RecordEvent("Stage length"); + } + public void OnCycleNormalFieldChange() { if (Workspace.animationDef == null) return; diff --git a/Assets/Scripts/Managers/InputManager.cs b/Assets/Scripts/Managers/InputManager.cs index 3db59c04..69d0e21a 100644 --- a/Assets/Scripts/Managers/InputManager.cs +++ b/Assets/Scripts/Managers/InputManager.cs @@ -4,18 +4,24 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; +using UnityEngine.UI; +using UnityEngine.EventSystems; namespace RimWorldAnimationStudio { public class InputManager : Singleton { public Transform dialogBoxes; + public GameObject keyframeSelector; private float lastUpdate = -1f; private float timeBetweenUpdates = 0.15f; private float largeStep = 0.1f; private float smallStep = 0.03f; + private bool isDragging; + private Vector2 v0; + public bool CanRepeatThisUpdate() { if (Time.unscaledTime > lastUpdate + timeBetweenUpdates) @@ -55,6 +61,7 @@ namespace RimWorldAnimationStudio bool canRepeatThisUpdate = CanRepeatThisUpdate(); + // Check keybinds foreach (Keybind keybind in KeybindConfig.Instance.GetAllKeybinds()) { if (IsModifierKeyHeld() && keybind.keyModifiers.NullOrEmpty()) goto nextKeybind; @@ -73,6 +80,89 @@ namespace RimWorldAnimationStudio nextKeybind:; } + + // Drag selection + if (Input.GetMouseButtonDown(0)) + { StartKeyframeSelectionDrag(); } + + else if (Input.GetMouseButtonUp(0)) + { EndKeyframeSelectionDrag(); } + + else if (isDragging) + { UpdateKeyframeSelector(); } + + else + { HideKeyframeSelector(); } + } + + public void StartKeyframeSelectionDrag() + { + PointerEventData pointer = new PointerEventData(EventSystem.current); + pointer.position = Input.mousePosition; + + List raycastResults = new List(); + EventSystem.current.RaycastAll(pointer, raycastResults); + + if (raycastResults.Count > 0) + { + foreach (var go in raycastResults) + { + //Debug.Log(go.gameObject); + if (go.gameObject?.GetComponent