/** * Copyright 2015 IBM Corp. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ // Uncomment to enable debugging of the Runnable class. //#define ENABLE_RUNNABLE_DEBUGGING namespace Mapbox.Unity.Utilities { using UnityEngine; using System.Collections; using System.Collections.Generic; /// /// Helper class for running co-routines without having to inherit from MonoBehavior. /// public class Runnable : MonoBehaviour { #region Public Properties /// /// Returns the Runnable instance. /// public static Runnable Instance { get { return Singleton.Instance; } } #endregion #region Public Interface /// /// Start a co-routine function. /// /// The IEnumerator returns by the co-routine function the user is invoking. /// Returns a ID that can be passed into Stop() to halt the co-routine. public static int Run(IEnumerator routine) { Routine r = new Routine(routine); return r.ID; } /// /// Stops a active co-routine. /// /// THe ID of the co-routine to stop. public static void Stop(int ID) { Routine r = null; if (Instance.m_Routines.TryGetValue(ID, out r)) r.Stop = true; } /// /// Check if a routine is still running. /// /// The ID returned by Run(). /// Returns true if the routine is still active. static public bool IsRunning(int id) { return Instance.m_Routines.ContainsKey(id); } #if UNITY_EDITOR private static bool sm_EditorRunnable = false; /// /// This function enables the Runnable in edit mode. /// public static void EnableRunnableInEditor() { if (!sm_EditorRunnable) { sm_EditorRunnable = true; UnityEditor.EditorApplication.update += UpdateRunnable; } } static void UpdateRunnable() { if (!Application.isPlaying) { Instance.UpdateRoutines(); } } #endif #endregion #region Private Types /// /// This class handles a running co-routine. /// private class Routine : IEnumerator { #region Public Properties public int ID { get; private set; } public bool Stop { get; set; } #endregion #region Private Data private bool m_bMoveNext = false; private IEnumerator m_Enumerator = null; #endregion public Routine(IEnumerator a_enumerator) { m_Enumerator = a_enumerator; Runnable.Instance.StartCoroutine(this); Stop = false; ID = Runnable.Instance.m_NextRoutineId++; Runnable.Instance.m_Routines[ID] = this; #if ENABLE_RUNNABLE_DEBUGGING Debug.Log( string.Format("Coroutine {0} started.", ID ) ); #endif } #region IEnumerator Interface public object Current { get { return m_Enumerator.Current; } } public bool MoveNext() { m_bMoveNext = m_Enumerator.MoveNext(); if (m_bMoveNext && Stop) m_bMoveNext = false; if (!m_bMoveNext) { Runnable.Instance.m_Routines.Remove(ID); // remove from the mapping #if ENABLE_RUNNABLE_DEBUGGING Debug.Log( string.Format("Coroutine {0} stopped.", ID ) ); #endif } return m_bMoveNext; } public void Reset() { m_Enumerator.Reset(); } #endregion } #endregion #region Private Data private Dictionary m_Routines = new Dictionary(); private int m_NextRoutineId = 1; #endregion /// /// THis can be called by the user to force all co-routines to get a time slice, this is usually /// invoked from an EditorApplication.Update callback so we can use runnable in Editor mode. /// public void UpdateRoutines() { if (m_Routines.Count > 0) { // we are not in play mode, so we must manually update our co-routines ourselves List routines = new List(); foreach (var kp in m_Routines) routines.Add(kp.Value); foreach (var r in routines) r.MoveNext(); } } } }