Trying to Make Static Classes Into Extensions of Unity's Classes but Getting Errors

I’ve had a nice little set of methods that I’ve created over the years and have had them in static classes so that I could call them from anywhere. The only (slight) downside to this is that I have to set my data equal to the class.method. I’d like to just use the method directly from the current object. What I’ve been doing is:

transform.position = Vector3x.SmoothFollow(blah, blah, blah);

What I’m trying to do is:

transform.position.SmoothFollow(blah, blah, blah);

Any of my functions that are used for Vector3’s should be callable from the Vector3. From what I can tell I have to put “this” in front of a method variable that is of the type that I want to call the method from. Thats what I’ve been doing, but getting errors. I am able to see the method as an available function via Monodevelops intellisense, which is cool, but when I call the function I get null reference errors. I guess I show some examples:

//Here's the one of the static classes that holds my methods
using UnityEngine;
using System.Collections;

namespace Extensions
{
    public static class Vector3x
    {
        //SmoothFollowLocal
        public static void SmoothFollowLocal(this Vector3 followingObject, Transform target, Vector3 offset, float speed)
        {
            Vector3 targetPosition = target.position + target.rotation * offset;
            followingObject = Vector3.Lerp(followingObject, targetPosition, speed * Time.deltaTime);
        }
        public static void SmoothFollowLocal(this Vector3 followingObject, Transform target, Vector3 offset, float speed, bool slerp)
        {
            Vector3 targetPosition = target.position + target.rotation * offset;
            if(slerp)
                followingObject = Vector3.Slerp(followingObject, targetPosition, speed * Time.deltaTime);
            else
                followingObject = Vector3.Lerp(followingObject, targetPosition, speed * Time.deltaTime);
        }
     
        public static void SmoothFollowLocal(this Vector3 followingObject, Vector3 targetPosition, Quaternion targetRotation, Vector3 offset, float speed, bool slerp)
        {
            Vector3 targetVector = targetPosition + targetRotation * offset;
            if(slerp)
                followingObject = Vector3.Slerp(followingObject, targetVector, speed * Time.deltaTime);
            else
                followingObject = Vector3.Lerp(followingObject, targetVector, speed * Time.deltaTime);
        }
        //Smooth Follow
        public static void SmoothFollow(this Vector3 followingObject, Transform target, Vector3 offset, float speed)
        {
            followingObject = Vector3.Lerp(followingObject, target.position + offset, speed * Time.deltaTime);
        }
        public static void SmoothFollow(this Vector3 followingObject, Transform target, float speed)
        {
            followingObject = Vector3.Lerp (followingObject, target.position, speed * Time.deltaTime);
        }
        //Follow
        public static void Follow(this Vector3 followingObject, Transform target, Vector3 offset)
        {
            followingObject = target.position + offset;
        }
        public static void Follow(this Vector3 followingObject, Transform target)
        {
            followingObject = target.position;
        }
        public static void Follow(this Vector3 followingObject, Vector3 target, Vector3 offset)
        {
            followingObject = target + offset;
        }
        public static void Follow(this Vector3 followingObject, Vector3 target)
        {
            followingObject = target;
        }
        //Pulse
        public static void Pulse(this Vector3 vector, float rate, float minScale, float maxScale)
        {
            float scale = (Mathf.Sin(Time.time * (rate * 2 * Mathf.PI)) + 1f) / 2f;
            scale = Mathf.Lerp(minScale, maxScale, scale);
            vector = new Vector3(scale, scale, scale);
        }
    }
}

Now I’ll call the Pulse method from a non-static class:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
//Note I'm using the namespace that all my class extensions are under
using Extensions;

public class FrameworkTesting : MonoBehaviour
{
    //Public Variables
    Transform trans;

    //Private Variables

    void Start()
    {
        //Pulse takes 3 floats , which is what I'm passing it.
        trans.Pulse(1f, 0.5f, 1f);
    }
}

In the start method, after typing trans and it’s dot-operator, Monodevelop pulls up the suggested/available methods and variables; Pulse IS an option along with all of my other methods. However, when I run it, I get a null reference exception. Here’s an example of an error:

NullReferenceException: Object reference not set to an instance of an object
Extensions.Transformx.Pulse (UnityEngine.Transform trans, Single rate, Single minScale, Single maxScale) (at Assets/Code/Scripts/Framework/Transformx.cs:165)
FrameworkTesting.Start () (at Assets/Code/Scripts/FrameworkTesting.cs:16)

One thing that caught my eye was that the error is saying that the method variables rate, minScale and maxScale are of the “Single” data type. I have them declared as floats. Not sure if that has any relevance, but I thought I’d point it out. I know all of these methods work because I used them a bunch, I was just accessing them differently (As I explained at the top of this post). I could really use some help, if something doesn’t make sense or wasn’t presented clearly please let me know so I can help you help me :slight_smile:

This doesn’t have anything to do with the fact that Vectors are passed by value, not reference?

We try and fire up your code when I get home to check.

I didn’t think about that, However it doesn’t work for the Transform either.

“Single” is just the internal representation of a float. It’s got to do with floats being single-precision as compared to doubles, which are… you guess correctly!

Now, your method call makes no sense - you have declared Pulse as an extension method for Vector3 (with the declaration “this Vector3 vector”), but you’re calling it on a Transform. So the code you’re posting doesn’t compile. If I paste it in, I’m getting errors.

If you’re actually going trans.position.Pulse(1f, 0.5f, 1f); , then the error might simply be that you haven’t assigned the trans variable to anything. You have declared it as a private variable in your code, so unless you have deleted a line where you go “trans = transform”, you’ll get a nullpointer.

Finally, if you fix all of that, your code won’t work. This is because, as BM said, Vector3s are value types, and are passed by value. So this Vector3:

myVector3.(1f, 0.5f, 1f)

is not the same Vector3 that shows up here:

public static void Pulse(this Vector3 vector, float rate, float minScale, float maxScale)

It’s instead a copy of the value. This is for speed and memory optimization reasons. So, in order to make your extension methods work with Vector3s, you’ll have to return the modified Vector3:

public static Vector3 Pulse(this Vector3 vector, float rate, float minScale, float maxScale)
        {
            float scale = (Mathf.Sin(Time.time * (rate * 2 * Mathf.PI)) + 1f) / 2f;
            scale = Mathf.Lerp(minScale, maxScale, scale);
            return new Vector3(scale, scale, scale);
        }

//usage:

    trans.localScale = trans.localScale.Pulse(1f, 0.5f, 1f);
2 Likes

Thanks for the in-depth answer. This was very helpful :slight_smile: