GenericPlayerManager.cs 10.6 KB
//Log Dir         http://docs.unity3d.com/Manual/LogFiles.html
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using UnityEngine.UI;

public abstract class GenericPlayerManager : MonoBehaviour {

	private const string DEFAULT_ANIMATION = "_default";
	private const string NONE_ANIMATION = "_defaultWORD";

	private const float FADE_LENGTH = 0.6F;

	private const float WORD_DEFAULT_SPEED = 1.1F;
	private const float WORD_MAX_SPEED = 2F;

	// Velocidade da primeira letra de uma palavra
	private const float LETTER_FIRST_SPEED = 2.6F;
	// Velocidade das outras letras
	private const float LETTER_DEFAULT_SPEED = 3.1F;
	// Velocidade máxima de uma letra que o slider pode configurar
	private const float LETTER_MAX_SPEED = 4.6F;
	// Taxa de velocidade da primeira letra de uma palavra equivalente a velocidade de uma palavra
	private const float LETTER_FIRST_UNIT_SPEED = (LETTER_MAX_SPEED - LETTER_FIRST_SPEED) / (WORD_MAX_SPEED - WORD_DEFAULT_SPEED);
	// Taxa de velocidade das outras letras equivalente a velocidade de uma palavra
	private const float LETTER_UNIT_SPEED = (LETTER_MAX_SPEED - LETTER_DEFAULT_SPEED) / (WORD_MAX_SPEED - WORD_DEFAULT_SPEED);

	// Velocidade de reprodução de números
	private const float NUMBER_DEFAULT_SPEED = 1.5F;
	// Velocidade máxima de um número que o slider pode configurar
	private const float NUMBER_MAX_SPEED = 2.9F;
	// Taxa de velocidade equivalente a uma unidade de velocidade de uma palavra
	private const float NUMBER_UNIT_SPEED = (NUMBER_MAX_SPEED - NUMBER_DEFAULT_SPEED) / (WORD_MAX_SPEED - WORD_DEFAULT_SPEED);

	private static float hSliderValue = WORD_DEFAULT_SPEED;
	private float letterSpeed = LETTER_DEFAULT_SPEED;
	private float numberSpeed = NUMBER_DEFAULT_SPEED;

	protected string glosa = "";
	private static String[] stringPos = { DEFAULT_ANIMATION };//vetor que sera usado para quebrar a glosa

	private AnimationClip aniClip;
	private GameObject AVATAR;
	private Animation COMPONENT_ANIMATION;
	public Text SUBTITLES;

	// Guarda os nomes das palavras ja carregadas.
	private HashSet<string> loadedAssetBundles = new HashSet<string>();
	// Guarda os nomes das palavras que nao tem assetbundle.
	private HashSet<string> nonexistentAssetBundles = new HashSet<string>();

	// Lista de animações sendo reproduzidas.
	// Utilizada para apresentar a legenda.
	private volatile Queue<KeyValuePair<string, string>> animQueue = new Queue<KeyValuePair<string, string>>();

	// Listas de AnimationState de animações sendo reproduzidas.
	// Utilizadas para alterar a velocidade da animação quando há evento no slider.
	private volatile List<AnimationState> wordStateList = new List<AnimationState>();
	private volatile List<AnimationState> letterStateList = new List<AnimationState>();
	private volatile List<AnimationState> numberStateList = new List<AnimationState>();

	// "play" flag. Indica se está carregando a glosa.
	private volatile bool loading = false;

	public virtual void Start()
	{
																															//Caching.CleanCache();
		AVATAR = GameObject.FindGameObjectWithTag("avatar");//referencia para o avatar
		COMPONENT_ANIMATION = AVATAR.GetComponent<Animation>();//referencia para o componente animador do avatar
		Application.ExternalCall("onLoadPlayer");//var onLoadPlayer = function(){}
	}

	protected virtual void setSubtitle(string text)
	{
		SUBTITLES.text = text;
	}

