Manual timeline, cycle and actor tweaking

This commit is contained in:
AbstractConcept 2022-09-16 17:50:15 -05:00
parent dfa564759b
commit 1dd7781179
165 changed files with 4040 additions and 166 deletions

View file

@ -10,6 +10,8 @@ GameObject:
m_Component:
- component: {fileID: 7929422519883802245}
- component: {fileID: 7929422519883802244}
- component: {fileID: 3275330537164762353}
- component: {fileID: 7621569460770085946}
m_Layer: 0
m_Name: ActorHead
m_TagString: Untagged
@ -70,17 +72,57 @@ SpriteRenderer:
m_SortingLayerID: -2115984483
m_SortingLayer: 22
m_SortingOrder: 1
m_Sprite: {fileID: 0}
m_Sprite: {fileID: 21300000, guid: 0b37cc6354dc6a94cb2d2de2529baa4e, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
m_Size: {x: 1, y: 1}
m_Size: {x: 1.28, y: 1.28}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!114 &3275330537164762353
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7929422519883802246}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b23e33f312d52c642b86f5f2138f4030, type: 3}
m_Name:
m_EditorClassIdentifier:
bodyPartRenderer: {fileID: 7929422519883802244}
parent: {fileID: -4411442180840688308}
--- !u!61 &7621569460770085946
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7929422519883802246}
m_Enabled: 1
m_Density: 1
m_Material: {fileID: 0}
m_IsTrigger: 0
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: 0}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0.5, y: 0.5}
oldSize: {x: 1.28, y: 1.28}
newSize: {x: 1.28, y: 1.28}
adaptiveTilingThreshold: 0.5
drawMode: 0
adaptiveTiling: 0
m_AutoTiling: 0
serializedVersion: 2
m_Size: {x: 0.5, y: 0.5}
m_EdgeRadius: 0
--- !u!1 &7929422520673851210
GameObject:
m_ObjectHideFlags: 0
@ -92,6 +134,7 @@ GameObject:
- component: {fileID: 7929422520673851209}
- component: {fileID: 7929422520673851208}
- component: {fileID: -4411442180840688308}
- component: {fileID: -7575978412006062152}
m_Layer: 0
m_Name: ActorBody
m_TagString: Untagged
@ -153,15 +196,15 @@ SpriteRenderer:
m_SortingLayerID: -2115984483
m_SortingLayer: 22
m_SortingOrder: 0
m_Sprite: {fileID: 0}
m_Sprite: {fileID: 21300000, guid: e6887bc2f64df4d4b91bd2d0ad0ffd98, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
m_Size: {x: 1, y: 1}
m_Size: {x: 1.5058824, y: 1.5058824}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!114 &-4411442180840688308
@ -176,5 +219,32 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 511a9ed9093e7fc458dec8d3c657f9a5, type: 3}
m_Name:
m_EditorClassIdentifier:
actorID: 0
bodyRenderer: {fileID: 7929422520673851208}
headRenderer: {fileID: 7929422519883802244}
--- !u!61 &-7575978412006062152
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7929422520673851210}
m_Enabled: 1
m_Density: 1
m_Material: {fileID: 0}
m_IsTrigger: 0
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: -0.2}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0.5, y: 0.5}
oldSize: {x: 1.5058824, y: 1.5058824}
newSize: {x: 1.5058824, y: 1.5058824}
adaptiveTilingThreshold: 0.5
drawMode: 0
adaptiveTiling: 0
m_AutoTiling: 0
serializedVersion: 2
m_Size: {x: 0.75, y: 1}
m_EdgeRadius: 0

View file

