Per character text animation in Unity!

For quite some time, I've being working with some simple UI text animations, mostly of the times we just deal with that using tweens, scaling the transform of the object, but inspired by one post on reddit, I create the Text Juicer, a simple tool, where you can configure and animate text, per character, like that:

The project is open source, and is on github:
https://github.com/badawe/Text-Juicer

Feel free to add more effects and send new PRs

Infinity list ~ Reusable Cells ~ for NGUI

Hello everyone, I'm a bit of a crunch lately, so this is why I'm not posting so much, but I get some time today, and decided to do a post, about something really useful for unity developers, that want to display like a list of friends or anything like that, but can't rely on having more than 300 game objects on the screen. 

I've made something like this, 3 years ago, but have some bugs, but I get to a stable version! :P
 

But that was really old, and I need this again, I've tested a few solutions on Asset Store, but none of this are really good in my option, then I've found a solution from one guy, that really works REALLY well! 

Here is the link (Thanks guys from OrangeLabsUK):
https://github.com/OrangeLabsUK/InfiniteList_NGUI


I've just made a few update, so is easy to replace the cell, and use in other functions, I've just changed the method PopulateListItemWithIndex to virtual, so I can change the data of this cell. And thats it, perfect, reusable, really light (I'm displaying more than 1000 objects at once, without a single problem

Intercept errors from unity and show a nice popup

I know most of Unity developers, already know about that, but this is something that i discovered not long time ago, and I want to share with you guys,  take a look at the gif bellow


So, this is done pretty simple, Unity has one way so you can grab all the Logs from the editor:

And that's it! 
In addition with that, I strongly advise have something like RayGun this tool, will track your errors and show  a bunch of other useful information, like this:

Errors Count, what is the status, where is happen and a lot more useful information

Update from Lior Tal

RegisterLogCallback is now deprecated in Unity 5.

It suffered from a big issue that it was basically just a delegate (not exposed as an event).
This means that any class that used it was overriding all the other delegates that were assigned to it.

Now that Unity 5 was released, you could use Application.logMessageReceived (event) to achieve the same.
— Lior Tal



Convert Text Component to TextMeshProUGUI keeping configurations

For the ones who dosent know, TextMesh Pro is an alternative solution for the Unity Text component, with a lot of more customization and better performance, check some videos / demos here: http://digitalnativestudios.com/

And here is the tool working:

Just to share a quick script that I've done. basically get all instances of the Text component on the current scene, and replace for an new TextMeshPROUGUI one, keeping all your settings, like:

  • Font
  • Color
  • Size
  • Best Fit
  • Alignment
  • Overflow

Worked really well for my purpose, maybe can help someone else, just save the embed script in some editor folder:

Tested with the Unity 4.6.3f1 and Text Mesh Pro Beta v0.1.46 Beta 4.5.1 (you need to get access for this beta on the forum: http://digitalnativestudios.com/forum/ )

Unity UI vs NGUI - the learning path

So, I just want to share a few things that I'm discovering in the process, and a few tips and tricks, from a guy who work for the past 3 years only with NGUI

 

The draw calls (Overdraw)

The first thing that I've noticed, is that the draw calls, doing exaclty the same way I do things in NGUI result in a lot more draw calls using the new UI, check this example bellow:

 Even object off the screen view, are being draw, and 64 draw calls for something simple like that, is wrong, really wrong

Even object off the screen view, are being draw, and 64 draw calls for something simple like that, is wrong, really wrong

So, the main difference here, from the NGUI, on NGUI when a Widget have alpha 0, this are not draw anymore.  But the same in Unity UI do not work, so you need to actually deactivate every object that you aren't using anymore.  So the solution for this are pretty simple, I already work in a structure that help this a lot, that will be something like this:


  Object Controller  (GameObject, Controller Class)    - Content (Game Object, Canvas Group)    -- Objects inside 

Object Controller  (GameObject, Controller Class)
 - Content (Game Object, Canvas Group)
 -- Objects inside 

So, after making this changes, this is the result of the draw calls:

I know that 31 draw calls still not perfect as I want, but there is a lot of dynamic images being loaded from the server, and a lot of dynamic fonts here! I'll do more optimization later, and post here the results! 

 

More few things about Draw Calls (Updated 03/29)

I've decided run a few tests about the Draw Calls to make sure of how this is working exactly, so I've created a new scene, with only one background, here is the result:

 

So 2 draw calls
 - Canvas
 - Background Image

Adding one more image above everything, and this being inside an atlas, the same atlas that we had on for the background image:

Now 3 draw calls
 - Canvas
 - Background Image
 - New Error Sign

WHAT?! They are in the same atlas right?! But images overdrawing something else, will generate more one draw call! :( So if I add one more image side by side with the last error sign

Now with 2 images, that is not overlapping we had 3 draw calls
- Canvas
- Background Image
- Error Sign

 - Additional Error Sign draw call is saved by the batching. 

Now adding one more image, from the same atlas

His next image, the like sign, do not add any more draw calls! (YEY) because is in the same atlas

But what happens when this like sign is in the middle of the 2 errors signs?

3 Draw calls?! 

Ok, that was new for me, I tough that this will generate 2 extra draw calls, but I guess I'm wrong,  but what about we adding a font inside this same example

4 Draw Calls
 - Canvas
 - Background
 - The 3 Images 
- Text

Ok, adding a text generate one more draw call, I'm expecting that, so lest put this text in the middle of the 3 images, and see what happens:

 5 Draw Calls,   - Canvas  - Background  - Image  - Text  - Extra 2 Images

5 Draw Calls, 
 - Canvas
 - Background
 - Image
 - Text
 - Extra 2 Images

And what about reuse this same text? This will generate more draw calls of not?

Sadly yes, reusing the same font again, generate one more draw call, so, keep in mind that every TextObject will result in more one draw call =/

So a few things to keep in mind:

  • You can overlap objects without getting additional draw calls (if they are not text with dynamic fonts)
  • Every new text asset will generate new draw calls



This post will be updated regularly 


Wrapping Unity C# Coroutines for Exception Handling, Value Retrieval, and Locking

Busy weeks lately! But one of things that really drive me crazy is not be able to get exeptions inside coroutines, I know a lot of peoples have this issue as well, and everyone is asking, and one friend of mine show me this:

http://zingweb.com/blog/2013/02/05/unity-coroutine-wrapper/

This is just amazing, you can have call back from a coroutine, try catch and everything we want :D

 

   1:  using UnityEngine;
   2:  using System;
   3:  using System.Collections;
   4:  using System.Collections.Generic;
   5:   
   6:  /// <summary>
   7:  /// Extending MonoBehaviour to add some extra functionality
   8:  /// Exception handling from: http://twistedoakstudios.com/blog/Post83_coroutines-more-than-you-want-to-know
   9:  /// 
  10:  /// 2013 Tim Tregubov
  11:  /// </summary>
  12:  public class TTMonoBehaviour : MonoBehaviour
  13:  {
  14:      private LockQueue LockedCoroutineQueue { get; set; }
  15:              
  16:      /// <summary>
  17:      /// Coroutine with return value AND exception handling on the return value. 
  18:      /// </summary>
  19:      public Coroutine<T> StartCoroutine<T>(IEnumerator coroutine)
  20:      {
  21:          Coroutine<T> coroutineObj = new Coroutine<T>();
  22:          coroutineObj.coroutine = base.StartCoroutine(coroutineObj.InternalRoutine(coroutine));
  23:          return coroutineObj;
  24:      }
  25:      
  26:      /// <summary>
  27:      /// Lockable coroutine. Can either wait for a previous coroutine to finish or a timeout or just bail if previous one isn't done.
  28:      /// Caution: the default timeout is 10 seconds. Coroutines that timeout just drop so if its essential increase this timeout.
  29:      /// Set waitTime to 0 for no wait
  30:      /// </summary>
  31:      public Coroutine<T> StartCoroutine<T>(IEnumerator coroutine, string lockID, float waitTime = 10f)
  32:      {
  33:          if (LockedCoroutineQueue == null) LockedCoroutineQueue = new LockQueue();
  34:          Coroutine<T> coroutineObj = new Coroutine<T>(lockID, waitTime, LockedCoroutineQueue);
  35:          coroutineObj.coroutine = base.StartCoroutine(coroutineObj.InternalRoutine(coroutine));
  36:          return coroutineObj;
  37:      }
  38:      
  39:      /// <summary>
  40:      /// Coroutine with return value AND exception handling AND lockable
  41:      /// </summary>
  42:      public class Coroutine<T>
  43:      {
  44:          private T returnVal;
  45:          private Exception e;
  46:          private string lockID;
  47:          private float waitTime;
  48:          
  49:          private LockQueue lockedCoroutines; //reference to objects lockdict
  50:          private bool lockable;
  51:          
  52:          public Coroutine coroutine;
  53:          public T Value
  54:          {
  55:              get 
  56:              { 
  57:                  if (e != null)
  58:                  {
  59:                      throw e;
  60:                  }
  61:                  return returnVal;
  62:              }
  63:          }
  64:          
  65:          public Coroutine() { lockable = false; }
  66:          public Coroutine(string lockID, float waitTime, LockQueue lockedCoroutines)
  67:          {
  68:              this.lockable = true;
  69:              this.lockID = lockID;
  70:              this.lockedCoroutines = lockedCoroutines;
  71:              this.waitTime = waitTime;
  72:          }
  73:          
  74:          public IEnumerator InternalRoutine(IEnumerator coroutine)
  75:          {
  76:              if (lockable && lockedCoroutines != null)
  77:              {        
  78:                  if (lockedCoroutines.Contains(lockID))
  79:                  {
  80:                      if (waitTime == 0f)
  81:                      {
  82:                          //Debug.Log(this.GetType().Name + ": coroutine already running and wait not requested so exiting: " + lockID);
  83:                          yield break;
  84:                      }
  85:                      else
  86:                      {
  87:                          //Debug.Log(this.GetType().Name + ": previous coroutine already running waiting max " + waitTime + " for my turn: " + lockID);
  88:                          float starttime = Time.time;
  89:                          float counter = 0f;
  90:                          lockedCoroutines.Add(lockID, coroutine);
  91:                          while (!lockedCoroutines.First(lockID, coroutine) && (Time.time - starttime) < waitTime)
  92:                          {
  93:                              yield return null;
  94:                              counter += Time.deltaTime;
  95:                          }
  96:                          if (counter >= waitTime)
  97:                          { 
  98:                              string error = this.GetType().Name + ": coroutine " + lockID + " bailing! due to timeout: " + counter;
  99:                              Debug.LogError(error);
 100:                              this.e = new Exception(error);
 101:                              lockedCoroutines.Remove(lockID, coroutine);
 102:                              yield break;
 103:                          }
 104:                      }
 105:                  }
 106:                  else
 107:                  {
 108:                      lockedCoroutines.Add(lockID, coroutine);
 109:                  }
 110:              }
 111:              
 112:              while (true)
 113:              {
 114:                  try 
 115:                  {
 116:                      if (!coroutine.MoveNext())
 117:                      {
 118:                          if (lockable) lockedCoroutines.Remove(lockID, coroutine);
 119:                          yield break;
 120:                      }
 121:                  }
 122:                  catch (Exception e)
 123:                  {
 124:                      this.e = e;
 125:                      Debug.LogError(this.GetType().Name + ": caught Coroutine exception! " + e.Message + "\n" + e.StackTrace); 
 126:                      if (lockable) lockedCoroutines.Remove(lockID, coroutine);
 127:                      yield break;
 128:                  }
 129:                  
 130:                  object yielded = coroutine.Current;
 131:                  if (yielded != null && yielded.GetType() == typeof(T))
 132:                  {
 133:                      returnVal = (T)yielded;
 134:                      if (lockable) lockedCoroutines.Remove(lockID, coroutine);
 135:                      yield break;
 136:                  }
 137:                  else
 138:                  {
 139:                      yield return coroutine.Current;
 140:                  }
 141:              }
 142:          }
 143:      }
 144:      
 145:      
 146:      /// <summary>
 147:      /// coroutine lock and queue
 148:      /// </summary>
 149:      public class LockQueue
 150:      {
 151:          private Dictionary<string, List<IEnumerator>> LockedCoroutines { get; set; }
 152:          
 153:          public LockQueue()
 154:          {
 155:              LockedCoroutines = new Dictionary<string, List<IEnumerator>>();
 156:          }
 157:          
 158:          /// <summary>
 159:          /// check if LockID is locked
 160:          /// </summary>
 161:          public bool Contains(string lockID)
 162:          {
 163:              return LockedCoroutines.ContainsKey(lockID);
 164:          }
 165:          
 166:          /// <summary>
 167:          /// check if given coroutine is first in the queue
 168:          /// </summary>
 169:          public bool First(string lockID, IEnumerator coroutine)
 170:          {
 171:              bool ret = false;
 172:              if (Contains(lockID))
 173:              {
 174:                  if (LockedCoroutines[lockID].Count > 0)
 175:                  {
 176:                      ret = LockedCoroutines[lockID][0] == coroutine;
 177:                  }
 178:              }
 179:              return ret;
 180:          }
 181:          
 182:          /// <summary>
 183:          /// Add the specified lockID and coroutine to the coroutine lockqueue
 184:          /// </summary>
 185:          public void Add(string lockID, IEnumerator coroutine)
 186:          {
 187:              if (!LockedCoroutines.ContainsKey(lockID))
 188:              {
 189:                  LockedCoroutines.Add(lockID, new List<IEnumerator>());
 190:              }
 191:              
 192:              if (!LockedCoroutines[lockID].Contains(coroutine))
 193:              {
 194:                  LockedCoroutines[lockID].Add(coroutine);
 195:              }
 196:          }
 197:          
 198:          /// <summary>
 199:          /// Remove the specified coroutine and queue if empty
 200:          /// </summary>
 201:          public bool Remove(string lockID, IEnumerator coroutine)
 202:          {
 203:              bool ret = false;
 204:              if (LockedCoroutines.ContainsKey(lockID))
 205:              {
 206:                  if (LockedCoroutines[lockID].Contains(coroutine))
 207:                  {
 208:                      ret = LockedCoroutines[lockID].Remove(coroutine);
 209:                  }
 210:                  
 211:                  if (LockedCoroutines[lockID].Count == 0)
 212:                  {
 213:                      ret = LockedCoroutines.Remove(lockID);
 214:                  }
 215:              }
 216:              return ret;
 217:          }
 218:          
 219:      }
 220:   
 221:  }

You can check for more info here: http://zingweb.com/blog/2013/02/05/unity-coroutine-wrapper/

And thanks Philip for showing that :)
 


 

 

Performance Test v2

After a few comments on the reddit, I've decided run a couple of more tests, using a few tips that I get from there, so the new results will be bellow, and first I want to make a few notes here:
 

  • I'm using Unity 4.5.5.1f with Visual Studio 2013
  • All of this are tested on my PC running on the editor, I'll make mobiles builds later
  • If you are looking to this, and thinking this can save the performance on your game, you're probably screwed! This at most of the cases will not BRING a big impact
  • There is a TON of other things to take in considerations besides how fast this performance.
  • Don't take this too serious :)
  • An amazing guy  braaadt show me this link [ http://mattwarren.org/2014/09/19/the-art-of-benchmarking/ ], and I've made a few more tests here, just to make an update and better version about it! 

Now for testing purposes I'll use this Main Method here: 

   1:  public void Profile(string pDescription, int pInteractions, bool pRemoveBestAndWorst, Action pAction)
   2:  {
   3:      //If I want to remove the best and the worst interaction
   4:      if (pRemoveBestAndWorst)
   5:          pInteractions += 2;
   6:   
   7:      //Cleaning the Memory
   8:      GC.Collect();
   9:      GC.WaitForPendingFinalizers();
  10:      GC.Collect();
  11:   
  12:          
  13:   
  14:      //Warm Up the method
  15:      pAction();
  16:      List<TimeSpan> tAllTimes = new List<TimeSpan>();
  17:      for (int i = 0 ; i < pInteractions ; i++)
  18:      {
  19:          Stopwatch tStopwatch = new Stopwatch();
  20:          tStopwatch.Start();
  21:   
  22:          pAction();
  23:   
  24:          tStopwatch.Stop();
  25:          tAllTimes.Add(tStopwatch.Elapsed);
  26:      }
  27:      TimeSpan tAverageTime = GetAverageTime(tAllTimes, pRemoveBestAndWorst);
  28:   
  29:      if (pRemoveBestAndWorst)
  30:          pInteractions -= 2;
  31:   
  32:      Debug.Log("Average time of " + pDescription + " in "+pInteractions+" times is: " + tAverageTime); 
  33:  }
  34:   
  35:  private TimeSpan GetAverageTime (List<TimeSpan> pListOfTimes, bool pRemoveBestAndWorst)
  36:  {
  37:      if (pRemoveBestAndWorst)
  38:      {
  39:          pListOfTimes.Sort((TimeSpan a, TimeSpan b) =>
  40:          {
  41:              b.Ticks.CompareTo(a.Ticks);
  42:              return 0;
  43:          });
  44:   
  45:          //Debug.Log("Removing worst: " + pListOfTimes[0]);
  46:          //Removing the fastest
  47:          pListOfTimes.RemoveAt(0);
  48:   
  49:          //Debug.Log("Removing best: " + pListOfTimes[pListOfTimes.Count - 1]);
  50:          //Removing the slowest
  51:          pListOfTimes.RemoveAt(pListOfTimes.Count - 1);
  52:   
  53:      }
  54:   
  55:   
  56:      double doubleAverageTicks = pListOfTimes.Average(timeSpan => timeSpan.Ticks);
  57:      long longAverageTicks = Convert.ToInt64(doubleAverageTicks);
  58:   
  59:      pListOfTimes.Sort((TimeSpan a, TimeSpan b) => b.CompareTo(a));
  60:   
  61:   
  62:   
  63:      return new TimeSpan(longAverageTicks);
  64:  }
  65:  private TimeSpan GetAverageTime (List<TimeSpan> pListOfTimes, bool pRemoveBestAndWorst)
  66:  {
  67:      if (pRemoveBestAndWorst)
  68:      {
  69:          pListOfTimes.Sort((TimeSpan a, TimeSpan b) =>
  70:          {
  71:              b.Ticks.CompareTo(a.Ticks);
  72:              return 0;
  73:          });
  74:   
  75:          //Debug.Log("Removing worst: " + pListOfTimes[0]);
  76:          //Removing the fastest
  77:          pListOfTimes.RemoveAt(0);
  78:   
  79:          //Debug.Log("Removing best: " + pListOfTimes[pListOfTimes.Count - 1]);
  80:          //Removing the slowest
  81:          pListOfTimes.RemoveAt(pListOfTimes.Count - 1);
  82:   
  83:      }
  84:   
  85:   
  86:      double doubleAverageTicks = pListOfTimes.Average(timeSpan => timeSpan.Ticks);
  87:      long longAverageTicks = Convert.ToInt64(doubleAverageTicks);
  88:   
  89:      pListOfTimes.Sort((TimeSpan a, TimeSpan b) => b.CompareTo(a));
  90:   
  91:   
  92:   
  93:      return new TimeSpan(longAverageTicks);
  94:  }

You can use the above code to test whatever you want, is not a TRUE PROFILER, but sometimes you just want make sure if something that you did, are faster than something before, maybe is useful to someone :)


So, lets do a couple of more tests and see what performance in each way:

Test 1 - An empty method

   1:  Profile("Empty Test", repeatForHowManyTimes, () =>
   2:              {
   3:                  for (int i = 0; i < totalItems; i++)
   4:                  {
   5:                      
   6:                  }
   7:              });

Average time of Method: 0.0000114


Test 2 - Populating an List<T>

   1:   Profile("Populate an List<T>", repeatForHowManyTimes, () =>
   2:              {
   3:                  List<ClassOne> tNewList = new List<ClassOne>();
   4:                  for (int j = 0 ; j < totalItems ; j++)
   5:                  {
   6:                      tNewList.Add(GenerateNewObject());
   7:                  }
   8:              });

Average time of Method: 0.0239758


Test 3 - Populating an Array

   1:  Profile("Populate an Array", repeatForHowManyTimes, () =>
   2:              {
   3:                  ClassOne[] tNewClass = new ClassOne[totalItems];
   4:                  for (int j = 0 ; j < totalItems ; j++)
   5:                  {
   6:                      tNewClass[j] = GenerateNewObject();
   7:                  }
   8:              });

Average time of Method: 0.0234332


Test 4 - Traveling a List<T> using foreach

   1:   List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:   
   7:  Profile("Foreach on a List<T>", repeatForHowManyTimes, () =>
   8:  {
   9:      foreach (ClassOne tClasseOne in tNewList)
  10:      {
  11:   
  12:      }
  13:  });

Average time of Method: 0.0001528


Test 5 - Traveling and Array using foreach

   1:  ClassOne[] tNewClass = new ClassOne[totalItems];
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewClass[j] = GenerateNewObject();
   5:  }
   6:  Profile("Foreach on a Array", repeatForHowManyTimes, () =>
   7:  {
   8:      foreach (ClassOne tClasseOne in tNewClass)
   9:      {
  10:   
  11:      }
  12:  });
  13:              

Average time of Method: 0.0000192


Test 6 - Traveling a List<T> with the Count on the for

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:  Profile("Traveling a List without asgin the Count", repeatForHowManyTimes, () =>
   7:  {
   8:      for (int index = 0 ; index < tNewList.Count ; index++)
   9:      {
  10:          ClassOne tClasseOne = tNewList[index];
  11:      }
  12:  });

Average time of Method: 0.0000658


Test 7 - Traveling a List<T> using for

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:  Profile("Traveling a List<T> using For", repeatForHowManyTimes, () =>
   7:  {
   8:      int tCount = tNewList.Count;
   9:      for (int index = 0 ; index < tCount ; index++)
  10:      {
  11:          ClassOne tClasseOne = tNewList[index];
  12:      }
  13:  });

Average time of Method: 0.0000423


Teste 8 - Traveling an Array using For

   1:  ClassOne[] tNewClass = new ClassOne[totalItems];
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewClass[j] = GenerateNewObject();
   5:  }
   6:  Profile("Traveling a Array using For", repeatForHowManyTimes, removeBestAndWorst, () =>
   7:  {
   8:      int tCount = tNewClass.Length;
   9:      for (int index = 0 ; index < tCount ; index++)
  10:      {
  11:          ClassOne tClasseOne = tNewClass[index];
  12:      }
  13:  });

