using System; using UnityEngine; using System.Collections; using System.Collections.Generic; using SimpleJSON; namespace DillDoe { public class AnimatorP : MVRScript { #region PluginInfo public string pluginAuthor = "DillDoe"; // addition by Mr.Ripley / edited by VamTastic public string pluginName = "AnimatorP"; public string pluginVersion = "1.2b"; public string pluginDate = "01/24" + "/2019"; public string pluginDescription = @"This plugin controls animator(s) playback for custom unity asset. You are free to edit/change everything below this, please do not delete the PluginInfo. Append it if you modify the script 02/06 - updated for multiple animations, loop toggling, variable trigger speed, audio speed and bug fixes. 08/06 - beta update. Disable Vive controller due to changes in VaM 1.17.5 which changed steamvr inputs. added autofind animator when attached to asset with animation instead of selecting from drop down box. 12/30 - Fix loading error 8/12/22 - this can now handle more than one animator by Mr.Ripley "; #endregion #region Vars // vr controllers protected VrController LC; protected VrController RC; protected JSONStorableBool _swapTriggers; protected Animator goA; protected AudioSource goS; protected Dictionary AnimClip = new Dictionary(); protected Dictionary AClip = new Dictionary(); protected RuntimeAnimatorController ac; protected bool OC = true; protected bool bReleased = false; protected bool pLoop = false; protected UIDynamicPopup displayPopup; protected JSONStorableFloat ScrubA; protected JSONStorableStringChooser animT; protected JSONStorableStringChooser audioT; protected JSONStorableBool useTrigger; protected JSONStorableBool useTscrub; protected JSONStorableBool useTspeed; protected JSONStorableBool bLoop; protected JSONStorableBool rLoop; protected JSONStorableFloat triggerS; protected JSONStorableFloat speedLslider; protected UIDynamicSlider scrubslider; protected bool AtomTrue; #endregion public override void Init() { try { AtomTrue = false; #region find controllers // get controllers if (!SuperController.singleton.isOVR) { //disabled steamvr due to changes in vam GameObject Rctrl = GameObject.Find("Controller (right)"); if (Rctrl != null) { //RC = new SteamVRController(Rctrl); } GameObject Lctrl = GameObject.Find("Controller (left)"); if (Lctrl != null) { //LC = new SteamVRController(Lctrl); } } else { // Triggers will be made in 'Start' so that saved swapTriggers will have been loaded _swapTriggers = new JSONStorableBool("swap triggers", false, (bool aSwap) => { OVRInput.Axis1D axis; if (!aSwap) { axis = OVRInput.Axis1D.PrimaryIndexTrigger; } else { axis = OVRInput.Axis1D.PrimaryHandTrigger; } LC = new OculusController(OVRInput.Controller.LTouch, axis); RC = new OculusController(OVRInput.Controller.RTouch, axis); }); RegisterBool(_swapTriggers); CreateToggle(_swapTriggers); } #endregion #region Left Side UI UIDynamic spacer = CreateSpacer(false); spacer.height = 100f; //************** Scrub ScrubA = new JSONStorableFloat("Scrub Animation", 0f, Scruby, 0f, 1f, true); RegisterFloat(ScrubA); scrubslider = CreateSlider(ScrubA); //************** play button & register play action var pbutton = CreateButton("Play"); if (pbutton != null) { pbutton.button.onClick.AddListener(pPlay); } JSONStorableAction playP = new JSONStorableAction("Play", pPlay); RegisterAction(playP); //************** Speed speedLslider = new JSONStorableFloat("Animation Speed", 1f, 0.01f, 2f, false, true); RegisterFloat(speedLslider); CreateSlider(speedLslider); //************** Toggle Play/Reverse JSONStorableBool toggleP = new JSONStorableBool("Play/Reverse Toggle", false, OCVtoggle); RegisterBool(toggleP); CreateToggle(toggleP); //OC = true; JSONStorableAction OCV = new JSONStorableAction("Toggle Play/Reverse", OCVtoggle2); RegisterAction(OCV); //************** Looping bLoop = new JSONStorableBool("Looping", false, LoopO); RegisterBool(bLoop); CreateToggle(bLoop); //************** Looping rLoop = new JSONStorableBool("Looping - Reverse", false, LoopR); RegisterBool(rLoop); CreateToggle(rLoop); JSONStorableString jstring = new JSONStorableString("info", "\n[ Animated Assets ] Choose CustomUnityAsset(s) with animator(s) \n" + "[ Scrub Animation ] Play aniamtion frame by frame based on silder \n" + "[ Play ] Play Animation \n" + "[ Animation Speed ] Animation Playback Speed \n" + "[ Play/Reverse Toggle ] Check to Play, Uncheck to Play Backwards \n" + "[ Looping ] Check to Loop Animation \n" + "[ Looping - Reverse ] Check to Loop Backwards\n" + "Check both to toggle back & forth"); UIDynamicTextField dtext = CreateTextField(jstring); dtext.backgroundColor = new Color(0, 0, 0, 0); dtext.height = 520f; #endregion #region Right Side UI //************** Animator choices animT = new JSONStorableStringChooser("animT", null, null, "Animators", AnimChooser); RegisterStringChooser(animT); UIDynamicPopup aA = CreateScrollablePopup(animT, true); aA.popupPanelHeight = 400f; aA.popup.onOpenPopupHandlers += AnimFinder; //************** Audio choices audioT = new JSONStorableStringChooser("audioT", null, null, "Audio", AudioChooser); RegisterStringChooser(audioT); UIDynamicPopup aAudio = CreateScrollablePopup(audioT, true); aAudio.popupPanelHeight = 400f; //aAudio.popup.onOpenPopupHandlers += AnimFinder; spacer = CreateSpacer(true); spacer.height = 6f; //************** reverse play button var rbutton = CreateButton("Reverse", true); if (rbutton != null) { rbutton.button.onClick.AddListener(rPlay); } JSONStorableAction playR = new JSONStorableAction("Reverse", rPlay); RegisterAction(playR); //************** Use Trigger useTrigger = new JSONStorableBool("Play with Trigger", false, PlayReset); RegisterBool(useTrigger); CreateToggle(useTrigger, true); //************** Use Trigger Scrub useTscrub = new JSONStorableBool("Scrub with Trigger", false, TriggerScrub); RegisterBool(useTscrub); CreateToggle(useTscrub, true); useTspeed = new JSONStorableBool("Variable Speed", false, TriggerSpeed); RegisterBool(useTspeed); CreateToggle(useTspeed, true); //************** Trigger Sensitivity triggerS = new JSONStorableFloat("Trigger Sensitivity", 0.99f, 0.1f, 0.99f); RegisterFloat(triggerS); CreateSlider(triggerS, true); #endregion JSONStorableString jstring2 = new JSONStorableString("info", "\n[ Animators ] Asset's Animator(s) (Automatically found) \n" + "[ Audio ] Playback Asset's Audio w/ speed \n (None for regular audio speed) \n" + "[ Reverse ] Play Animation Backwards\n" + "[ Play with Trigger ] Check to play with Vive/Oculus Trigger (single controller only, Left or Right) \n" + "[ Scrub with Trigger ] Check to scrub animation based on Trigger's Position \n" + "[ Variable Speed ] Animation speed is based on trigger postion \n" + "[ Trigger Sensitivity ] How far trigger is pulled before Playing."); UIDynamicTextField dtext2 = CreateTextField(jstring2, true); dtext2.backgroundColor = new Color(0, 0, 0, 0); dtext2.height = 520f; } catch (Exception e) { SuperController.LogError("Init Exception caught: " + e); } } void Start() { // Cause the controllers to be created with saved swapTrigger value if (_swapTriggers != null) { _swapTriggers.val = !_swapTriggers.val; _swapTriggers.val = !_swapTriggers.val; } } // Update is called with each rendered frame by Unity void Update() { try { if ((RC != null || LC != null) && goA != null && useTrigger.val == true) { float axis = 0f; if (RC != null) { axis = RC.GetTrigger(); } else if (LC != null) { axis = LC.GetTrigger(); } // Check to see that trigger is press if (axis > 0.01f && (useTscrub.val || useTspeed.val)) { // check if scrubbing is enabled. if (useTscrub.val) { ScrubA.val = axis; } // check if variable speed is enabled else if (useTspeed.val == true) { bReleased = true; goA.enabled = true; goA.speed = Mathf.Abs(axis * speedLslider.val); goA.SetFloat("direction", 1.0f); goA.Play(pluginLabelJSON.val); if (goS != null) { goS.pitch = Mathf.Abs(axis * speedLslider.val); goS.mute = false; } } } else if (axis > triggerS.val && useTscrub.val == false && useTspeed.val == false) { if (goA.enabled == false) { pPlay(); } } // triggered released, stop animation & sound if (axis < 0.1f && bReleased) { bReleased = false; PlayReset(true); } } // Looping if (bLoop.val == true && goA != null && pLoop == false) { if (goA.enabled == false) { pPlay(); if (rLoop.val == true) { pLoop = true; } } } if (rLoop.val == true && goA != null && pLoop == true) { if (goA.enabled == false) { rPlay(); if (bLoop.val == true) { pLoop = false; } } } // There seems to be a slight delay loading custom assets, so check to see if it's loaded before applying script. if (containingAtom.GetComponentsInChildren()[0]!= null && !AtomTrue) { if (containingAtom.GetComponentsInChildren(typeof(Renderer), true).Length > 0 ) { SuperController.LogMessage("Animator Found!"); AtomTrue = true; AssetChooser(); } } } catch (Exception e) { // only give error if custom asset are loaded. Prevent Error on Loading messages. if (AtomTrue && goA != null) { SuperController.LogError("Update Exception caught: " + e); AtomTrue=false; } } } private void OnDestroy() { Destroy(this.gameObject); } // protected Dictionary AnimClip = new Dictionary(); #region Asset Choice protected void AssetChooser() { goA = containingAtom.GetComponentsInChildren()[0]; if (goA != null) { // find animator Animator[] animators = containingAtom.GetComponentsInChildren(); if(animators != null) { List aList = new List(); foreach (Animator a in animators) { // get length of animation if(a.runtimeAnimatorController != null){ ac = a.runtimeAnimatorController; // find the animator name from prefab and add to list foreach (AnimationClip state in ac.animationClips) { aList.Add(state.name); AnimClip[state.name] = a; } aList.Add(ac.name); AnimClip[ac.name] = a; } } animT.choices = aList; goA.enabled = false; // prime it for playback*/ } // find audio AudioSource[] aClip = containingAtom.GetComponentsInChildren(); if (aClip != null) { List cList = new List(); cList.Add("None"); foreach (AudioSource c in aClip) { cList.Add(c.name); AClip[c.name] = c; } cList.Add("MUTE"); audioT.choices = cList; } } } #endregion #region Animation Options protected void AnimFinder() { AssetChooser(); } // protected void AnimChooser(string s) // { // add choosen animator to label // pluginLabelJSON.val = s; // } protected void AnimChooser(string s) { // add choosen animator to label pluginLabelJSON.val = s; if (goA != null) { goA = AnimClip[s]; } } protected void AudioChooser(string a) { if (goA != null) { if (a == "None") { goS = null; } else if (a == "MUTE") { goS.enabled = false; } else { goS = AClip[a]; } } } protected void OCVtoggle(bool e) { if (goA != null) { bLoop.val = false; if (e == true) { pPlay(); } else { rPlay(); } } } protected void Scruby(JSONStorableFloat lh) { if (goA != null) { goA.enabled = true; goA.speed = 1f; goA.SetFloat("direction", 1f); goA.Play(pluginLabelJSON.val, 0, lh.val); goA.speed = 0; if (goS != null) goS.enabled = false; // disable audio Wait4Me(0.01f); // reset animator } } protected void OCVtoggle2() { if (goA != null) { if (OC == true) { pPlay(); OC = false; } else { rPlay(); OC = true; } } } protected void LoopO(bool e) { pLoop = !e; } protected void LoopR(bool e) { pLoop = e; } protected void AudioMute(bool e) { if (goS != null) { if (audioT.val != "MUTE") { goS.enabled = true; } goS.pitch = Mathf.Abs(speedLslider.val); goS.mute = e; // false to unmute } // Audio = None, reset speed and toggle mute if (goS == null & AClip != null) { foreach (KeyValuePair c in AClip) { c.Value.pitch = 1f; c.Value.mute = e; } } } protected void pPlay() { if (goA != null) { goA.enabled = true; goA.speed = Mathf.Abs(speedLslider.val); goA.SetFloat("direction", 1.0f); goA.Play(pluginLabelJSON.val, 0, 0f); AudioMute(false); // pause till end of animation StartCoroutine(Wait4Me(ac.animationClips[0].length / goA.speed)); } } protected void rPlay() { if (goA != null) { goA.enabled = true; goA.speed = Mathf.Abs(speedLslider.val); goA.SetFloat("direction", -1f); // reverse direction goA.Play(pluginLabelJSON.val, 0, 1f); // start from end and play to start AudioMute(false); // pause till end of animation StartCoroutine(Wait4Me(ac.animationClips[0].length / goA.speed)); } } protected void TriggerScrub(bool e) { if (useTspeed.val && e) { useTspeed.val = false; } // turn off trigger speed PlayReset(e); } protected void TriggerSpeed(bool e) { if (useTscrub.val && e) { useTscrub.val = false; } // turn off scrub PlayReset(e); } protected void PlayReset(bool e) { if (goA != null) { goA.enabled = false; AudioMute(true); } } // wait s seconds then disable animator to stop animation reset protected IEnumerator Wait4Me(float s) { yield return new WaitForSeconds(s); //goA.enabled = false; PlayReset(true); } #endregion #region Controller Abstractions protected interface VrController { float GetTrigger(); } class OculusController : VrController { public OculusController(OVRInput.Controller aController, OVRInput.Axis1D aAxis = OVRInput.Axis1D.PrimaryHandTrigger) { _controller = aController; _axis = aAxis; } public float GetTrigger() { return OVRInput.Get(_axis, _controller); } OVRInput.Controller _controller; OVRInput.Axis1D _axis; } /*class SteamVRController : VrController { public SteamVRController(GameObject aCtrl) { //SteamVR_TrackedObject trackedObj = aCtrl.GetComponent(); _controller = SteamVR_Controller.Input((int)trackedObj.index); } public float GetTrigger() { return _controller.GetAxis(Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger).x; } SteamVR_Controller.Device _controller; }*/ #endregion } }