Optimizing an A* Pathfinder

Just to share one nice little project I have done! 

My plan was optimized an simple A* algorithm the maximum as I can, and I've managed to reduce the method GetPah() from almost 30 ms to only 4,82 ms and the GC from 40,60 KB to 0 KB! 

Was a pretty fun and interesting to work on this kind of stuff! 

The project's available as Open Source on git hub, with all the benchmarks and discoveries I found along the way:


 

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



Automatically show the build version from Build Settings

I've found this, and has been proved really worth, all that I want is display a label on the menu on my games / apps, when you display the current version of your game, so you can debug things easily when you're trying to found / debug some bugs.

You will need to put this file, inside some Editor folder

What this will do is, as soon you open Unity, he will look for the Version from the Bundle Settings, and generate a new file for you, in this case one like these:
 

So whenever you want to display the current bundle version, just use simple as this:
 

And now you're ready to go :)

There is only a catch, this will be update when you open your unity, and ONLY when you open your Unity, so when you change the bundle version, you need to restart the unity so you get this new version.

Fast way to hide a specific Gizmo

Just discovered that, and want to share with you guys.

Sometimes when you are working with a few plugins that have specific gizmos, they can enter in from what you are trying to adjust, one fast way to disable gizmos is just close the current scrips on the target game object, like this: 

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 :)

Few amazing unity tips / tricks

I see this post and reddit, and I'll keep and copy here, so I can update myself with my own tips! 

Original Post:

http://tequibo.com/unity3d-tips/

  Showing private variables in inspector.

Showing private variables in inspector.

  Adding multiple prefabs in component’s list in the editor. Note: will not maintain order of items

Adding multiple prefabs in component’s list in the editor. Note: will not maintain order of items

  Removing empty entry from an array

Removing empty entry from an array

  Making empty gameobject visible only in editor

Making empty gameobject visible only in editor

  Dealing with parenting and scrolling

Dealing with parenting and scrolling

  Showing gizmos, colliders and raycasts (with Debug.DrawRay) IN GAME WINDOW while in play mode, works for selected objects only

Showing gizmos, colliders and raycasts (with Debug.DrawRay) IN GAME WINDOW while in play mode, works for selected objects only