Average time of Method: 0.0000205


Test 9 - Traveling a List<T> testing for some case

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:  Profile("Seraching on a Array interating a List<T>", repeatForHowManyTimes, removeBestAndWorst, () =>
   7:  {
   8:      int tCount = tNewList.Count;
   9:      int tRandomItemToSerach = Random.Range(0, 100);
  10:   
  11:      for (int index = 0 ; index < tCount ; index++)
  12:      {
  13:          ClassOne tClasseOne = tNewList[index];
  14:          if (tClasseOne.simpleInt == tRandomItemToSerach)
  15:          {
  16:   
  17:          }
  18:      }
  19:  });

Average time of Method: 0.0000572


Test 10 - Traveling a Array testing for some case

   1:  ClassOne[] tNewClass = new ClassOne[totalItems];
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewClass[j] = GenerateNewObject();
   5:  }
   6:  Profile("Seraching on a Array interating for", repeatForHowManyTimes, removeBestAndWorst, () =>
   7:  {
   8:      int tCount = tNewClass.Length;
   9:      int tRandomItemToSerach = Random.Range(0, 100);
  10:   
  11:      for (int index = 0 ; index < tCount ; index++)
  12:      {
  13:          ClassOne tClasseOne = tNewClass[index];
  14:          if (tClasseOne.simpleInt == tRandomItemToSerach)
  15:          {
  16:   
  17:          }
  18:      }
  19:  });

