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

214 lines
7.4 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;
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;
int stageLenght = Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks;
if (stageTick > stageLenght)
{
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();
}
else
{ stageTick = stageLenght; }
}
stageTimelineSlider.maxValue = stageLenght;
stageTimelineSlider.value = stageTick;
UpdateAnimation();
}
public void UpdateAnimation()
{
if (stageTickText != null)
{ stageTickText.text = stageTick.ToString(); }
if (stageLengthText != null)
{ stageLengthText.text = Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks.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;
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 == 2 ? 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();
for (int actorID = 0; actorID < Workspace.animationDef.actors.Count; actorID++)
{
GameObject actorBodyObject = Instantiate(actorBodyPrefab, transform);
actorBodies.Add(actorBodyObject.GetComponent<ActorBody>());
GameObject actorCardObject = Instantiate(actorCardPrefab, actorCards);
actorCardObject.GetComponent<ActorCard>().Initialize(Workspace.animationDef.actors[actorID]);
AnimationTimeline animationTimeline = Instantiate(animationTimelinePrefab, animationTimelines);
animationTimeline.Initialize(actorID);
}
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 void ToggleAnimation()
{
isAnimating = !isAnimating;
}
public void ToggleAnimationLoop()
{
isLooping = !isLooping;
}
public void UpdateFromStageTimelineSlider()
{
if (Workspace.animationDef == null)
{ return; }
int stageLenght = Workspace.animationDef.animationStages[Workspace.stageID].playTimeTicks;
if (stageLenght != (int)stageTimelineSlider.value)
{ stageTick = (int)stageTimelineSlider.value; }
}
}
}