HOTween、iTween とGoKitのspeedの比較


テストの結果：iTween < HoTween < GoKit
コードは以下です。

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using Holoville.HOTween;

public class SpeedTest : MonoBehaviour
{
	// ENUMS //////////////////////////////////////////////////
	
	private	enum TweenEngine 
	{
		None,
		HOTween,
		iTween
		,GoKit
	}
	
	private	enum TestType 
	{
		Emit,
		Loop,
		YoyoLoop
	}
	
	// SETTINGS ///////////////////////////////////////////////
	
	/// <summary>
	/// I keep this as a constant because iTween has no "version" field.
	/// </summary>
	private	const	string								ITWEEN_VERSION = "2.0.45";

	private const string  GOKIT_VERSION = "2.0.5";
	
	// VARS ///////////////////////////////////////////////////
	
	/// <summary>
	/// Basic tween duration when speed is set to x1.
	/// </summary>
	public		float									twDuration = 4;
	
	private		int										numCubes = 0;
	private		TweenEngine								twEngine;
	private		TestType								testType;
	private		float									timeScale;
	private		Quaternion								startRot = Quaternion.Euler( new Vector3( 20, 20, 20 ) );
	
	private		bool									twRunning;
	
	private		string[]								cubesBarLabels = new string[] {"1","10","50","100","500","1000","2000","3000","4000"};
	private		int										cubesBarSelIndex = 4;
	private		string[]								timescaleBarLabels = new string[] {"x0.25","x0.5","x1","x2","x4","x8","x16","x32"};
	private		int										timescaleBarSelIndex = 2;
	private		string[]								engineLabels = new string[] {"None","HOTween v" + HOTween.VERSION,"iTween v" + ITWEEN_VERSION,"GoKit v" + GOKIT_VERSION};
	private		int										engineSelIndex = 0;
	private		string[]								testTypeLabels = new string[] {"Emit","Loop","PingPong"};
	private		int										testTypeSelIndex = 0;
	private		bool									toggleColOut = true;
	private		bool									togglePos = true;
	private		bool									toggleRot;
	private		bool									toggleScale;
	
	private		bool									stylesSet;
	private		GUIStyle								cntrdLabelStyle;

	private     bool                                    poolManagerOn = false;
	public      static     bool                          withGC;

	private     List<GoTween>                            goTweens = new List<GoTween> ();
	
	// REFERENCES /////////////////////////////////////////////
	
	private		GameObject								thisGO; // Reference to this gameObject: stored in order to speed up iTween creation.
	private		GameObject								cubePrefab;
	private		Transform								cubesContainer;
	private		List<GameObject>						cubes; // List of all cubes in the scene.
	
	
	// ===================================================================================
	// UNITY METHODS ---------------------------------------------------------------------
	
	private void Start()
	{
		thisGO = this.gameObject;
		
		// Store the prefab from which all cubes will be created.
		cubePrefab = (GameObject) Resources.Load( "Cube" );
		// Instantiate a parent for all cubes (just to keep the Hierarchy panel clean).
		cubesContainer = new GameObject( "CUBES" ).transform;
		InvokeRepeating ("MemoryGC",1,30);
	}