Average time of Method: 0.0000354


Test 11 - Searching on a List<T> using LinQ

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:  Profile("Search on a List<T> using LinQ", repeatForHowManyTimes, removeBestAndWorst, () =>
   7:  {
   8:      int tRandomItemToSerach = Random.Range(0, 100);
   9:      ClassOne tFound = tNewList.FirstOrDefault(x => tRandomItemToSerach == x.simpleInt);
  10:  });

Average time of Method: 0.0000178


Test 12 - Inverted for testing case 

   1:  List<ClassOne> tNewList = new List<ClassOne>();
   2:  for (int j = 0 ; j < totalItems ; j++)
   3:  {
   4:      tNewList.Add(GenerateNewObject());
   5:  }
   6:   
   7:  Profile("Inverted Interaction on a List<T>", repeatForHowManyTimes, removeBestAndWorst, () =>
   8:  {
   9:      int tCount = tNewList.Count;
  10:      int tRandomItemToSerach = Random.Range(0, 100);
  11:   
  12:      for (int index = tCount - 1 ; index >= 0 ; index--)
  13:      {
  14:          ClassOne tClasseOne = tNewList[index];
  15:          if (tClasseOne.simpleInt == tRandomItemToSerach)
  16:          {
  17:   
  18:          }
  19:      }
  20:  });