	// Define a velocidade das animacões com base no slider da GUI
	public void setSlider(float x)
	{
		hSliderValue = x;

		letterSpeed = getProporcionalSpeed(LETTER_DEFAULT_SPEED, LETTER_UNIT_SPEED);
		numberSpeed = getProporcionalSpeed(NUMBER_DEFAULT_SPEED, NUMBER_UNIT_SPEED);

		foreach (AnimationState state in wordStateList)
			if (state != null)
				state.speed = hSliderValue;

		foreach (AnimationState state in letterStateList)
			if (state != null)
				state.speed = letterSpeed;

		foreach (AnimationState state in numberStateList)
			if (state != null)
				state.speed = numberSpeed;
	}

	private float getProporcionalSpeed(float speed, float unit)
	{
		return speed + (hSliderValue - WORD_DEFAULT_SPEED) * unit;
	}


	public void stop_animations()
	{
		StopCoroutine("loadAndPlay");
		loading = false;

		stopAnimations();
	}

	public void stopAnimations()
	{
		try {
			StopCoroutine("handleStates");
		} catch (NullReferenceException nre) { Debug.Log("StopCoroutine handlestates nullreff::"+nre.ToString()); }

			setSubtitle("");

		try {
			animQueue.Clear();
			wordStateList.Clear();
			letterStateList.Clear();
			numberStateList.Clear();
		} catch (NullReferenceException nre) { Debug.Log("SetQueueList null reff::"+nre.ToString()); }

		COMPONENT_ANIMATION.Stop();
		COMPONENT_ANIMATION.CrossFade(DEFAULT_ANIMATION, FADE_LENGTH, PlayMode.StopAll);

	}

	/*
	 *	Manda reproduzir animação e adiciona a file de animações a serem reproduzidas.
	 *
	 *	Caso não haja SUBTITLE, name será utilizado como SUBTITLE.
	 *	Caso não haja fadeLength, será atribuido FADE_LENGTH.
	 *	Caso não haja velocidade, hSliderValue será atribuída.
	 */
	private AnimationState playAnimation(string name, string subtitle, float speed)
	{

		try
		{
			AnimationState state = COMPONENT_ANIMATION.CrossFadeQueued(name, FADE_LENGTH, QueueMode.CompleteOthers);
			state.speed = speed;
			animQueue.Enqueue(new KeyValuePair<string, string>(subtitle, name));

			//Debug.Log("Loaded: " + name + " (" + state.speed + ")");
			return state;
		}
		catch (NullReferenceException nre)
		{
			Debug.Log("'" + name + "' não foi encontrado!\n" + nre.ToString());
		}

		return null;
	}
	private AnimationState playAnimation(string name, string subtitle) {
		return playAnimation(name, subtitle, hSliderValue);
	}
	private AnimationState playAnimation(string name) {
		return playAnimation(name, name);
	}


	/*
	 *	Destaca caractere de uma string.
	 */
	private string highlight(string word, int index)
	{
		string subtitle;

		if (index > 0)
			subtitle = word.Substring(0, index) + "<b><color=white>-";
		else
			subtitle = "<b><color=white>";

		subtitle += word[index];

		if (index < word.Length - 1)
			subtitle += "-</color></b>" + word.Substring(index + 1, word.Length - index - 1);
		else
			subtitle += "</color></b>" + word.Substring(index + 1, word.Length - index - 1);

		return subtitle;
	}


	/**
	 * 	Returns the asset bundle named aniName.
	 *
	 *	@return AssetBundle - se for encontrado.
	 *			null - se ocorrer num erro.
	 */
	protected abstract WWW loadAssetBundle(string aniName);


	public void play()
	{
		if (loading) stop_animations();

		StartCoroutine("loadAndPlay");
	}