@ -107,7 +107,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 1, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 20}
m_SizeDelta: {x: -10, y: 20}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &7758949423219383195
MonoBehaviour:
@ -147,7 +147,7 @@ MonoBehaviour:
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_Interactable: 0
m_TargetGraphic: {fileID: 0}
m_FillRect: {fileID: 0}
m_HandleRect: {fileID: 4629009613275671144}
@ -164,6 +164,8 @@ MonoBehaviour:
ghostSliderPrefab: {fileID: 3581489635090573721, guid: 890d76c226858de4fa96adfe7cc85383,
type: 3}
maxGhosts: 0
actorID: 0
keyframeID: 0
--- !u!1 &8359461402257861397
GameObject:
m_ObjectHideFlags: 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f2673788e9e92e147b38af939d2fb7fe
guid: 51ffbfed19686f041975a6e1757db741
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
@ -45,7 +45,7 @@ TextureImporter:
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 20
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,73 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
namespace RimWorldAnimationStudio
{
public class ActorBody : MonoBehaviour
public class ActorBody : MonoBehaviour, IPointerClickHandler, IDragHandler
{
public int actorID;
public SpriteRenderer bodyRenderer;
public SpriteRenderer headRenderer;
public void Initialize(int actorID)
{
this.actorID = actorID;
}
public void OnPointerClick(PointerEventData eventData)
{
if (eventData.pointerCurrentRaycast.gameObject.GetComponent<ActorBody>() == null)
{ return; }
foreach (ActorBody actorBody in AnimationController.Instance.actorBodies)
{
if (actorBody == this)
{ continue; }
actorBody.bodyRenderer.color = new Color(1f, 1f, 1f);
actorBody.headRenderer.color = new Color(1f, 1f, 1f);
}
bodyRenderer.color = new Color(0f, 1f, 0f);
headRenderer.color = new Color(0f, 1f, 0f);
Workspace.actorID = actorID;
}
public void OnDrag(PointerEventData eventData)
{
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (Workspace.actorManipulationMode == ActorManipulationMode.Pan)
{
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).bodyOffsetX = mousePosition.x;
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).bodyOffsetZ = mousePosition.y;
}
else if (Workspace.actorManipulationMode == ActorManipulationMode.Rotate)
{
float angle = Vector2.SignedAngle(Vector2.down, (Vector2)mousePosition - (Vector2)transform.position);
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).bodyAngle = angle;
}
else if (Workspace.actorManipulationMode == ActorManipulationMode.Face)
{
float angle = Vector2.SignedAngle(Vector2.up, (Vector2)mousePosition - (Vector2)transform.position);
int facing = -Mathf.RoundToInt(angle / 90f );
facing = facing < 0 ? facing + 4 : facing;
Debug.Log(facing.ToString());
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).bodyFacing = facing;
}
PawnAnimationClip clip = Workspace.Instance.GetPawnAnimationClip(actorID);
clip.BuildSimpleCurves();
}
}
}

View file

@ -0,0 +1,67 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
namespace RimWorldAnimationStudio
{
public class ActorBodyPart : MonoBehaviour, IDragHandler, IPointerClickHandler
{
public SpriteRenderer bodyPartRenderer;
public ActorBody parent;
public void OnPointerClick(PointerEventData eventData)
{
if (eventData.pointerCurrentRaycast.gameObject.GetComponent<ActorBodyPart>() == null)
{ return; }
foreach (ActorBody actorBody in AnimationController.Instance.actorBodies)
{
actorBody.bodyRenderer.color = new Color(1f, 1f, 1f);
actorBody.headRenderer.color = new Color(1f, 1f, 1f);
}
bodyPartRenderer.color = new Color(0f, 1f, 0f);
}
public void OnDrag(PointerEventData eventData)
{
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
PawnKeyframe keyframe = Workspace.animationDef.animationStages[Workspace.stageID].animationClips[parent.actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID);
if (Workspace.actorManipulationMode == ActorManipulationMode.Pan)
{
float distance = ((Vector2)mousePosition - (Vector2)transform.position).y;
Vector3 headOffset = new Vector3(0f, 0.34f, 0f);
headOffset = Quaternion.Euler(0, 0, keyframe.bodyAngle) * headOffset;
distance = Vector2.Dot(parent.transform.up, (Vector2)(mousePosition - parent.transform.position - headOffset));
Debug.Log(headOffset.ToString());
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[parent.actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).headBob = distance;
}
else if (Workspace.actorManipulationMode == ActorManipulationMode.Rotate)
{
float angle = Vector2.SignedAngle(Vector2.down, (Vector2)mousePosition - (Vector2)transform.position);
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[parent.actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).headAngle = angle;
}
else if (Workspace.actorManipulationMode == ActorManipulationMode.Face)
{
float angle = Vector2.SignedAngle(Vector2.up, (Vector2)mousePosition - (Vector2)transform.position);
int facing = -Mathf.RoundToInt(angle / 90f);
facing = facing < 0 ? facing + 4 : facing;
Debug.Log(facing.ToString());
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[parent.actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).headFacing = facing;
}
PawnAnimationClip clip = Workspace.Instance.GetPawnAnimationClip(parent.actorID);
clip.BuildSimpleCurves();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b23e33f312d52c642b86f5f2138f4030
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -49,8 +49,6 @@ namespace RimWorldAnimationStudio
if (keyframe.HasValidKeyframeID() == false)
{ keyframe.GenerateKeyframeID(); }
Debug.Log(keyframe.atTick.Value);
BodyAngle.Add((float)keyframe.atTick / (float)duration, keyframe.bodyAngle, true);
HeadAngle.Add((float)keyframe.atTick / (float)duration, keyframe.headAngle, true);
BodyOffsetX.Add((float)keyframe.atTick / (float)duration, keyframe.bodyOffsetX, true);

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using UnityEngine;
@ -25,8 +26,15 @@ namespace RimWorldAnimationStudio
public void GenerateKeyframeID()
{
keyframeID = Random.Range(100000, 1000000);
Debug.Log("Generated ID: " + keyframeID);
int _keyframeID = Random.Range(100000, 1000000);
if (Workspace.animationDef.animationStages.Any(x => x.animationClips.Any(y => y.keyframes.Any(z => z.keyframeID == _keyframeID))))
{
GenerateKeyframeID();
return;
}
keyframeID = _keyframeID;
}
public bool HasValidKeyframeID()

View file

@ -36,6 +36,10 @@ namespace RimWorldAnimationStudio
public AnimationTimeline animationTimelinePrefab;
private float currentTime = 0;
private int cycleIndex = 0;
public InputField cyclesNormalField;
public InputField cyclesFastField;
public void Update()
{
@ -58,25 +62,29 @@ namespace RimWorldAnimationStudio
currentTime -= 1f/60f;
stageTick += 1;
int stageLenght = Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks;
if (stageTick > stageLenght)
if (stageTick > Workspace.animationClipWindowSize)
{
if (stageLoopDropdown.value == 1)
{ stageTick = 1; }
else if (stageLoopDropdown.value == 2 && Workspace.stageID < Workspace.animationDef.animationStages.Count - 1)
{
stageTick = 1;
Workspace.stageID++;
stageIDField.text = Workspace.stageID.ToString();
{
++cycleIndex;
stageTick = 1;
if (cycleIndex > int.Parse(cyclesNormalField.text))
{
++Workspace.stageID;
stageIDField.text = Workspace.stageID.ToString();
cycleIndex = 0;
}
}
else
{ stageTick = stageLenght; }
{ stageTick = Workspace.animationClipWindowSize; }
}
stageTimelineSlider.maxValue = stageLenght;
stageTimelineSlider.maxValue = Workspace.animationClipWindowSize;
stageTimelineSlider.value = stageTick;
UpdateAnimation();
@ -87,12 +95,12 @@ namespace RimWorldAnimationStudio
if (stageTickText != null)
{ stageTickText.text = stageTick.ToString(); }
if (stageLengthText != null)
{ stageLengthText.text = Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks.ToString(); }
{ stageLengthText.text = Workspace.animationClipWindowSize.ToString(); }
for (int actorID = 0; actorID < actorBodies.Count; actorID++)
{
ActorBody actorBody = actorBodies[actorID];
string bodyType = actorCards.transform.GetChild(actorID).GetComponent<ActorCard>().bodyType; //bodyTypeDropdowns[actorID].options[bodyTypeDropdowns[actorID].value].text;
string bodyType = actorCards.transform.GetChild(actorID).GetComponent<ActorCard>().bodyType;
PawnAnimationClip clip = Workspace.animationDef.animationStages[Workspace.stageID].animationClips[actorID];
float clipPercent = (float)(stageTick % clip.duration) / clip.duration;
@ -128,7 +136,7 @@ namespace RimWorldAnimationStudio
actorBody.bodyRenderer.sortingLayerName = clip.layer;
actorBody.headRenderer.sortingLayerName = clip.layer;
actorBody.headRenderer.sortingOrder = headFacing == 2 ? 1 : -1;
actorBody.headRenderer.sortingOrder = headFacing == 0 ? -1 : 1;
}
}
@ -154,10 +162,17 @@ namespace RimWorldAnimationStudio
Reset();
Workspace.animationClipWindowSize = Workspace.animationDef.animationStages[Workspace.stageID].animationClips.Select(x => x.duration).Max();
stageTimelineSlider.maxValue = Workspace.animationClipWindowSize;
cyclesNormalField.text = Mathf.Max(Mathf.CeilToInt((float)Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks / Workspace.animationClipWindowSize), 1).ToString();
cyclesFastField.text = Mathf.Max(Mathf.CeilToInt((float)Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicksQuick / Workspace.animationClipWindowSize), 1).ToString();
for (int actorID = 0; actorID < Workspace.animationDef.actors.Count; actorID++)
{
GameObject actorBodyObject = Instantiate(actorBodyPrefab, transform);
actorBodies.Add(actorBodyObject.GetComponent<ActorBody>());
actorBodyObject.GetComponent<ActorBody>().Initialize(actorID);
GameObject actorCardObject = Instantiate(actorCardPrefab, actorCards);
actorCardObject.GetComponent<ActorCard>().Initialize(Workspace.animationDef.actors[actorID]);
@ -204,10 +219,13 @@ namespace RimWorldAnimationStudio
if (Workspace.animationDef == null)
{ return; }
int stageLenght = Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks;
if (stageLenght != (int)stageTimelineSlider.value)
if (stageTick != (int)stageTimelineSlider.value)
{ stageTick = (int)stageTimelineSlider.value; }
}
public void ToggleActorManipulationMode(int mode)
{
Workspace.actorManipulationMode = (ActorManipulationMode)mode;
}
}
}

View file

@ -48,8 +48,8 @@ namespace RimWorldAnimationStudio
{
Debug.Log("Loaded AnimationDef: " + animationDef.defName);
animationDef.Initialize();
Workspace.animationDef = animationDef;
animationDef.Initialize();
Workspace.isDirty = true;
var animationDefCards = Resources.FindObjectsOfTypeAll(typeof(AnimationDefCard)) as AnimationDefCard[];
@ -78,8 +78,19 @@ namespace RimWorldAnimationStudio
{
Debug.Log("Saving AnimationDef: " + Workspace.animationDef.defName);
AnimationDef animationDef = Workspace.animationDef;
foreach (AnimationStage stage in animationDef.animationStages)
{
foreach (PawnAnimationClip clip in stage.animationClips)
{
clip.keyframes = clip.keyframes.OrderBy(x => x.atTick).ToList();
}
}
Defs defs = new Defs();
defs.animationDefs.Add(Workspace.animationDef);
defs.animationDefs.Add(animationDef);
XmlUtility.WriteXML(defs, path);
}

7
Assets/Scripts/Enums.cs Normal file
View file

@ -0,0 +1,7 @@
public enum ActorManipulationMode
{
Pan,
Rotate,
Face,
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ee120e06e7d8c1f45ad58fc6baab0d47
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,65 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class ActorKeyframeCard : MonoBehaviour
{
public InputField positionXField;
public InputField positionZField;
public InputField rotationField;
private int lastTick = -1;
private bool isDirty = false;
public void Update()
{
if ((Workspace.animationDef == null || AnimationController.Instance.stageTick == lastTick) && isDirty == false)
{ return; }
ActorBody actorBody = AnimationController.Instance.actorBodies[Workspace.actorID];
string bodyType = AnimationController.Instance.actorCards.transform.GetChild(Workspace.actorID).GetComponent<ActorCard>().bodyType;
PawnAnimationClip clip = Workspace.animationDef.animationStages[Workspace.stageID].animationClips[Workspace.actorID];
float clipPercent = (float)(AnimationController.Instance.stageTick % clip.duration) / clip.duration;
Vector3 deltaPos = new Vector3(clip.BodyOffsetX.Evaluate(clipPercent), 0, clip.BodyOffsetZ.Evaluate(clipPercent));
deltaPos += Workspace.animationDef.actors[Workspace.actorID].bodyTypeOffset.GetOffset(bodyType);
float bodyAngle = clip.BodyAngle.Evaluate(clipPercent);
float headAngle = clip.HeadAngle.Evaluate(clipPercent);
if (bodyAngle < 0) bodyAngle = 360 - ((-1f * bodyAngle) % 360);
if (bodyAngle > 360) bodyAngle %= 360;
if (headAngle < 0) headAngle = 360 - ((-1f * headAngle) % 360);
if (headAngle > 360) headAngle %= 360;
int bodyFacing = (int)clip.BodyFacing.Evaluate(clipPercent);
Vector3 headBob = new Vector3(0, 0, clip.HeadBob.Evaluate(clipPercent)) + PawnUtility.BaseHeadOffsetAt(bodyType, bodyFacing);
Vector3 bodyPos = new Vector3(deltaPos.x, deltaPos.z, 0);
Vector3 headPos = new Vector3(headBob.x, headBob.z, 0);
positionXField.text = bodyPos.x.ToString("0.000");
positionZField.text = bodyPos.y.ToString("0.000");
rotationField.text = bodyAngle.ToString("0.000");
lastTick = AnimationController.Instance.stageTick;
isDirty = false;
}
public void OnValueChanged()
{
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[Workspace.actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).bodyOffsetX = float.Parse(positionXField.text);
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[Workspace.actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).bodyOffsetZ = float.Parse(positionZField.text);
Workspace.animationDef.animationStages[Workspace.stageID].animationClips[Workspace.actorID].keyframes.FirstOrDefault(x => x.keyframeID == Workspace.keyframeID).bodyAngle = float.Parse(rotationField.text);
Workspace.Instance.GetPawnAnimationClip(Workspace.actorID).BuildSimpleCurves();
isDirty = true;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 58ad3d066d9103541806d07bc98823d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -4,11 +4,12 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class KeyframeSlider : Slider
public class KeyframeSlider : Slider, IPointerClickHandler, IBeginDragHandler, IEndDragHandler
{
public AnimationTimeline timeline;
//public AnimationClip clip;
@ -31,9 +32,8 @@ namespace RimWorldAnimationStudio
this.keyframeID = keyframeID;
PawnKeyframe keyframe = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID);
Debug.Log(keyframe);
value = (float)keyframe.atTick / Workspace.Instance.GetCurrentStageLength();
Debug.Log(keyframe.atTick);
value = (float)keyframe.atTick / Workspace.animationClipWindowSize;
OnValueChanged();
onValueChanged.AddListener(delegate (float value) { OnValueChanged(); });
@ -44,32 +44,15 @@ namespace RimWorldAnimationStudio
PawnKeyframe keyframe = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID);
PawnAnimationClip clip = Workspace.Instance.GetPawnAnimationClip(actorID);
int stageLength = Workspace.Instance.GetCurrentStageLength();
int newTick = Mathf.RoundToInt(value * stageLength);
/*if (timeline.CanAddKeyFrameAtTick(newTick) == false)
{
int delta = keyframe.atTick > newTick ? 1 : -1;
while (timeline.CanAddKeyFrameAtTick(newTick) == false)
{
newTick += delta;
if (newTick == 1 || newTick == stageLength) { break; }
}
if (timeline.CanAddKeyFrameAtTick(newTick) == false)
{ value = (float)keyframe.atTick / stageLength; return; }
}*/
int newTick = Mathf.RoundToInt(value * Workspace.animationClipWindowSize);
keyframe.atTick = newTick;
Debug.Log("Value changed: " + newTick);
//value = (float)keyframe.atTick / stageLength;
UpdateGhostFrames();
clip.BuildSimpleCurves();
AnimationController.Instance.stageTick = keyframe.atTick.Value;
}
// Ghost sliders are non-interactable slider handle
@ -81,12 +64,11 @@ namespace RimWorldAnimationStudio
if (maxGhosts == 0)
{ return; }
int stageLength = Workspace.Instance.GetCurrentStageLength();
int nGhosts = GetGhostFramesRequired();
for (int i = 0; i < Mathf.Max(nGhosts, ghostSliders.childCount); i++)
{
if ((i - 1) * clip.duration + keyframe.atTick <= stageLength)
if ((i - 1) * clip.duration + keyframe.atTick <= Workspace.animationClipWindowSize)
{
if (ghostSliders.childCount <= i)
{ Instantiate(ghostSliderPrefab, ghostSliders); }
@ -97,7 +79,7 @@ namespace RimWorldAnimationStudio
Slider ghostSlider = ghostSliderObject.GetComponent<Slider>();
Debug.Log(ghostSlider);
ghostSlider.value = (float)((i + 1) * clip.duration + keyframe.atTick) / stageLength;
ghostSlider.value = (float)((i + 1) * clip.duration + keyframe.atTick) / Workspace.animationClipWindowSize;
float mult = 1f - Mathf.Pow((float)i / maxGhosts, 2);
ghostSlider.transform.FindDeepChild("Handle").GetComponent<Image>().color = new Color(0, 0.5f, 0.5f, 0.5f * mult);
@ -118,7 +100,36 @@ namespace RimWorldAnimationStudio
if (clip.duration <= 1)
{ return 0; }
return Math.Min(Mathf.CeilToInt((float)Workspace.Instance.GetCurrentStageLength() / clip.duration), maxGhosts);
return Math.Min(Mathf.CeilToInt((float)Workspace.animationClipWindowSize / clip.duration), maxGhosts);
}
public void OnPointerClick(PointerEventData eventData)
{
PawnKeyframe keyframe = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID);
foreach (KeyframeSlider keyframeSlider in timeline.transform.GetComponentsInChildren<KeyframeSlider>())
{
if (keyframeSlider == this)
{ continue; }
keyframeSlider.transform.FindDeepChild("Handle").GetComponent<Image>().color = new Color(1f, 1f, 1f);
}
transform.FindDeepChild("Handle").GetComponent<Image>().color = new Color(0f, 1f, 0f);
AnimationController.Instance.stageTick = keyframe.atTick.Value;
Workspace.keyframeID = keyframeID;
}
public void OnBeginDrag(PointerEventData eventData)
{
interactable = true;
}
public void OnEndDrag(PointerEventData eventData)
{
interactable = false;
}
}
}

View file

@ -11,6 +11,8 @@ namespace RimWorldAnimationStudio
{
public static AnimationDef animationDef;
public static int stageID = 0;
public static int actorID = 0;
public static int keyframeID = 0;
public static bool isDirty = false;
public static List<string> defNames = new List<string>() { "Human" };
@ -23,13 +25,15 @@ namespace RimWorldAnimationStudio
private static int maxHistoryDepth = 100;
private static int historyIndex = 0;
public int GetCurrentStageLength()
{
if (stageID < 0 || stageID >= animationDef.animationStages.Count)
{ return 0; }
public static ActorManipulationMode actorManipulationMode = ActorManipulationMode.Pan;
public static int animationClipWindowSize = 600;
return animationDef.animationStages[stageID].playTimeTicks;
}
//public int? GetCurrentKeyframe()
//{
// return animationDef?.animationStages[stageID]?.animationClips[actorID]?.keyframes.FirstOrDefault(x => x.keyframeID == keyframeID)?.keyframeID;
//}
public PawnAnimationClip GetPawnAnimationClip(int actorID)
{