Average time of Method: 0.0000554


Compatarions

Populating a List<T> vs Array

Screenshot_103014_035535_PM.jpg

Traveling using foreach List<T> vs Array

Screenshot_103014_035837_PM.jpg

Traveling a list with the Count as enumerator vs assigned to a variable

Screenshot_103014_040153_PM.jpg

Traveling using for Array vs List<T>

Comparison inside a for List<T> vs Array

Using LinQ vs regular comparision

Regular For vs Inverted For

Last conclusions :)

  •  User regular array if you can, I know sometimes you need to change this size a lot, in this case go with List<T>
  • Assign the variable to a for, (int tCount = tList.Count) bofore the for, boost the performance in more than 55%
  • Using LinQ when you can, is really fast, in my tests about 221% faster.
  • And inverted for, (i--) is 23% slower :P
  • Using a foreach in a Array compared to a list, is 695% faster :P



 

Thread in Unity!

Just a quick update about something that I discovered this weekend

Thread Ninja - Multithread Coroutine

Basically what this do is, you can run a heavy task on another thread, this makes the task run keeping the FPS at some acceptable level while running your task, example:

   1:  for(int i = 0 ; i < int.MaxValue; i++)
   2:  {
   3:  }

This will lock your Unity until this finish, but if you make the same thing using the Tread Ninja. the Unity will keep running without problem or slowdown.

   1:  this.StartCoroutineAsync(IEnumeratorYourAsyncRoutine());
   2:   
   3:  IEnumeratorYourAsyncRoutine()
   4:  {
   5:      for(int i =0;i < int.MaxValue; i++)
   6:      {
   7:      }
   8:  }

