Search code examples
c#genericsunity-game-enginetypescreateinstance

Default constructor not found


I am making a finite state machine for my game in unity:

using UnityEngine;
using System;
public class StateMachine : MonoBehaviour {

    private State currentState;

    public void ChangeState<T>() where T : State {
        if (currentState != null) {
            currentState.Exit ();
        }
        currentState = (T)Activator.CreateInstance (typeof(T), this.gameObject);
        currentState.Enter();
    }

    void Update() {
        if (currentState != null) {
            currentState.Execute ();
        }
    }

    void OnCollisionEnter2D (Collision2D col)
    {
        currentState.ResolveCollision (col);
    }
}

Abstract class for the state:

using UnityEngine;
public abstract class State  {
    protected GameObject parent;

    public State(GameObject parent) {
        this.parent = parent;
    }

    abstract public void Enter();
    abstract public void Execute();
    abstract public void Exit();
    abstract public void ResolveCollision(Collision2D col);
}

Short example of a running state:

using UnityEngine;
public class Running : State {

    public Running(GameObject parent) : base(parent) {}

    public override void Enter ()
    {
        return;
    }

    public override void Execute ()
    {
        if (Input.GetMouseButtonDown (0)) {
            parent.GetComponent<StateMachine> ().ChangeState<Jumping> ();
        }
    }

    public override void Exit ()
    {
        return;
    }

    public override void ResolveCollision (Collision2D col)
    {
        switch (col.gameObject.tag) {
        case "Obstacle":
            break;
        case "Pickupable":
            break;
        case "Enemy":
            break;
        }
        return;
    }
}

Jumping:

using UnityEngine;
public class Jumping : State {


    public override void Enter ()
    {
        return;
    }

    public override void Execute ()
    {
        return;
    }

    public override void Exit ()
    {
        return;
    }

    public override void ResolveCollision (Collision2D col)
    {
        switch (col.gameObject.tag) {
        case "Obstacle":
            break;
        case "Pickupable":
            break;
        case "Enemy":
            break;
        }
    }
}

The thought is that you attach the StateMachine to any gameobject and it would be easy to change state of that object.

Each state would need the parent object to operate on to for example change its position.

Each state would also need to be able to change the current state. Passing the parent gameobject (the gameobject the state machine is attached to) would allow the state to both access the statemachine and operate on the gameobject.

When changing a state i always know the gameobject to operate on (this is obviously the gameobject that the statemachine is attached to) so a requirement is that i do not manually pass in the gameobject each time i change the state.

To achieve this i thought of using (T)Activator.CreateInstance (typeof(T), this.gameObject);

When calling ChangeState<Running> (); in start method i get this:

MissingMethodException: Method not found: 'Default constructor not found...ctor() of Running'.

if i remove the argument this.gameobject (and remove the constructor in state and running) the game runs without errors. What is the problem and how can i solve it? (Still using the activator method) ... Any help is appretiated!

EDIT:

Just letting the user create an object of the class and assigning the gameobject later suits my needs but it is according to me not that elegant of a solution:

public void ChangeState(State state) {
    state.parent = this.gameObject;
    if (currentState != null) {
        currentState.Exit ();
    }
    //currentState = (T)Activator.CreateInstance (typeof(T), this.gameObject);
    currentState.Enter ();
}

Solution

  • Instead of using Activator.CreateInstance, you could also use reflection to instantiate your states. This has 2 benefits:

    • You can implement some logic to find the correct constructor based on parameter types (and you'll get more descriptive errors if you mess something up)
    • It performs a little better

    Here's how you'd do that:

    var ctor = typeof(T).GetConstructor(new []{typeof(GameObject)})
    currentState = ctor.Invoke(new object[]{this.gameObject}) as T;
    

    Or if you have a known & limited set of states, you could simply create a factory class that encapsulates the instantiation of different states. You can redirect to the correct private method based on the generic type argument, and within those private methods simply call the constructor directly. That performs even better.

    Though, if you're going for clean, using a Dependency Injection framework (Autofac is one of my favorites) is usually the cleaner solution.