Helpers And Extensions Part 1

Helpers And Extensions Part 1

If you code long enough then you are going to run into the same kinds of problems. And rather than solving them again and again, you’ll start to put together a tool box of sorts to solve these problems.

Here at Hundred Hour Club, we have put together our own tool box that helps us with various issues. The first of which we are going to cover here are some extension methods.

If you haven’t used extension methods yet, then you probably haven’t started to build your own toolbox yet, as they tend to be one of the first tools. Extension methods, are methods added to a class outside that class’s definition.

This has many benefits such as being able to easily call a function from a .net class that you yourself didn’t define or adding behavior to things like a List of objects that you would not be able to add functionality by defining a method on the individual item itself.

Let’s start with some useful Unity examples

Vector3 Extensions

public static class Vector3Extensions
{
	public static Vector3 GetDirectionTo(this Vector3 input, Vector3 to) =>
                                         (to - input).normalized;

	public static Vector3 GetDirectionTo(this Vector3 input, Transform to)=>
                                         GetDirectionTo(input,to.position);

	public static Vector3 GetDirectionTo(this Vector3 input, 
                                             MonoBehaviour to)=> 
             GetDirectionTo(input,to.transform);

If this is your first extension method, then there are a couple things about this to cover first. Extension methods are defined in Static classes, using static functions. Extension methods are defined by using the ‘this’ key word inside the first parameter of the static function.

As a standard we always call the object we are extending ‘input’. Having a standard name just makes reading and writing new extensions easier to work with.

This extension can me used as such

Vector3 spinDirection = transform.position.GetDirectionTo(origin);

Now you can easily google how to get the direction between point A and point B. And if you use it enough, you might even memorize it. If you remember your physics, and you forget it, you might be able to just pencil it out as needed, but the benefit of the extension method, is that it’s written one time. And it’s easy to remember. For this logic, we wanted a soccer ball to spin in the direction of the user that kicked it. Using our little extension method, we could grab the direction without having to worry about possibly mixing up from ‘to & from’ in the direction equation.

We also added a transform and a monobehavior overload for ease of use

Transform Extensions

/// <summary>
/// Adds a child and set it's local position to zero 
/// </summary>
public static class TransformExtensions
{
    public static void AddChildAtRoot(this Transform input, Transform child)
    {
	child.parent = input;
	child.localPosition = Vector3.zero;
    }
}

Adding a child at the root of something is really useful. It’s also easy to forget to zero out it’s local position and have the objects parented in funny positions, So the ‘AddChildAtRoot’ function takes care of that.

In Lamina Island we needed to spawn billboards that always face the camera. We would spawn one for each player and use them appropriately. To accomplish this, of course we want to spawn it at a particular position (usually rooted to a transform point) and set it as a child of that so it’ll update when the object or player moves.

Add Child at root takes care of that nicely.

Another really useful thing for a transform to answer is. Are we facing another transform. Is the player facing the object he’s trying to use. Sometimes it’s easier to ask if one transform is facing another rather than setting up a whole complex sensory system, especially for simple tasks.

/// <param name="certaintyPoint">
///  -1 => facing opposite completey
///   1 => facing perfectly
///   0 => to the side of (right angles)
/// </param>

public static bool IsFacingTarget(this Transform input,
                                       Transform target, 
                                       float certaintyPoint = .7f)
{
  Vector3 directionToTarget = (target.position - input.position).normalized;

  float dot = Vector3.Dot(input.forward,directionToTarget);

  bool isFacing = dot > certaintyPoint;

  return isFacing;
}

Of course if you look at this. You’ll start to notice that we could have cut the first line down by using our Vector3 extension like this

Vector3 directionToTarget = input.position.GetDirectionTo(target.position);

And while that works. It does look a bit ugly doesn’t it? We can of course turn this line into it’s own extension method.

public static Vector3 GetDirectionTo(this Transform input, Transform target)=> input.position.GetDirectionTo(target.position);

And just like that we’ve made our lives a little be easier from this point forward. Our original extension method is as simple as this now.

public static bool IsFacingTarget(this Transform input,
                                       Transform target, 
                                       float certaintyPoint = .7f)
{
  Vector3 directionToTarget =  input.GetDirectionTo(target);

  float dot = Vector3.Dot(input.forward, directionToTarget);

  bool isFacing = dot > certaintyPoint;
  
  return isFacing;
}

As you get more and more extension methods, eventually they will start using each other, and you’ll have them for so long you’ll forget that you had to write the functionality yourself. There are of course libraries and libraries of extension methods you can always download, but it’s best to sample the different ones you run into and add them as you need them instead of just grabbing everything that might be useful.

My Fin Tech tool box and my Hundred Hour Club tool box are very different tool boxes due to their different needs. And depending on the projects you tend to take on, your needs will be different than another. Adding too many extensions at once, is a sure way to let them collect dust and get in the way. What you want to do is slowly increase your tool box so that when you run into a problem you’ve already solved, second nature to bring out the extension and solve it again.