This is awesome! One big problem using this, if that you can't move anything from Unity inside this coroutine, but if you want you can use this little help here:

   1:  yield return Ninja.JumpToUnity;

This can be a big saver for some things, and best of all, is FREE!

For vs Foreach vs LinQ vs Array vs List - Performance Tests

So, this is something that I always read about it, and I've see a few contradictions here and there, so I've decided to do this test by my own! And see which performance betters compared to the others! 

Populating

So, for the testing purpose I'll just created a simple class here, like this one:

using System;

[Serializable]
public class ClassOne
{
    public float simpleFloat;
    public int simpleInt;
    public string simpleString;

    public ClassOne(float pFloat, int pInt, string pString)
    {
        simpleFloat = pFloat;
        simpleInt = pInt;
        simpleString = pString;
    }
}

And I'll first populate with a 5000 items and create one Array and one List

public class PerformanceTestController : MonoBehaviour 
{

    public List<ClassOne> list = new List<ClassOne>();
    public ClassOne[] array;


    public bool populateList;
    public bool populateArray;


    private void PopulateList()
    {
        Stopwatch tStopwatch = new Stopwatch();
        tStopwatch.Start();
        list = new List<ClassOne>();
        for (int i = 0; i < 1000; i++)
        {
            ClassOne tClassOne = GenerateNewObject();

            list.Add(tClassOne);
        }
        tStopwatch.Stop();
        Debug.Log("Total time to populate a list: " + tStopwatch.Elapsed);
    }

   

