テストの結果: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(){
}
}