	private void MemoryGC(){
		System.GC.Collect ();
	}
	private void OnGUI()
	{
		if ( !stylesSet ) {
			// One time set of GUI styles.
			stylesSet = true;
			GUI.skin.label.wordWrap = false;
			GUI.skin.button.fontSize = GUI.skin.toggle.fontSize = GUI.skin.label.fontSize = 11;
			cntrdLabelStyle = new GUIStyle( GUI.skin.label );
			cntrdLabelStyle.alignment = TextAnchor.UpperCenter;
		}
		
		// Create the test controls.
		
		GUILayout.BeginArea( new Rect( 10, 2, Screen.width - 20, Screen.height - 4 ) );
		GUILayout.BeginVertical();
		GUILayout.BeginHorizontal();
		GUILayout.FlexibleSpace();
		GUILayout.BeginVertical();
		
		GUILayout.BeginHorizontal();
		GUILayout.FlexibleSpace();
		GUILayout.Label( "Cubes" );
		int prevNumCubes = numCubes;
		cubesBarSelIndex = GUILayout.Toolbar( cubesBarSelIndex, cubesBarLabels );
		numCubes = Int32.Parse( cubesBarLabels[cubesBarSelIndex] );
		if ( twRunning && prevNumCubes != numCubes )		StartTweenTest();
		GUILayout.FlexibleSpace();
		GUILayout.EndHorizontal();
		
		GUILayout.BeginHorizontal();
		GUILayout.FlexibleSpace();
		GUILayout.Label( "Speed" );
		int prevTSIndex = timescaleBarSelIndex;
		timescaleBarSelIndex = GUILayout.Toolbar( timescaleBarSelIndex, timescaleBarLabels );
		timeScale = float.Parse( timescaleBarLabels[timescaleBarSelIndex].Substring( 1 ) );
		if ( timescaleBarSelIndex != prevTSIndex )			StartTweenTest();
		GUILayout.FlexibleSpace();
		GUILayout.EndHorizontal();
		
		GUILayout.BeginHorizontal();
		GUILayout.FlexibleSpace();
		GUILayout.Label( "Tweens" );
		int prevTotTogs = GetTotToggles();
		toggleColOut = GUILayout.Toggle( toggleColOut, "FadeOut" );
		togglePos = GUILayout.Toggle( togglePos, "Position" );
		toggleRot = GUILayout.Toggle( toggleRot, "Rotation" );
		toggleScale = GUILayout.Toggle( toggleScale, "Scale" );
		if ( prevTotTogs != GetTotToggles() )
			if ( twEngine != TweenEngine.None )				StartTweenTest();
		GUILayout.Space( 20 );
		GUILayout.Label( "Test type" );
		int prevTestTypeInd = testTypeSelIndex;
		testTypeSelIndex = GUILayout.Toolbar( testTypeSelIndex, testTypeLabels );
		testType = (TestType)testTypeSelIndex;
		if ( prevTestTypeInd != testTypeSelIndex )
			if ( twEngine != TweenEngine.None )				StartTweenTest();
		GUILayout.FlexibleSpace();
		GUILayout.EndHorizontal();
		
		GUILayout.BeginHorizontal();
		GUILayout.FlexibleSpace();
		GUILayout.Label( "Engine" );
		int prevEngineInd = engineSelIndex;
		engineSelIndex = GUILayout.Toolbar( engineSelIndex, engineLabels );
		twEngine = (TweenEngine)engineSelIndex;
		if ( prevEngineInd != engineSelIndex ) {
			Clear();
			if ( twEngine != TweenEngine.None )				StartTweenTest();
		}
		GUILayout.FlexibleSpace();
		GUILayout.EndHorizontal();

		GUILayout.Space( 20 );
		GUILayout.BeginHorizontal();
		GUILayout.FlexibleSpace();
		GUILayout.Label( "WithGC" );
		withGC = GUILayout.Toggle(withGC, "WithGC" );
		//GUILayout.Label( "PoolManagerOn" );
		
		//poolManagerOn = GUILayout.Toggle( poolManagerOn, "PoolManager" );
		GUILayout.FlexibleSpace();
		GUILayout.EndHorizontal();

		if ( twRunning ) {
			GUILayout.FlexibleSpace();
			
			GUILayout.BeginHorizontal();
			GUILayout.FlexibleSpace();
			if ( GUILayout.Button( "Play" ) )					Play();
			if ( GUILayout.Button( "Pause" ) )					Pause();
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			
			GUILayout.BeginHorizontal();
			GUILayout.FlexibleSpace();
			GUILayout.Label( ( twEngine == TweenEngine.HOTween ? "HOTween" : twEngine == TweenEngine.iTween? "iTween":"GoKit" ) + "\n" + numCubes + " tweened cubes, " + GetTotToggles() + " tweeened properties on each cube", cntrdLabelStyle );
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
		}
		
		GUILayout.EndVertical();
		GUILayout.FlexibleSpace();
		GUILayout.EndHorizontal();
		GUILayout.EndVertical();
		GUILayout.EndArea();
	}
	
	// ===================================================================================
	// PRIVATE METHODS -------------------------------------------------------------------
	
	/// <summary>
	/// Clears any running tween and destroys the cubes.
	/// </summary>
	private void Clear()
	{
		if ( twRunning ) {
			// Kill all HOTweens
			HOTween.Kill();
			//System.GC.Collect();
			// Kill all iTweens
			// (I do it while iterating through each tweened cube, because iTween.Stop() won't work if there are too many cubes).
			foreach ( GameObject cube in cubes ) {
				iTween.Stop( cube );
				//Go.killAllTweensWithTarget(cube);
				Go.killAllTweensWithTarget(cube.transform);
				//Go.Destroy(cube);
				Destroy( cube );
			}
			
			cubes = null;
		}
		
		twRunning = false;
	}
	