    private void PopulateArray ()
    {
        Stopwatch tStopwatch = new Stopwatch();
        tStopwatch.Start();
        array = new ClassOne[1000];
        for (int i = 0 ; i < 1000 ; i++)
        {
            ClassOne tClassOne = GenerateNewObject();

            array[i] = tClassOne;
        }
        tStopwatch.Stop();
        Debug.Log("Total time to populate a array: " + tStopwatch.Elapsed);
    }


    private static ClassOne GenerateNewObject ()
    {
        int tRandomInt = Random.Range(0, 100);
        float tRandomFloat = Random.Range(0f, 100f);
        string tString = string.Format("String test: int = {0}, float = {1}", tRandomInt, tRandomFloat);

        ClassOne tClassOne = new ClassOne(tRandomFloat, tRandomInt, tString);
        return tClassOne;
    }

    void Update()
    {
        if (populateList)
        {
            populateList = false;
            PopulateList();
        }

        if (populateArray)
        {
            populateArray = false;
            PopulateArray();
        }
    }

Result Populating a List vs Array

    The  Array , after 3 tests, seems to be an average of 13.62% faster than populate a List

 

The Array, after 3 tests, seems to be an average of 13.62% faster than populate a List

Update - Populating a list with max items

When you initialize a List without max item setted, the list is initialized with 40 empty items, when you reach the item 41, the list will add more 40 items to the list and goes on, so a few more comparison between the performance in both:

 Don't&nbsp;

Don't 



Traveling using foreach - difference between Array and List

Now lets try the difference between the foreach and for in this generated class, using the following methods:

 public void TravelListInForeach()
 {
     Stopwatch tStopwatch = new Stopwatch();
     tStopwatch.Start();
     foreach (ClassOne tClassOne in list)
     {

     }
     tStopwatch.Stop();
     Debug.Log("Total time to run the List using foreach: " + tStopwatch.Elapsed);
 }

public void TravelArrayInForeach ()
{
    Stopwatch tStopwatch = new Stopwatch();
    tStopwatch.Start();
    foreach (ClassOne tClassOne in array)
    {

    }
    tStopwatch.Stop();
    Debug.Log("Total time to run the Array using foreach: " + tStopwatch.Elapsed);
}

Result traveling using foreach

 Using the foreach the Array proved to be an average of 647% faster than List

Using the foreach the Array proved to be an average of 647% faster than List


Traveling using for - difference between Array and List

Now lets using a regular for, using these methods:

 public void TravelListInFor ()
 {
     Stopwatch tStopwatch = new Stopwatch();
     tStopwatch.Start();
     for (int i = 0; i < list.Count; i++)
     {
       ClassOne tClassOne = list[i];
     }
     tStopwatch.Stop();
     Debug.Log("Total time to run the List using for: " + tStopwatch.Elapsed);
 }

public void TravelArrayInFor ()
{
    Stopwatch tStopwatch = new Stopwatch();
    tStopwatch.Start();
    for (int i = 0; i < array.Length; i++)
    {
      ClassOne tClassOne = array[i];
    }
    tStopwatch.Stop();
    Debug.Log("Total time to run the Array using for: " + tStopwatch.Elapsed);
}

Result traveling using for

 Using a regular for, the Array proved to be an average of 189% faster than List

Using a regular for, the Array proved to be an average of 189% faster than List


Traveling using for - difference between Array and List assign the variable first

I've decided to made more one test using for, but this time saving the Count and the Length in a variable before the for, like this:

 public void TravelListInFor2 ()
 {
     Stopwatch tStopwatch = new Stopwatch();
     tStopwatch.Start();
     int tCount = list.Count;
     for (int i = 0 ; i < tCount ; i++)
     {
       ClassOne tClassOne = list[i];
     }
     tStopwatch.Stop();
     Debug.Log("Total time to run the List using for: " + tStopwatch.Elapsed);
 }

public void TravelArrayInFor2 ()
{
    Stopwatch tStopwatch = new Stopwatch();
    tStopwatch.Start();
    int tLenght = array.Length;
    for (int i = 0 ; i < tLenght ; i++)
    {
      ClassOne tClassOne = array[i];
    }
    tStopwatch.Stop();
    Debug.Log("Total time to run the Array using for: " + tStopwatch.Elapsed);
}

Results

 Now the difference between both are not so bigger, but the array still 60% faster than list

Now the difference between both are not so bigger, but the array still 60% faster than list

 Just to display, that saving the enumerator before the for, gives a performance increase of 56%

Just to display, that saving the enumerator before the for, gives a performance increase of 56%


Search a regular for 

The code used is here

  public void SearchUsingForOnArray ()
  {
      Stopwatch tStopwatch = new Stopwatch();
      tStopwatch.Start();
      int tCount = array.Length;
      for (int i = 0 ; i < tCount ; i++)
      {
        ClassOne tClassOne = array[i];

        if (tClassOne.simpleInt == 50)
          break;
      }
      tStopwatch.Stop();
      Debug.Log("Total time to search the list using for: " + tStopwatch.Elapsed);
  }
 public void SearchUsingForOnList ()
    {
        Stopwatch tStopwatch = new Stopwatch();
        tStopwatch.Start();
        int tCount = list.Count;
        for (int i = 0 ; i < tCount ; i++)
        {
            ClassOne tClassOne = list[i];

            if(tClassOne.simpleInt == 50)
                break;
        }
        tStopwatch.Stop();
        Debug.Log("Total time to search the list using for: " + tStopwatch.Elapsed);
    }

Results

 Again, the performance of the same operation on a Length is outstanding

Again, the performance of the same operation on a Length is outstanding


Searching using LinQ 

code used:

 public void SearchUsingLinqOnList ()
 {
     Stopwatch tStopwatch = new Stopwatch();
     tStopwatch.Start();

     ClassOne tClassOne = list.FirstOrDefault(x => 50 == x.simpleInt);
     tStopwatch.Stop();
     Debug.Log("Total time to search the list using for: " + tStopwatch.Elapsed);
 }

public void SearchUsingLinqOnArray ()
{
    Stopwatch tStopwatch = new Stopwatch();
    tStopwatch.Start();

    ClassOne tClassOne = array.FirstOrDefault(x => 50 == x.simpleInt);
    tStopwatch.Stop();
    Debug.Log("Total time to search the list using for: " + tStopwatch.Elapsed);
}

Results

 This is strange! The List is almost 200% times faster than the Array on this case.

This is strange! The List is almost 200% times faster than the Array on this case.

Another thing that I've noticed, when you are using a LinQ on a array, the first time initialization need to generate the enumerator, that's why the first one is so expensive. 

        

 

 

 

 

Conclusion

After this tests, a few last comments here:

- I'm not a C# or Visual Studio expert, so maybe a few things here that I don't know why
- Array is 99% of the times faster than a List, so use it whenever you can
- The LinQ is not lighter than a regular for, but maintains a clear code in my opinion
- Avoid foreach
- Assign the enumerator to a variable, do not user Count on a for in to a List


That's it guys :)

Palett Picker - Unity Plugin

I've been looking around for some good Pallet solution for unity, cause sometimes is kind of hard keep every necessary colors for each project in a good place.

Them I found this:
https://github.com/prime31/PaletteKit

Is a pretty amazing tool to Load / Save / Export Pallet colors, is basically made to work together with the ProBuilder, but can be used wherever you like, I'm using a lot with NGUI:
 

Pick color for NGUI

The amazing thing about this plugin is this little box floating over the scene view, where you will have all your needed colors in hand:

To add new colors is pretty simple too:

add-new-color


If you want more detail, check this video about the plugin: