rimworld-animation-studio/Assets/Scripts/AnimationController.cs

300 lines
11 KiB
C#

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml.Serialization;
using UnityEngine;
using SFB;
using UnityEngine.UI;
namespace RimWorldAnimationStudio
{
public class AnimationController : Singleton<AnimationController>
{
public bool isAnimating = false;
public bool isLooping = true;
public int stageTick = 0;
public bool autoAdvanceToNextStage = false;
public Slider stageTimelineSlider;
public GameObject actorBodyPrefab;
public List<ActorBody> actorBodies = new List<ActorBody>();
public Text stageTickText;
public Text stageLengthText;
public Transform actorCards;
public GameObject actorCardPrefab;
public Dropdown stageLoopDropdown;
public InputField stageIDField;
public Transform animationTimelines;
public AnimationTimeline animationTimelinePrefab;
private float currentTime = 0;
private int cycleIndex = 0;
public InputField cyclesNormalField;
public InputField cyclesFastField;
public void Update()
{
// No animation, exit
if (Workspace.animationDef == null) { return; }
// Dirty animation, reset
if (Workspace.animationDef != null && Workspace.isDirty)
{ Initialize(); return; }
// Not animating, update preview
if (isAnimating == false) { UpdateAnimation(); return; }
// Animating, update stage tick before updating preview
currentTime += Time.deltaTime;
if (currentTime < 1f/60f)
{ return; }
currentTime -= 1f/60f;
stageTick += 1;
if (stageTick > Workspace.animationClipWindowSize)
{
if (stageLoopDropdown.value == 1)
{ stageTick = 1; }
else if (stageLoopDropdown.value == 2 && Workspace.stageID < Workspace.animationDef.animationStages.Count - 1)
{
++cycleIndex;
stageTick = 1;
if (cycleIndex > int.Parse(cyclesNormalField.text))
{
++Workspace.stageID;
stageIDField.text = Workspace.stageID.ToString();
cycleIndex = 0;
}
}
else
{ stageTick = Workspace.animationClipWindowSize; }
}
stageTimelineSlider.maxValue = Workspace.animationClipWindowSize;
stageTimelineSlider.value = stageTick;
UpdateAnimation();
}
public void UpdateAnimation()
{
if (stageTickText != null)
{ stageTickText.text = stageTick.ToString(); }
if (stageLengthText != null)
{ 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;
PawnAnimationClip clip = Workspace.animationDef.animationStages[Workspace.stageID].animationClips[actorID];
float clipPercent = (float)(stageTick % clip.duration) / clip.duration;
Vector3 deltaPos = new Vector3(clip.BodyOffsetX.Evaluate(clipPercent), 0, clip.BodyOffsetZ.Evaluate(clipPercent));
deltaPos += Workspace.animationDef.actors[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);
int headFacing = (int)clip.HeadFacing.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);
actorBody.transform.position = bodyPos;
actorBody.transform.eulerAngles = new Vector3(0, 0, bodyAngle);
actorBody.headRenderer.transform.localPosition = headPos;
actorBody.headRenderer.transform.eulerAngles = new Vector3(0, 0, headAngle);
actorBody.bodyRenderer.sprite = Resources.Load<Sprite>("Textures/Humanlike/Bodies/" + bodyType + bodyFacing);
actorBody.headRenderer.sprite = Resources.Load<Sprite>("Textures/Humanlike/Heads/Head" + headFacing);
actorBody.bodyRenderer.sortingLayerName = clip.layer;
actorBody.headRenderer.sortingLayerName = clip.layer;
actorBody.headRenderer.sortingOrder = headFacing == 0 ? -1 : 1;
}
}
public void UpdateStageID()
{
if (Workspace.animationDef == null)
{
stageIDField.text = "0";
return;
}
int.TryParse(stageIDField.text, out int i);
Workspace.stageID = Mathf.Clamp(i, 0, Workspace.animationDef.animationStages.Count - 1);
stageTick = 0;
stageIDField.text = Workspace.stageID.ToString();
}
public void Initialize()
{
Debug.Log("Initializing animation preview");
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]);
AnimationTimeline animationTimeline = Instantiate(animationTimelinePrefab, animationTimelines);
animationTimeline.Initialize(actorID);
}
StageCardManager.Instance.Initialize();
Workspace.isDirty = false;
UpdateAnimation();
}
public void Reset()
{
Workspace.stageID = 0;
stageTick = 0;
isAnimating = false;
foreach (ActorBody actorBody in actorBodies)
{ Destroy(actorBody.gameObject); }
foreach (Transform actorCard in actorCards)
{ Destroy(actorCard.gameObject); }
foreach (Transform animationTimeline in animationTimelines)
{ Destroy(animationTimeline.gameObject); }
actorBodies.Clear();
}
public bool AddAnimationStage()
{
return true;
}
public bool CloneAnimationStage()
{
AnimationStage stage = Workspace.animationDef.animationStages[Workspace.stageID].Copy();
stage.Initialize();
stage.stageName += " (Clone)";
Workspace.animationDef.animationStages.Insert(Workspace.stageID + 1, stage);
return true;
}
public bool MoveAnimationStage(int startIndex, int delta)
{
if (startIndex + delta < 0 || startIndex + delta >= Workspace.animationDef.animationStages.Count)
{ return false; }
AnimationStage stage = Workspace.animationDef.animationStages[startIndex];
Workspace.animationDef.animationStages[startIndex] = Workspace.animationDef.animationStages[startIndex + delta];
Workspace.animationDef.animationStages[startIndex + delta] = stage;
return true;
}
public bool RemoveAnimationStage()
{
if (Workspace.animationDef.animationStages.Count == 1)
{
Debug.LogWarning("Cannot delete animation stage - the animation must contain at least one animation stage.");
return false;
}
Workspace.animationDef.animationStages.RemoveAt(Workspace.stageID);
Workspace.stageID = Workspace.stageID >= Workspace.animationDef.animationStages.Count ? Workspace.stageID = Workspace.animationDef.animationStages.Count - 1 : Workspace.stageID;
return true;
}
public bool AddActor()
{
return true;
}
public bool RemoveActor()
{
if (Workspace.animationDef.actors.Count == 1)
{
Debug.LogWarning("Cannot delete actor - the animation must contain at least one actor.");
return false;
}
foreach (AnimationStage stage in Workspace.animationDef.animationStages)
{ stage.animationClips.RemoveAt(Workspace.actorID); }
Workspace.animationDef.actors.RemoveAt(Workspace.actorID);
Workspace.actorID = Workspace.actorID >= Workspace.animationDef.actors.Count ? Workspace.actorID = Workspace.animationDef.actors.Count - 1 : Workspace.actorID;
return true;
}
public void ToggleAnimation()
{
isAnimating = !isAnimating;
}
public void ToggleAnimationLoop()
{
isLooping = !isLooping;
}
public void UpdateFromStageTimelineSlider()
{
if (Workspace.animationDef == null)
{ return; }
if (stageTick != (int)stageTimelineSlider.value)
{ stageTick = (int)stageTimelineSlider.value; }
}
public void ToggleActorManipulationMode(int mode)
{
Workspace.actorManipulationMode = (ActorManipulationMode)mode;
}
}
}