	/// <summary>
	/// Clears any running tweens and cubes, and starts a new test.
	/// </summary>
	private void StartTweenTest()
	{
		Clear();
		
		cubes = new List<GameObject>();
		for ( int i = 0; i < numCubes; ++i )
			NewTween();

		twRunning = true;
	}
	
	/// <summary>
	/// Creates a new cube and a random tween, based on the current settings.
	/// </summary>
	private void NewTween()
	{
		GameObject cubeGO = (GameObject)Instantiate( cubePrefab, ( togglePos ? Vector3.zero : RandomFromPosition() ), startRot );
		Destroy( cubeGO.GetComponent<BoxCollider>() );
		cubes.Add( cubeGO );
		// Set the cube parent.
		Transform cube = cubeGO.transform;
		cube.parent = cubesContainer;
		// Random color.
		Color startCol = RandomColor();
		Color endCol = startCol;
		endCol.a = 0;
		cube.renderer.material.color = startCol;
		// Random duration.
		float duration = twDuration * 0.5f + UnityEngine.Random.value * twDuration * 0.5f;
		// Settings.
		LoopType hoLoopType = ( testType == TestType.Emit || testType == TestType.Loop ? LoopType.Restart : LoopType.Yoyo );
		int hoLoops = ( testType == TestType.Emit ? 1 : -1 );
		iTween.LoopType iLoopType = ( testType == TestType.Emit ? iTween.LoopType.none : testType == TestType.Loop ? iTween.LoopType.loop : iTween.LoopType.pingPong );
		EaseType hoeaseType = EaseType.EaseInOutSine;
		iTween.EaseType iEaseType = iTween.EaseType.easeInOutSine;

		Go.defaultEaseType = GoEaseType.SineInOut;
		GoLoopType  goLoopType = (testType == TestType.Emit || testType == TestType.Loop ? GoLoopType.RestartFromBeginning : GoLoopType.PingPong);
		int goLoops = (testType == TestType.Emit ? 1 : -1);
		// Duration related to timeScale.
		// HOTween could do this by setting the TimeScale parameter,
		// but to keep both tests similar I won't use it.
		duration /= timeScale;
		// This lets me know when a callback has been added, so I don't do it twice.
		// The callback needs to be added in case of the Emit test type,
		// so that a cube can be destroyed (and a new one created) when the tween is complete.
		bool callbackAdded = false;
		
		switch ( twEngine ) {
		case TweenEngine.HOTween:
			// HOTWEEN
			if ( toggleColOut ) {
				if ( !callbackAdded && testType == TestType.Emit ) {
					// Add onComplete callback.
					callbackAdded = true;
					HOTween.To( cube.renderer.material, duration, new TweenParms().Prop( "color", endCol ).Ease( EaseType.EaseInSine ).OnComplete( OnHOTweenComplete, cubeGO ) );
				} else {
					HOTween.To( cube.renderer.material, duration, new TweenParms().Prop( "color", endCol ).Ease( EaseType.EaseInSine ).Loops( hoLoops, hoLoopType ) );
				}
			}
			// Since the other animations target is the same, I just create different parms, and than apply them to the cube's transform.
			TweenParms hoParms = new TweenParms().Ease( hoeaseType ).Loops( hoLoops, hoLoopType );
			if ( !callbackAdded && testType == TestType.Emit ) {
				// Add onComplete callback.
				callbackAdded = true;
				hoParms.OnComplete( OnHOTweenComplete, cubeGO );
			}
			if ( togglePos )			hoParms.Prop( "position", RandomToPosition() );
			if ( toggleRot )			hoParms.Prop( "rotation", RandomRotation() );
			if ( toggleScale )			hoParms.Prop( "localScale", RandomScale() );
			// Create a new tween for the cube's transform, and add the parms.
			if ( hoParms.hasProps )		HOTween.To( cube, duration, hoParms );
		break;
		case TweenEngine.iTween:
			// ITWEEN
			if ( toggleColOut ) {
				if ( !callbackAdded && testType == TestType.Emit ) {
					// Add onComplete callback.
					callbackAdded = true;
					iTween.ColorTo( cubeGO, iTween.Hash( "time", duration, "color", endCol, "easetype", iTween.EaseType.easeInSine, "oncomplete", "OniTweenComplete", "oncompletetarget", thisGO, "oncompleteparams", cubeGO ) );
				} else {
					iTween.ColorTo( cubeGO, iTween.Hash( "time", duration, "color", endCol, "easetype", iTween.EaseType.easeInSine, "looptype", iLoopType ) );
				}
			}
			if ( togglePos ) {
				Vector3 randomPos = RandomToPosition();
				if ( !callbackAdded && testType == TestType.Emit ) {
					// Add onComplete callback.
					callbackAdded = true;
					iTween.MoveTo( cubeGO, iTween.Hash( "time", duration, "x", randomPos.x, "y", randomPos.y, "z", randomPos.z, "easetype", iEaseType, "looptype", iLoopType, "oncomplete", "OniTweenComplete", "oncompletetarget", thisGO, "oncompleteparams", cubeGO ) );
				} else {
					iTween.MoveTo( cubeGO, iTween.Hash( "time", duration, "x", randomPos.x, "y", randomPos.y, "z", randomPos.z, "easetype", iEaseType, "looptype", iLoopType ) );
				}
			}
			if ( toggleRot ) {
				if ( !callbackAdded && testType == TestType.Emit ) {
					// Add onComplete callback.
					callbackAdded = true;
					iTween.RotateTo( cubeGO, iTween.Hash( "time", duration, "rotation", RandomRotation().eulerAngles, "easetype", iEaseType, "looptype", iLoopType, "oncomplete", "OniTweenComplete", "oncompletetarget", thisGO, "oncompleteparams", cubeGO ) );
				} else {
					iTween.RotateTo( cubeGO, iTween.Hash( "time", duration, "rotation", RandomRotation().eulerAngles, "easetype", iEaseType, "looptype", iLoopType ) );
				}
			}
			if ( toggleScale ) {
				Vector3 randomScale = RandomScale();
				if ( !callbackAdded && testType == TestType.Emit ) {
					// Add onComplete callback.
					callbackAdded = true;
					iTween.ScaleTo( cubeGO, iTween.Hash( "time", duration, "x", randomScale.x, "y", randomScale.y, "z", randomScale.z, "easetype", iEaseType, "looptype", iLoopType, "oncomplete", "OniTweenComplete", "oncompletetarget", thisGO, "oncompleteparams", cubeGO ) );
				} else {
					iTween.ScaleTo( cubeGO, iTween.Hash( "time", duration, "x", randomScale.x, "y", randomScale.y, "z", randomScale.z, "easetype", iEaseType, "looptype", iLoopType ) );
				}
			}
		break;
		case TweenEngine.GoKit:
			// ITWEEN
			if ( toggleColOut ) {
				if ( !callbackAdded && testType == TestType.Emit ) {
					callbackAdded = true;
					Go.to (cubeGO.transform,duration,new GoTweenConfig().materialColor(endCol)
					       .setIterations(goLoops,goLoopType)
					       .onComplete(delegate {
						cubes.RemoveAt(cubes.IndexOf(cubeGO));
						Destroy(cubeGO);
						NewTween();
				}));
				} else {
					Go.to (cubeGO.transform,duration,new GoTweenConfig().materialColor(endCol).setIterations(goLoops,goLoopType));
				}
			}
			if ( togglePos ) {
				if ( !callbackAdded && testType == TestType.Emit ) {
					callbackAdded = true;
					Go.to(cubeGO.transform,duration,new GoTweenConfig().position(RandomToPosition(),true)
					      .setIterations(goLoops,goLoopType)
					      .onComplete(delegate {
						cubes.RemoveAt(cubes.IndexOf(cubeGO));
						Destroy(cubeGO);
						NewTween();
					}));
				} else {
					Go.to(cubeGO.transform,duration,new GoTweenConfig().position(RandomToPosition(),true).setIterations(goLoops,goLoopType));
				}
			}
			if ( toggleRot ) {
				if ( !callbackAdded && testType == TestType.Emit ) {
					callbackAdded = true;
					Go.to(cubeGO.transform,duration,new GoTweenConfig().rotation(RandomRotation(),true)
					      .setIterations(goLoops,goLoopType)
					      .onComplete(delegate {
						cubes.RemoveAt(cubes.IndexOf(cubeGO));
						Destroy(cubeGO);
						NewTween();
					}));
				} else {
					Go.to(cubeGO.transform,duration,new GoTweenConfig().rotation(RandomRotation(),true).setIterations(goLoops,goLoopType));
				}
			}
			if ( toggleScale ) {
				if ( !callbackAdded && testType == TestType.Emit ) {
					callbackAdded = true;
					Go.to(cubeGO.transform,duration,new GoTweenConfig()
					      .scale(RandomScale(),true)
					      .setIterations(goLoops,goLoopType)
					      .onComplete(delegate {
						cubes.RemoveAt(cubes.IndexOf(cubeGO));
						Destroy(cubeGO);
						NewTween();
					}));
				} else {
					Go.to(cubeGO.transform,duration,new GoTweenConfig().scale(RandomScale(),true).setIterations(goLoops,goLoopType));
				}
			}
			break;
		}
	}
	
	/// <summary>
	/// Returns a random initial position.
	/// </summary>
	private Vector3 RandomFromPosition()
	{
		Vector2 r = UnityEngine.Random.insideUnitCircle * 30;
		Vector3 v = new Vector3( r.x, r.y, UnityEngine.Random.Range( -10, -30 ) );
		
		return v;
	}
	
	/// <summary>
	/// Returns a random position to tween to.
	/// </summary>
	private Vector3 RandomToPosition()
	{
		Vector3 v = UnityEngine.Random.onUnitSphere * 40;
		if ( v.z > 0 )		v.z = -v.z;
		
		return v;
	}
	
	/// <summary>
	/// Returns a random rotation.
	/// </summary>
	private Quaternion RandomRotation()
	{
		return UnityEngine.Random.rotation;
	}
	
	/// <summary>
	/// Returns a random scale.
	/// </summary>
	private Vector3 RandomScale()
	{
		float rand = UnityEngine.Random.Range( 2f, 4f );
		return new Vector3( rand, rand, rand );
	}
	
	/// <summary>
	/// Returns a random color.
	/// </summary>
	private Color RandomColor()
	{
		return new Color( UnityEngine.Random.Range( 0.1f, 0.8f ), UnityEngine.Random.Range( 0.1f, 0.8f ), UnityEngine.Random.Range( 0.1f, 0.8f ), 1 );
	}
	
