New paste functions plus keyframe drag select

This commit is contained in:
AbstractConcept 2022-10-13 00:33:18 -05:00
parent 3d2c11f469
commit f449a9b4e2
117 changed files with 399 additions and 81 deletions

View file

@ -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:

View file

@ -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}

View file

@ -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<AnimationTimeline>()?.ToList()?.IndexOf(this);
int? siblingCount = transform.parent.GetComponentsInChildren<AnimationTimeline>()?.ToList()?.Count();
int? siblingIndex = anchorTransform.parent.GetComponentsInChildren<AnimationTimeline>()?.ToList()?.IndexOf(this);
int? siblingCount = anchorTransform.parent.GetComponentsInChildren<AnimationTimeline>()?.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();
}
}
}

View file

@ -382,6 +382,62 @@ namespace RimWorldAnimationStudio
}
public void PastePawnKeyframes()
{
List<int> 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<AnimationTimeline>()[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;

View file

@ -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<InputManager>
{
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<RaycastResult> raycastResults = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointer, raycastResults);
if (raycastResults.Count > 0)
{
foreach (var go in raycastResults)
{
//Debug.Log(go.gameObject);
if (go.gameObject?.GetComponent<Button>() != null) return;
if (go.gameObject?.GetComponent<IDragHandler>() != null) return;
if (go.gameObject?.GetComponentInParent<IDragHandler>() != null) return;
}
}
v0 = (Vector2)Input.mousePosition;
isDragging = true;
}
public void EndKeyframeSelectionDrag()
{
if (isDragging == false) return;
isDragging = false;
Vector2 v1 = (Vector2)Input.mousePosition;
List<int> keyframeIDs = new List<int>();
IEnumerable<KeyframeSlider> sliders = Selectable.allSelectablesArray.Select(x => x.GetComponent<KeyframeSlider>());
foreach (KeyframeSlider slider in sliders)
{
Transform handle = slider?.transform?.FindDeepChild("Handle");
if (handle == null) continue;
Vector2 vi = (Vector2)handle.GetComponent<RectTransform>().position;
if (vi.x < Mathf.Min(v0.x, v1.x)) continue;
if (vi.x > Mathf.Max(v0.x, v1.x)) continue;
if (vi.y < Mathf.Min(v0.y, v1.y)) continue;
if (vi.y > Mathf.Max(v0.y, v1.y)) continue;
keyframeIDs.Add(slider.keyframeID);
}
if (keyframeIDs.NotNullOrEmpty())
{ Workspace.keyframeID = keyframeIDs; }
}
public void UpdateKeyframeSelector()
{
Vector2 v1 = (Vector2)Input.mousePosition;
Vector2 pos = new Vector2(Mathf.Min(v0.x, v1.x), Mathf.Min(v0.y, v1.y));
Vector2 dim = new Vector2(Mathf.Abs(v1.x - v0.x), Mathf.Abs(v1.y - v0.y));
keyframeSelector.GetComponent<RectTransform>().anchoredPosition = pos;
keyframeSelector.GetComponent<RectTransform>().sizeDelta = dim;
if (keyframeSelector.activeSelf == false)
{ keyframeSelector.SetActive(true); }
}
public void HideKeyframeSelector()
{
if (keyframeSelector.activeSelf)
{ keyframeSelector.SetActive(false); }
}
public void QuitApplication()

View file

@ -194,6 +194,14 @@ namespace RimWorldAnimationStudio
return bestAtTick;
}
public int GetEarliestAtTickInCopiedKeyframes(int actorID)
{
IEnumerable<Keyframe> keyframes = copiedKeyframes.Where(x => x.actorID == actorID);
if (keyframes == null || keyframes.Any() == false) return -1;
return keyframes.Min(x => x.atTick).Value;
}
[SerializeField]
public LinkedList<HistoricRecord> pastSnapshots = new LinkedList<HistoricRecord>();
public LinkedList<HistoricRecord> futureSnapshots = new LinkedList<HistoricRecord>();

View file

@ -348,7 +348,6 @@
<Keybind>
<command>CycleActorBodyPartSelecion</command>
<repeatable>true</repeatable>
<win>
<keyCode>Tab</keyCode>
</win>