mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
231 lines
7.5 KiB
C#
231 lines
7.5 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 : 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;
|
|
|
|
public Transform actorCards;
|
|
public GameObject actorCardPrefab;
|
|
|
|
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);
|
|
|
|
foreach (Actor actor in anim.actors)
|
|
{
|
|
AddActor();
|
|
}
|
|
|
|
UpdateAnimation();
|
|
}
|
|
}
|
|
|
|
public void SaveXML()
|
|
{
|
|
if (anim == null)
|
|
{ return; }
|
|
|
|
string path = StandaloneFileBrowser.SaveFilePanel("Save file", "", anim.defName + ".xml", ".xml");
|
|
|
|
if (path == null || path == "")
|
|
{ return; }
|
|
|
|
XmlSerializer writer = new XmlSerializer(typeof(AnimationDef));
|
|
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
|
|
ns.Add("", "");
|
|
FileStream file = File.Create(path);
|
|
|
|
writer.Serialize(file, anim, ns);
|
|
file.Close();
|
|
}
|
|
|
|
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) { return; }
|
|
if (isAnimating == false) { UpdateAnimation(); 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 AddActor()
|
|
{
|
|
GameObject actorCardObject = Instantiate(actorCardPrefab, actorCards);
|
|
actorCardObject.GetComponent<ActorCard>().Initialize(anim, actorCards.childCount - 1);
|
|
}
|
|
|
|
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 = actorCards.transform.GetChild(actorID).GetComponent<ActorCard>().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;
|
|
}
|
|
}
|
|
}
|
|
}
|