	/// <summary>
	/// Gets the total number of toggles that are selected.
	/// </summary>
	private int GetTotToggles()
	{
		int res = 0;
		if ( toggleColOut )	++res;
		if ( togglePos )	++res;
		if ( toggleRot )	++res;
		if ( toggleScale )	++res;
		return res;
	}
	
	/// <summary>
	/// Plays current tweens.
	/// </summary>
	private void Play()
	{
		if (twEngine == TweenEngine.HOTween)
			HOTween.Play ();
		else if (twEngine == TweenEngine.iTween)
			iTween.Resume ();
		else {
			//Go.
		}
	}
	
	/// <summary>
	/// Pauses current tweens.
	/// </summary>
	private void Pause()
	{
		if (twEngine == TweenEngine.HOTween) {
			HOTween.Pause ();
		} else if (twEngine == TweenEngine.iTween) {
			iTween.Pause (); // Warning: doesn't work if test type is Emit and there are too many cubes.
		}
		else {

		}
	}
	
	// ===================================================================================
	// HANDLERS --------------------------------------------------------------------------
	
	/// <summary>
	/// HOTween onComplete callback.
	/// </summary>
	private void OnHOTweenComplete( TweenEvent e )
	{
		// Destroy the completed cube and create a new tweened one.
		GameObject cubeGO = (GameObject)( e.parms[0] );
		Destroy( cubeGO );
		cubes.RemoveAt( cubes.IndexOf( cubeGO ) );
		NewTween();
	}
	
	/// <summary>
	/// iTween onComplete callback.
	/// </summary>
	private void OniTweenComplete( GameObject p_cubeGO )
	{
		// Destroy the completed cube and create a new tweened one.
		Destroy( p_cubeGO );
		cubes.RemoveAt( cubes.IndexOf( p_cubeGO ) );
		NewTween();
	}
	private void GoTweenPlay(){

	}
	private void GoTweenPause(){

	}
}