Skip to content

Instantly share code, notes, and snippets.

@alankent
Created February 16, 2023 20:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alankent/95a0ba1315bd1b9d7af8fe7060b8052f to your computer and use it in GitHub Desktop.
Save alankent/95a0ba1315bd1b9d7af8fe7060b8052f to your computer and use it in GitHub Desktop.
Simple Unity script to capture an object position delta movement so you can easily apply it by exiting Timeline preview mode
using System.Linq;
using UnityEditor;
using UnityEditor.Timeline;
using UnityEditor.Timeline.Actions;
using UnityEngine;
using UnityEngine.Timeline;
// Adding a new menu item to the move/rotate etc commands would be cooler!
// https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/GUI/Tools/BuiltinTools.cs
// Also https://github.com/HiddenMonk/Unity3DRuntimeTransformGizmo looked interesting (a move gizmo)
// See https://docs.unity3d.com/ScriptReference/MenuItem.html
#if UNITY_EDITOR
namespace OrdinaryCartoonMaker
{
public static class MoveAnimatedCharacterState
{
public static AnimationTrack track;
public static bool markedPosition1Set = false;
public static Vector3 markedPosition1;
public static bool markedPosition2Set = false;
public static Vector3 markedPosition2;
}
[MenuEntry("Mark Object Start Position", MenuPriority.CustomTimelineActionSection.start)]
public class MarkStartAnimatedCharacterMenuItem : TimelineAction
{
public override ActionValidity Validate(ActionContext context)
{
if (!context.tracks.Any())
{
return ActionValidity.NotApplicable;
}
var track = context.tracks.First() as AnimationTrack;
if (track != null && context.director != null)
{
// TODO: Really should be cleared when selection changes to a different track.
if (track != MoveAnimatedCharacterState.track)
{
MoveAnimatedCharacterState.track = null;
MoveAnimatedCharacterState.markedPosition1Set = false;
MoveAnimatedCharacterState.markedPosition2Set = false;
}
var binding = context.director.GetGenericBinding(track);
if (binding != null)
{
return ActionValidity.Valid;
}
}
return ActionValidity.Invalid;
}
public override bool Execute(ActionContext context)
{
var track = context.tracks.First() as AnimationTrack;
var binding = context.director.GetGenericBinding(track) as Animator;
MoveAnimatedCharacterState.markedPosition1 = binding.transform.localPosition;
MoveAnimatedCharacterState.markedPosition1Set = true;
MoveAnimatedCharacterState.track = track;
Debug.Log("MARKED START POSITION");
Debug.Log(MoveAnimatedCharacterState.markedPosition1);
return true;
}
}
[MenuEntry("Mark Object End Position", MenuPriority.CustomTimelineActionSection.start)]
public class MarkEndAnimatedCharacterMenuItem : TimelineAction
{
public override ActionValidity Validate(ActionContext context)
{
if (!context.tracks.Any() || !MoveAnimatedCharacterState.markedPosition1Set)
{
return ActionValidity.NotApplicable;
}
var track = context.tracks.First() as AnimationTrack;
if (track != null && context.director != null)
{
// TODO: Really should be cleared when selection changes to a different track.
if (track != MoveAnimatedCharacterState.track)
{
MoveAnimatedCharacterState.track = null;
MoveAnimatedCharacterState.markedPosition1Set = false;
MoveAnimatedCharacterState.markedPosition2Set = false;
}
var binding = context.director.GetGenericBinding(track);
if (binding != null)
{
return ActionValidity.Valid;
}
}
return ActionValidity.Invalid;
}
public override bool Execute(ActionContext context)
{
var track = context.tracks.First() as AnimationTrack;
var binding = context.director.GetGenericBinding(track) as Animator;
MoveAnimatedCharacterState.markedPosition2 = binding.transform.localPosition;
MoveAnimatedCharacterState.markedPosition2Set = true;
Debug.Log("MARKED END POSITION");
Debug.Log(MoveAnimatedCharacterState.markedPosition2);
return true;
}
}
[MenuEntry("Move Object Position", MenuPriority.CustomTimelineActionSection.start)]
public class MoveAnimatedCharacterMenuItem : TimelineAction
{
public override ActionValidity Validate(ActionContext context)
{
if (!context.tracks.Any() || !MoveAnimatedCharacterState.markedPosition1Set || !MoveAnimatedCharacterState.markedPosition2Set)
{
return ActionValidity.NotApplicable;
}
var track = context.tracks.First() as AnimationTrack;
if (track != null && context.director != null)
{
var binding = context.director.GetGenericBinding(track);
if (binding != null)
{
return ActionValidity.Valid;
}
}
return ActionValidity.Invalid;
}
public override bool Execute(ActionContext context)
{
var track = context.tracks.First() as AnimationTrack;
var binding = context.director.GetGenericBinding(track) as Animator;
var deltaMovement = MoveAnimatedCharacterState.markedPosition2 - MoveAnimatedCharacterState.markedPosition1;
Undo.RegisterCompleteObjectUndo(binding.gameObject, "Move animated object position");
binding.transform.localPosition += deltaMovement;
Debug.Log("MOVED OBJECT");
Debug.Log(deltaMovement);
return true;
}
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment