A few tips on object instantiation

Here are two tips that I found useful when instantiating new objects:

  1. When spawning multiple objects, use a parent object.
    Spawning multiple objects like the grid cells in this video will cause a lot of objects, named the same way, to get added to the game hierarchy window during runtime. This makes potential subsequent debugging much more annoying.
    What I do, is create an empty GameObject just before the loop, and use it as a parent in the Instantiate call (the last argument to Instantiate is a parent), something like this:
GameObject parentCell = new GameObject("GridCellVisuals");
for (...) {
  ...
  Instantiate(_singleCellVisualPrefab, LevelGrid.Instance.GetWorldPositionAt(gp), Quaternion.identity, parentCell.transform);
  ...
}

This results in an easier to navigate hierarchy.
(btw. one could also rename each of the cells to include its coordinates, using Object.name)

  1. Explicitly use the desired component type instead of generic Transform.
    You don’t need to pass prefabs as Transform - it can be the type of the component we will anyway cast it down to later (note: this post originally said “type of prefab” which does not make sense - see the responses).
    So use:
[SerializeField]
private SingleCellGridSystemVisual _singleCellVisualPrefab;

for setting the prefab in the engine - this will protect you from dragging the wrong prefab in there.
Additionally, this will work with Instantiate - the only difference is that it will immediately return the relevant component instead of Transform. But that’s exactly what we want here, since we end up with:

SingleCellGridSystemVisual newCell = Instantiate(_singleCellVisualPrefab, LevelGrid.Instance.GetWorldPositionAt(gp), Quaternion.identity, parentCell.transform);

instead of having to additionally GetComponent (and deal with nulls when we dragged a wrong prefab in).

7 Likes

These are all valid points

Point 2 is a little unclear, though. What exactly is a ‘prefab type’? Transform is valid. So is GameObject and indeed any script that is on the root of that prefab. In the example you specified, SingleCellGridSystemVisual is valid, but on our Unit, Unit is valid, BaseAction is valid, ShootAction is valid, UnitAnimator is valid. You can’t really define the prefab as a type.

This doesn’t invalidate your point. If you want to drag a prefab onto the script in the inspector, but you are only interested in the UnitAnimator script on that prefab, then by all means do

[SerializeField] UnitAnimator _unitAnimatorPrefab;
1 Like

Yeah, sorry, this was a mental shortcut on my side that definitely makes it confusing and is incorrect. It came from thinking about “our prefab” in this case which is a prefab containing this specific component.

So, as @bixarrio says, this is not somehow “the type of this prefab” (I don’t think it is possible to establish it in Unity’s type system), this is just “the type of the component of the prefab we are interested in”. In this case it just so happens that there is only one prefab with such component.

The main benefit in terms of “clean code” is that instead of taking more “generic” type as Transform or GameObject in that SerializedField and thus allowing potential mistakes down the road, we specify the component upfront.

I will update the post to make it clearer.

3 Likes

Your first point, (about using a parent GameObject for the Prefabs instantiation), is especially useful for debugging (…keeping all GameObjects organized). This practice will pay off in the future.

Thank you for pointing it out!

1 Like

While in this topic, I am really curious to why is not the GridSystemVisual script added as a component to the LevelGrid since it will always be used in pair. And instead of looking for the instance you would just GetComponent().

Thanks for the pointers, I’ll try to use them in the future as well.

You’re still going to need an instance (or other method of locating the LevelGrid) for those classes that access the LevelGrid that are NOT tightly paired with the LevelGrid.

2 Likes

Especially for the objects that have an instance per grid position (of which we already have a hundred and later on likeley even more) does it make sens to stash all instances into some “container” object. Unless there was some need for it, another extra empty wouldn’t actually be needed, the spawning object pretty much serves this purpose in the way the scene is currently organized.

So, within the GridSystemVisual (which is a MonoBehaviour), I simply did this:

public class GridSystemVisual : MonoBehaviour
{
  private GridSystemVisualSingle[,] singlesArray;

  private void Start()
  {
    singlesArray = new GridSystemVisualSingle[LevelGrid.Instance.GetGridWidth(), LevelGrid.Instance.GetGridHeight()];
    // Loop X
      // Loop Z 
      {
         GridPosition gridPosition = new GridPosition(x, z);
         Transform singleTransform = Instantiate(singlePrefab,
             LevelGrid.Instance.GetWorldPosition(gridPosition),
             Quaternion.identity, transform);
         singlesArray[x, z] = singleTransform.GetComponent<GridSystemVisualSingle>();
      }
  }
}

The GridSystem being a naked class, however, I handed in the transform from the LevelGrid which will call CreateDebugObjects():

public class LevelGrid : MonoBehaviour
{
    private void Awake()
    {
        //...
        gridSystem = new GridSystem(10, 10, 2f);
        gridSystem.CreateDebugObjects(gridDebugObjectPrefab, transform);
    }
}

And finally the GridSystem:

public class GridSystem
{

  public void CreateDebugObjects(Transform debugPrefab, Transform parent = null)
  {
    //Loop X
      //Loop Z
      {
          Transform debugTransform = GameObject.Instantiate(debugPrefab, GetWorldPosition(gridPosition), Quaternion.identity, parent);
      }
  }
}

And the transform for the parent is completely optionally for the caller.

Privacy & Terms