mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
Basic actor timelines and keyframe tweaking
This commit is contained in:
parent
0364322d46
commit
dfa564759b
246 changed files with 1918 additions and 150 deletions
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
|
@ -21,58 +22,81 @@ namespace RimWorldAnimationStudio
|
|||
|
||||
public override void BuildSimpleCurves()
|
||||
{
|
||||
BodyAngle.Clear();
|
||||
HeadAngle.Clear();
|
||||
BodyOffsetX.Clear();
|
||||
BodyOffsetZ.Clear();
|
||||
HeadFacing.Clear();
|
||||
BodyFacing.Clear();
|
||||
HeadBob.Clear();
|
||||
GenitalAngle.Clear();
|
||||
SoundEffects.Clear();
|
||||
|
||||
int duration = 0;
|
||||
|
||||
foreach (PawnKeyframe frame in keyframes)
|
||||
{ duration += frame.tickDuration; }
|
||||
|
||||
this.duration = duration;
|
||||
|
||||
int keyframePosition = 0;
|
||||
foreach (PawnKeyframe frame in keyframes)
|
||||
|
||||
for (int i = 0; i < keyframes.Count; i++)
|
||||
{
|
||||
if (frame.atTick.HasValue)
|
||||
PawnKeyframe keyframe = keyframes[i];
|
||||
|
||||
if (keyframe.atTick.HasValue)
|
||||
{
|
||||
BodyAngle.Add((float)frame.atTick / (float)duration, frame.bodyAngle, true);
|
||||
HeadAngle.Add((float)frame.atTick / (float)duration, frame.headAngle, true);
|
||||
BodyOffsetX.Add((float)frame.atTick / (float)duration, frame.bodyOffsetX, true);
|
||||
BodyOffsetZ.Add((float)frame.atTick / (float)duration, frame.bodyOffsetZ, true);
|
||||
HeadFacing.Add((float)frame.atTick / (float)duration, frame.headFacing, true);
|
||||
BodyFacing.Add((float)frame.atTick / (float)duration, frame.bodyFacing, true);
|
||||
HeadBob.Add((float)frame.atTick / (float)duration, frame.headBob, true);
|
||||
if (keyframe.HasValidKeyframeID() == false)
|
||||
{ keyframe.GenerateKeyframeID(); }
|
||||
|
||||
if (frame.genitalAngle.HasValue)
|
||||
{ GenitalAngle.Add((float)frame.atTick / (float)duration, frame.genitalAngle.Value, true); }
|
||||
Debug.Log(keyframe.atTick.Value);
|
||||
|
||||
if (frame.soundEffect != null)
|
||||
{ SoundEffects.Add((int)frame.atTick, frame.soundEffect); }
|
||||
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);
|
||||
BodyOffsetZ.Add((float)keyframe.atTick / (float)duration, keyframe.bodyOffsetZ, true);
|
||||
HeadFacing.Add((float)keyframe.atTick / (float)duration, keyframe.headFacing, true);
|
||||
BodyFacing.Add((float)keyframe.atTick / (float)duration, keyframe.bodyFacing, true);
|
||||
HeadBob.Add((float)keyframe.atTick / (float)duration, keyframe.headBob, true);
|
||||
|
||||
if (keyframe.genitalAngle.HasValue)
|
||||
{ GenitalAngle.Add((float)keyframe.atTick / (float)duration, keyframe.genitalAngle.Value, true); }
|
||||
|
||||
if (keyframe.soundEffect != null)
|
||||
{ SoundEffects.Add((int)keyframe.atTick, keyframe.soundEffect); }
|
||||
|
||||
if (i + 1 < keyframes.Count)
|
||||
{ keyframes[i].tickDuration = keyframes[i + 1].atTick.Value - keyframes[i].atTick.Value; }
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
BodyAngle.Add((float)keyframePosition / (float)duration, frame.bodyAngle, true);
|
||||
HeadAngle.Add((float)keyframePosition / (float)duration, frame.headAngle, true);
|
||||
BodyOffsetX.Add((float)keyframePosition / (float)duration, frame.bodyOffsetX, true);
|
||||
BodyOffsetZ.Add((float)keyframePosition / (float)duration, frame.bodyOffsetZ, true);
|
||||
HeadFacing.Add((float)keyframePosition / (float)duration, frame.headFacing, true);
|
||||
BodyFacing.Add((float)keyframePosition / (float)duration, frame.bodyFacing, true);
|
||||
HeadBob.Add((float)keyframePosition / (float)duration, frame.headBob, true);
|
||||
BodyAngle.Add((float)keyframePosition / (float)duration, keyframe.bodyAngle, true);
|
||||
HeadAngle.Add((float)keyframePosition / (float)duration, keyframe.headAngle, true);
|
||||
BodyOffsetX.Add((float)keyframePosition / (float)duration, keyframe.bodyOffsetX, true);
|
||||
BodyOffsetZ.Add((float)keyframePosition / (float)duration, keyframe.bodyOffsetZ, true);
|
||||
HeadFacing.Add((float)keyframePosition / (float)duration, keyframe.headFacing, true);
|
||||
BodyFacing.Add((float)keyframePosition / (float)duration, keyframe.bodyFacing, true);
|
||||
HeadBob.Add((float)keyframePosition / (float)duration, keyframe.headBob, true);
|
||||
|
||||
if (frame.genitalAngle.HasValue)
|
||||
GenitalAngle.Add((float)keyframePosition / (float)duration, frame.genitalAngle.Value, true);
|
||||
if (keyframe.genitalAngle.HasValue)
|
||||
GenitalAngle.Add((float)keyframePosition / (float)duration, keyframe.genitalAngle.Value, true);
|
||||
|
||||
if (frame.soundEffect != null)
|
||||
{ SoundEffects.Add(keyframePosition, frame.soundEffect); }
|
||||
if (keyframe.soundEffect != null)
|
||||
{ SoundEffects.Add(keyframePosition, keyframe.soundEffect); }
|
||||
|
||||
if (frame.tickDuration != 1 && frame.quiver.HasValue)
|
||||
if (keyframe.tickDuration != 1 && keyframe.quiver.HasValue)
|
||||
{
|
||||
quiver.Add(keyframePosition, true);
|
||||
quiver.Add(keyframePosition + frame.tickDuration - 1, false);
|
||||
quiver.Add(keyframePosition + keyframe.tickDuration - 1, false);
|
||||
}
|
||||
|
||||
keyframePosition += frame.tickDuration;
|
||||
keyframe.atTick = keyframePosition + 1;
|
||||
keyframePosition += keyframe.tickDuration;
|
||||
}
|
||||
}
|
||||
|
||||
keyframes[keyframes.Count - 1].tickDuration = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace RimWorldAnimationStudio
|
|||
{
|
||||
public class Keyframe
|
||||
{
|
||||
public float? atTick;
|
||||
public int? atTick;
|
||||
|
||||
public int tickDuration = 1;
|
||||
public string soundEffect;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
|
@ -19,5 +20,16 @@ namespace RimWorldAnimationStudio
|
|||
|
||||
public bool ShouldSerializegenitalAngle() { return genitalAngle != null; }
|
||||
public bool ShouldSerializequiver() { return quiver != null; }
|
||||
|
||||
[XmlIgnore] public int keyframeID;
|
||||
|
||||
public void GenerateKeyframeID()
|
||||
{
|
||||
keyframeID = Random.Range(100000, 1000000);
|
||||
Debug.Log("Generated ID: " + keyframeID);
|
||||
}
|
||||
|
||||
public bool HasValidKeyframeID()
|
||||
{ return keyframeID >= 100000 && keyframeID < 1000000; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ namespace RimWorldAnimationStudio
|
|||
public Dropdown stageLoopDropdown;
|
||||
public InputField stageIDField;
|
||||
|
||||
public Transform animationTimelines;
|
||||
public AnimationTimeline animationTimelinePrefab;
|
||||
|
||||
private float currentTime = 0;
|
||||
|
||||
public void Update()
|
||||
|
@ -158,6 +161,9 @@ namespace RimWorldAnimationStudio
|
|||
|
||||
GameObject actorCardObject = Instantiate(actorCardPrefab, actorCards);
|
||||
actorCardObject.GetComponent<ActorCard>().Initialize(Workspace.animationDef.actors[actorID]);
|
||||
|
||||
AnimationTimeline animationTimeline = Instantiate(animationTimelinePrefab, animationTimelines);
|
||||
animationTimeline.Initialize(actorID);
|
||||
}
|
||||
|
||||
Workspace.isDirty = false;
|
||||
|
@ -177,6 +183,9 @@ namespace RimWorldAnimationStudio
|
|||
foreach (Transform actorCard in actorCards)
|
||||
{ Destroy(actorCard.gameObject); }
|
||||
|
||||
foreach (Transform animationTimeline in animationTimelines)
|
||||
{ Destroy(animationTimeline.gameObject); }
|
||||
|
||||
actorBodies.Clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace RimWorldAnimationStudio
|
|||
Workspace.isDirty = true;
|
||||
|
||||
var animationDefCards = Resources.FindObjectsOfTypeAll(typeof(AnimationDefCard)) as AnimationDefCard[];
|
||||
Debug.Log(animationDefCards);
|
||||
|
||||
if (animationDefCards != null)
|
||||
{
|
||||
animationDefCards[0].Initialize();
|
||||
|
|
8
Assets/Scripts/GUI.meta
Normal file
8
Assets/Scripts/GUI.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e344d7c1fea27134ca49b05d9cac249c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
56
Assets/Scripts/GUI/AnimationTimeline.cs
Normal file
56
Assets/Scripts/GUI/AnimationTimeline.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class AnimationTimeline : MonoBehaviour
|
||||
{
|
||||
public int actorID;
|
||||
public KeyframeSlider keyframeSliderPrefab;
|
||||
|
||||
public void Initialize(int actorID)
|
||||
{
|
||||
this.actorID = actorID;
|
||||
|
||||
PawnAnimationClip clip = Workspace.Instance.GetPawnAnimationClip(actorID);
|
||||
|
||||
if (clip == null || clip.keyframes.NullOrEmpty())
|
||||
{
|
||||
Debug.Log("Clip was empty");
|
||||
|
||||
clip = new PawnAnimationClip();
|
||||
clip.keyframes.Add(new PawnKeyframe());
|
||||
clip.BuildSimpleCurves();
|
||||
}
|
||||
|
||||
foreach (PawnKeyframe keyframe in clip.keyframes)
|
||||
{
|
||||
KeyframeSlider keyframeSlider = Instantiate(keyframeSliderPrefab, transform);
|
||||
keyframeSlider.Initialize(this, actorID, keyframe.keyframeID);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddKeyFrame(int atTick)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public bool CanAddKeyFrameAtTick(int atTick)
|
||||
{
|
||||
foreach (Transform child in transform)
|
||||
{
|
||||
KeyframeSlider keyframeSlider = child.GetComponent<KeyframeSlider>();
|
||||
|
||||
if (keyframeSlider != null && Workspace.Instance.GetPawnKeyframe(keyframeSlider.actorID, keyframeSlider.keyframeID).atTick == atTick)
|
||||
{ return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/GUI/AnimationTimeline.cs.meta
Normal file
11
Assets/Scripts/GUI/AnimationTimeline.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b3dd6b099b5c67744b84a5ec7283277b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
124
Assets/Scripts/GUI/KeyframeSlider.cs
Normal file
124
Assets/Scripts/GUI/KeyframeSlider.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RimWorldAnimationStudio
|
||||
{
|
||||
public class KeyframeSlider : Slider
|
||||
{
|
||||
public AnimationTimeline timeline;
|
||||
//public AnimationClip clip;
|
||||
//public Keyframe keyframe;
|
||||
|
||||
public Transform ghostSliders;
|
||||
public Slider ghostSliderPrefab;
|
||||
public int maxGhosts = 4;
|
||||
|
||||
public int actorID;
|
||||
public int keyframeID;
|
||||
|
||||
public void Initialize(AnimationTimeline timeline, int actorID, int keyframeID)
|
||||
{
|
||||
this.timeline = timeline;
|
||||
//this.clip = clip;
|
||||
//this.keyframe = keyframe;
|
||||
|
||||
this.actorID = actorID;
|
||||
this.keyframeID = keyframeID;
|
||||
|
||||
PawnKeyframe keyframe = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID);
|
||||
Debug.Log(keyframe);
|
||||
|
||||
value = (float)keyframe.atTick / Workspace.Instance.GetCurrentStageLength();
|
||||
OnValueChanged();
|
||||
|
||||
onValueChanged.AddListener(delegate (float value) { OnValueChanged(); });
|
||||
}
|
||||
|
||||
public void OnValueChanged()
|
||||
{
|
||||
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; }
|
||||
}*/
|
||||
|
||||
keyframe.atTick = newTick;
|
||||
|
||||
Debug.Log("Value changed: " + newTick);
|
||||
//value = (float)keyframe.atTick / stageLength;
|
||||
|
||||
UpdateGhostFrames();
|
||||
|
||||
clip.BuildSimpleCurves();
|
||||
}
|
||||
|
||||
// Ghost sliders are non-interactable slider handle
|
||||
public void UpdateGhostFrames()
|
||||
{
|
||||
PawnKeyframe keyframe = Workspace.Instance.GetPawnKeyframe(actorID, keyframeID);
|
||||
PawnAnimationClip clip = Workspace.Instance.GetPawnAnimationClip(actorID);
|
||||
|
||||
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 (ghostSliders.childCount <= i)
|
||||
{ Instantiate(ghostSliderPrefab, ghostSliders); }
|
||||
|
||||
GameObject ghostSliderObject = ghostSliders.GetChild(i).gameObject;
|
||||
Debug.Log(ghostSliderObject);
|
||||
ghostSliderObject.SetActive(true);
|
||||
|
||||
Slider ghostSlider = ghostSliderObject.GetComponent<Slider>();
|
||||
Debug.Log(ghostSlider);
|
||||
ghostSlider.value = (float)((i + 1) * clip.duration + keyframe.atTick) / stageLength;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (i >= nGhosts)
|
||||
{ transform.GetChild(i).gameObject.SetActive(false); }
|
||||
}
|
||||
}
|
||||
|
||||
public int GetGhostFramesRequired()
|
||||
{
|
||||
PawnAnimationClip clip = Workspace.Instance.GetPawnAnimationClip(actorID);
|
||||
|
||||
if (Workspace.animationDef.animationStages[Workspace.stageID].isLooping == false)
|
||||
{ return 0; }
|
||||
|
||||
if (clip.duration <= 1)
|
||||
{ return 0; }
|
||||
|
||||
return Math.Min(Mathf.CeilToInt((float)Workspace.Instance.GetCurrentStageLength() / clip.duration), maxGhosts);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Scripts/GUI/KeyframeSlider.cs.meta
Normal file
11
Assets/Scripts/GUI/KeyframeSlider.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9c733f5218a2b4a449e3115b2bef26f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -7,6 +7,11 @@ namespace RimWorldAnimationStudio
|
|||
{
|
||||
public class SimpleCurve : IEnumerable<CurvePoint>, IEnumerable
|
||||
{
|
||||
public void Clear()
|
||||
{
|
||||
this.points.Clear();
|
||||
}
|
||||
|
||||
public int PointsCount
|
||||
{
|
||||
get
|
||||
|
|
|
@ -23,6 +23,24 @@ namespace RimWorldAnimationStudio
|
|||
private static int maxHistoryDepth = 100;
|
||||
private static int historyIndex = 0;
|
||||
|
||||
public int GetCurrentStageLength()
|
||||
{
|
||||
if (stageID < 0 || stageID >= animationDef.animationStages.Count)
|
||||
{ return 0; }
|
||||
|
||||
return animationDef.animationStages[stageID].playTimeTicks;
|
||||
}
|
||||
|
||||
public PawnAnimationClip GetPawnAnimationClip(int actorID)
|
||||
{
|
||||
return animationDef.animationStages[stageID].animationClips[actorID];
|
||||
}
|
||||
|
||||
public PawnKeyframe GetPawnKeyframe(int actorID, int keyframeID)
|
||||
{
|
||||
return animationDef.animationStages[stageID].animationClips[actorID].keyframes.FirstOrDefault(x => x.keyframeID == keyframeID);
|
||||
}
|
||||
|
||||
public void TrackChanges()
|
||||
{
|
||||
if (historyIndex < workspaceHistory.Count - 1)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue