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

197 lines
6.4 KiB
C#

using System;
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 : MonoBehaviour
{
public Defs defs;
public AnimationDef anim;
public bool isAnimating = false;
public bool isLooping = true;
public int stageTick = 0;
public int stageID = 0;
public bool autoAdvanceToNextStage = false;
//public InputField stageTickField;
//public InputField stageLengthField;
//public InputField stageIDField;
public ActorBody actorBodyPrefab;
public List<ActorBody> actorBodies = new List<ActorBody>();
public List<Dropdown> bodyTypeDropdowns = new List<Dropdown>();
public Text stageTickField;
public Text stageLengthField;
private float currentTime = 0;
public T ReadDataFromXML<T>(string inputPath)
{
using (StreamReader stringReader = new StreamReader(inputPath))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
}
public void LoadNewXML(string inputPath)
{
defs = ReadDataFromXML<Defs>(inputPath);
if (defs?.animationDefs != null)
{
Debug.Log(defs.animationDefs.Count);
Debug.Log(defs.animationDefs[0].defName);
anim = defs.animationDefs[0];
InitializeAnimation(anim);
UpdateAnimation();
}
}
public void OpenFileDialog()
{
var paths = StandaloneFileBrowser.OpenFilePanel("Open File", "", "", false);
Debug.Log(paths[0]);
LoadNewXML(paths[0]);
}
public void PlayAnimation(bool value)
{
isAnimating = value;
}
public void ToggleAnimationLoop()
{
isLooping = !isLooping;
}
public void UpdateStageID(InputField stageIDField)
{
if (anim == null)
{
stageIDField.text = "0";
return;
}
int i = int.Parse(stageIDField.text);
stageID = Mathf.Clamp(i, 0, anim.animationStages.Count - 1);
stageTick = 0;
stageIDField.text = stageID.ToString();
}
public void Update()
{
if (anim == null || isAnimating == false) return;
currentTime += Time.deltaTime;
if (currentTime < 1f/60f)
{ return; }
currentTime -= 1f/60f;
stageTick += 1;
if (stageTick > anim.animationStages[stageID].playTimeTicks)
{
if (isLooping)
{ stageTick = 1; }
else if (autoAdvanceToNextStage && stageID < anim.animationStages.Count - 1)
{ stageTick = 1; stageID++; }
else
{ stageTick = anim.animationStages[stageID].playTimeTicks; }
}
UpdateAnimation();
}
public void InitializeAnimation(AnimationDef anim)
{
ResetAnimation();
anim.Initialize();
this.anim = anim;
for (int actorID = 0; actorID < anim.actors.Count; actorID++)
{
ActorBody actorBody = Instantiate(actorBodyPrefab, transform) as ActorBody;
actorBodies.Add(actorBody);
}
}
public void ResetAnimation()
{
anim = null;
stageID = 0;
stageTick = 0;
isAnimating = false;
actorBodies.Clear();
foreach (Transform child in transform)
{ Destroy(child.gameObject); }
}
public void UpdateAnimation()
{
stageTickField.text = stageTick.ToString();
stageLengthField.text = anim.animationStages[stageID].playTimeTicks.ToString();
for (int actorID = 0; actorID < actorBodies.Count; actorID++)
{
ActorBody actorBody = actorBodies[actorID];
string bodyType = bodyTypeDropdowns[actorID].options[bodyTypeDropdowns[actorID].value].text;
PawnAnimationClip clip = anim.animationStages[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 += anim.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.ToString();
actorBody.headRenderer.sortingLayerName = clip.layer.ToString();
actorBody.headRenderer.sortingOrder = headFacing == 2 ? 1 : -1;
}
}
}
}