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 actorBodies = new List(); //public List bodyTypeDropdowns = new List(); public Text stageTickField; public Text stageLengthField; public Transform actorCards; public GameObject actorCardPrefab; private float currentTime = 0; public T ReadDataFromXML(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(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().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().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("Textures/Humanlike/Bodies/" + bodyType + bodyFacing); actorBody.headRenderer.sprite = Resources.Load("Textures/Humanlike/Heads/Head" + headFacing); actorBody.bodyRenderer.sortingLayerName = clip.layer.ToString(); actorBody.headRenderer.sortingLayerName = clip.layer.ToString(); actorBody.headRenderer.sortingOrder = headFacing == 2 ? 1 : -1; } } } }