Nicholas Carpenedo
Nicholas Carpenedo

Reputation: 97

Instantiating a Prefab Without Inheriting Monobehaviour

I am trying to make a main script for a game that I am making in Unity 3D.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

class Main
{
    public GameObject ship;

    [RuntimeInitializeOnLoadMethod]
    static void OnRuntimeMethodLoad()
    {
        GameObject player = Object.Instantiate (ship, transform.position, Quaternion.identity) as GameObject;
    }
}

However, this gives me an error: An Object Reference is Required for non-static field, method, or property 'Main.Ship' Is it possible to instantiate an object without inheriting MonoBehaviour?

Upvotes: 4

Views: 7464

Answers (2)

Jack Mariani
Jack Mariani

Reputation: 2408

You're using a non-static value in static method and this is not valid.

You must either remove the static from the method:

void OnRuntimeMethodLoad()

Or add static to the field:

public static GameObject ship;

CONSIDERATIONS

I'm not sure this will allow you to implement the ship in a easy way, because you won't be able to set them in the editor (static fields don't appear in the Inspector window).

You will need either to Inject them with another script, gather them from resources or create them inside the Main class.

INJECTION

In theory you may set them from another script but, since this you're using RuntimeInitializeOnLoadMethodit, it might be challenging.

You can do that using a Scriptable Object OnDisable or OnEnable methods.

OnEnable() { Main.ship = something }

Anyway I would suggest against it because you do not know when the script will be called.
From RuntimeInitializeOnLoadMethod - Unity

Note: The execution order of methods marked [RuntimeInitializeOnLoadMethod] is not guaranteed.

GATHER FROM RESOURCES

This is probably the easiest solution.

You could gather the prefab from Resources Folder

//Load a gamobject file (Assets/Resources/GameObjects/ship.prefab)
ship = Resources.Load<GameObject>("GameObjects/ship");

CREATE IN MAIN CLASS

Lastly you can create the ship in the Main class directly.

The code will look like this:

void CreateShip()
{
   ship = new GameObject();
   var shipComponent = ship.AddComponent<ComponentToAdd>();
   shipComponent.Setup();
   //add any other components
}

CONCLUSION

You can always revert and go back with another easier solution, such as calling your code inside a MonoBehaviour.

If you require a manager you may use a singleton such as this.

If you confirm you want to use RuntimeInitializeOnLoadMethodit I would suggest to go ahead with Resource solution.

Upvotes: 3

zambari
zambari

Reputation: 5035

As explained by the previous answers, you are mixing instance scope and class (static) scope.

While several methods have been mentioned that can lead to this script working in the way you started it (you can use the static Resources class to search the assets for a filename), I would like to suggest doing it the other way around, and making your class a MonoBehaviour. While this may sound counter-intuitive to people who have coded in c and prefer to have a clear entry point to their code, its very useful in many cases to get used to the fact that you need to add a GameObject that will contain your main script somewhere.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

class ShipInstanceCreator: MonoBehaviour
{
    public GameObject ship;

    void Start()
    {
       GameObject player = Object.Instantiate (ship, transform.position,   transform.rotation) as GameObject;
   }

What are the benefits? First and main benefit is the fact that whatever inherits from MonoBehaviour gets full support from UnityEditor - so you get free 'Inspector' windows, where you can drag and drop stuff, have sliders etc for free, you get the 'enable/disable' checkbox, you get callbacks when enable/disable state is changed, you get your local transform component (which is often surprisingly useful), you get collisions etc.

Unity will also be able to serialize its state without doing it manually, which means you get a fully defined life-cycle of an object (which can act really funky in the editor when parts of the assembly get reloaded and some don't).

Such script can also instantiate enemy ships and asteroids (at its own transform position and orienation), and because functionality differences between them can (and should) be implemented as behaviours specific to a given prefab, there is no need for your instancer class to know anything else about what it instanes, other than it should be a GameObject

Finally, in a well written Unity project there rarely is a 'main' class. It works best if you split up responsibilites into seperate components, with as little as possible dependencies between them (dependency on UnityEngine is ok). Rather than 'writing a main script' you should be writing a 'InstantiatePlayerScript' and quite possibly it's almost done and you can move on to writing another script that will define another behaviour. That's why they are called Behaviours. Instantiating a player is a behaviour that modifies scene and it should live on the scene like other objects, rather than try to 'sit on the cloud' as a god object, supervising everything from high above. While its absolutely possible to write a game like that, that way becomes much harder as complexity grows.

There are some parts of a game that are ok to make global (ie MusicManager - there's only a single music that you shuold be playing at a single time) but for things like player - what if one day you want to make your game multiplayer? While Singleton Pattern (aka writing a main class somewhere) works ok for many things in Unity, its very easy to overdo.

Upvotes: 3

Related Questions