Saturday, October 8, 2016

Love that Context Menu

So you're happily coding some simple test scenes.  Unfortunately, this usually involves lots of references in your scripts to other objects/components in the scene which in turn means lots of drag and drop.  You find this tedious and really just want things to automagically happen when possible.

It turns out that in version 4.0, Unity gave us some new abilities to make automagic things happen: component context menus!

It's as simple as adding a custom attribute above a script function!

[ContextMenu("My custom menu item")]
void MyCustomMethod() {

There's undoubtedly many great uses for this but for now, you're only focused on one: less click selecting, dragging, and dropping.

You have a parent in the hierarchy and you want it to automatically search its children and determine how to hook up references to them.  The parent in this case is the "squad" grouping of units that path together.  The children are the ones doing the actual pathing.  Here are the references that we want auto-filled:

  public NavMeshAgent frontMember;
  public NavMeshAgent rearMember;
  public NavMeshAgent leftMember;
  public NavMeshAgent rightMember;

This squad is determined by a spatial relationship.  If we assume that they are already placed in the map in their desired formation, we can write a context menu script to hook it up.  The front member will have the largest z value and the rear value will have the least.  You get the idea.
Your furious fingers get to work and produce this:

[ContextMenu("Auto Find Members")]
  void AutoFindMembers() {

    if (transform.childCount >= 4) {

      frontMember = transform.GetChild(0).GetComponent<NavMeshAgent>();
      rearMember = transform.GetChild(1).GetComponent<NavMeshAgent>();
      leftMember = transform.GetChild(2).GetComponent<NavMeshAgent>();
      rightMember = transform.GetChild(3).GetComponent<NavMeshAgent>();

      foreach(Transform child in transform) {

        if (child.position.z > frontMember.transform.position.z) {
          frontMember = child.GetComponent<NavMeshAgent>();
        }

        if (child.position.z < rearMember.transform.position.z) {
          rearMember = child.GetComponent<NavMeshAgent>();
        }

        if (child.position.x < leftMember.transform.position.x) {
          leftMember = child.GetComponent<NavMeshAgent>();
        }

        if (child.position.x > rightMember.transform.position.x) {
          rightMember = child.GetComponent<NavMeshAgent>();
        }

      }

      frontMember.name = "front";
      rearMember.name = "rear";
      leftMember.name = "left";
      rightMember.name = "right";

    } else {
      Debug.LogWarning("Need at least 4 children to perform auto find.");
    }

  }

Great!  This hooks it all up and renames the children as well so that you can easily verify the results.




Every little bit counts and you're sure there will be many more cases in the future where being able to call your code via a context menu will pay off.