	IEnumerator loadAndPlay()
	{
		loading = true;
		try{
			stopAnimations();
		} catch (NullReferenceException nre) { nre.ToString(); }

		string lastAnimationSubtitle = "";
		bool spelled = false;

		// Default
		playAnimation(DEFAULT_ANIMATION, "", 2F);
		StartCoroutine("handleStates");

		stringPos = glosa.Split(' ');

		foreach (string aniName in stringPos)
		{
			try {
				if (String.IsNullOrEmpty(aniName)) continue;
			} catch (Exception e) {
				Debug.Log(e + " :: NotNullNotEmpty");
			}

			bool nonexistent = nonexistentAssetBundles.Contains(aniName);
			bool loaded = loadedAssetBundles.Contains(aniName);

			if ( ! nonexistent && ! loaded)
			{
				// Função loadAssetBundle é definida pela classe filha
				WWW www = loadAssetBundle(aniName);

				if (www != null)
				{
					yield return www;

					AssetBundle bundle = null;

					if (www.error == null)
						bundle = www.assetBundle;

					if (bundle != null && ! String.IsNullOrEmpty(bundle.mainAsset.name))
					{
						aniClip = bundle.mainAsset as AnimationClip;
						bundle.Unload(false);

						if (aniClip)
						{
							COMPONENT_ANIMATION.AddClip(aniClip, aniName);

							loadedAssetBundles.Add(aniName);
							loaded = true;
						}
						else Debug.Log ("Sinal \"" + aniName + "\" não carregado corretamente.");
					}
				}
			}

			// Reproduz palavra
			if (loaded)
			{
				if (spelled)
				{
					// Default
					playAnimation(DEFAULT_ANIMATION, lastAnimationSubtitle);
					spelled = false;
				}

				wordStateList.Add(playAnimation(aniName));
				lastAnimationSubtitle = aniName;
			}
			// Soletra palavra
			else
			{
				// Se a animação não foi carregada e nem está marcada como não existente,
				// adiciona ao set de animações não existentes
				if ( ! nonexistent)
					nonexistentAssetBundles.Add(aniName);

				// Se já houve o soletramento de alguma palavra, reproduz animação default
				if (spelled)
					playAnimation(DEFAULT_ANIMATION, lastAnimationSubtitle, 1.6F);
				else
					spelled = true;

				bool defaultPlayed = false;

				// A reprodução da primeira letra deve ser longa para não ser cortada no fade
				letterSpeed = getProporcionalSpeed(LETTER_FIRST_SPEED, LETTER_FIRST_UNIT_SPEED);

				for (int i = 0; i < aniName.Length; i++)
				{
					char value = aniName[i];

					// Se for uma letra
					if (value >= 65 && value <= 90)
					{
						letterStateList.Add(playAnimation(value.ToString(), highlight(aniName, i), letterSpeed));
					}
					// Se for um número
					else if (value >= 48 && value <= 57)
					{
						numberStateList.Add(playAnimation(value.ToString(), highlight(aniName, i), numberSpeed));
					}
					// Se for uma vírgula
					else if (value == 44)
					{
						wordStateList.Add(playAnimation(value.ToString(), highlight(aniName, i)));
					}
					// Não há animação
					else
					{
						// Reproduz animação default apenas uma vez
						if ( ! defaultPlayed)
						{
							defaultPlayed = true;
							playAnimation(DEFAULT_ANIMATION, aniName);

							// A reprodução da próxima letra deve ser longa para não ser cortada no fade
							letterSpeed = getProporcionalSpeed(LETTER_FIRST_SPEED, LETTER_FIRST_UNIT_SPEED);
						}

						Debug.Log("Animação \"" + value + "\" inexistente.");
						continue;
					}

					defaultPlayed = false;
					letterSpeed = getProporcionalSpeed(LETTER_DEFAULT_SPEED, LETTER_UNIT_SPEED);
				}

				lastAnimationSubtitle = aniName;
			}
		}

		// Default
		playAnimation(DEFAULT_ANIMATION, lastAnimationSubtitle);

		loading = false;
	}

	/*
	 *	Sincroniza as legendas com as animações.
	 */
	IEnumerator handleStates()
	{
		// Enquanto estiver executando a rotina "loadAndPlay"
		// ou existir animações na fila de reprodução
		while (loading || animQueue.Count > 0)
		{
			if (animQueue.Count > 0)
			{
				KeyValuePair<string, string> pair = animQueue.Peek();

				setSubtitle(pair.Key);

				while (COMPONENT_ANIMATION.IsPlaying(pair.Value))
					yield return null;

				animQueue.Dequeue();
			}
			else yield return null;

			setSubtitle("");
		}

		wordStateList.Clear();
		letterStateList.Clear();
		numberStateList.Clear();
	}

}