Understanding Static Methods of a Class verse Instance Methods of a Class

So, in making my brick breaker game, I wanted to add some additional features to it. One was to add 3 lives to each level. To do this, I needed to create a function that allowed my LevelManager to track the lives and if the lives == 0, then run my LoadLevel. Like this…

	public static void SubtractLife(){
		lifes--;
		if(LevelManager.lifes == 0){
		     LoadLevel("lose");         
		}	
	}

So, when I went into my LoseCollider, I removed its ability to change level and replaced it with SubtractLife. Like this…

	void OnTriggerEnter2D(Collider2D trigger){
		LevelManager.SubtractLife();
		Ball.has_started = false;
	}

Well it didn’t like LoadLevel(“lose”) because it wasn’t a static method of LevelManager and so needed an object reference. Well I just made it static, as I figured why not, I can then easily access it from anywhere for level change functions. The ball, special events, w/e; I can call it when I need it. But then my buttons stopped working. They called on the LoadLevel method and I input the string in my inspector. Now it says “missing” before the method in the inspector. Like this…
Screenshot (16)

So it was a simple fix, I just created a new method call LoadLevelButton that wasn’t static…

	public void LoadLevelButton(string name){
		Application.LoadLevel(name);
	}

So, now I select that in the inspector and input my own string. My question is, and what I would like help on is am I doing it right? Should I make the LoadLevel function static, or am I just doing it wrong in creating multiple ways to accomplish the same thing? I guess what I’m really asking is why can’t my SubtractLife function call on LoadLevel unless LoadLevel is static?

using UnityEngine;
using System.Collections;

public class LevelManager : MonoBehaviour {
	
	private static int lifes = 3;
	public static void LoadLevel(string name){
		Brick.brickCount = 0;
		Application.LoadLevel(name);
	}
	public void LoadLevelButton(string name){
        Brick.brickCount = 0;
		Application.LoadLevel(name);
	}
	public static void LoadNextLevel(){
		Application.LoadLevel(Application.loadedLevel + 1);
	}
	
	public static void BricksDestroyed(){
		if(Brick.brickCount<=0){
			LoadNextLevel();
			LevelManager.LifeAdd();
		}
	}
	
	public static void LifeAdd(){
		lifes = 3;
	}
	
	public static void SubtractLife(){
		lifes--;
		if(LevelManager.lifes == 0){
			LoadLevel("lose");
			Ball.has_started = false;
		}	
	}

}
using UnityEngine;
using System.Collections;

public class LoseCollider : MonoBehaviour {

	void OnTriggerEnter2D(Collider2D trigger){
		print (trigger);
		LevelManager.SubtractLife();
        Ball.has_started = false;
	}
}

Ok, so I think I have it figured out. Its funny how typing out the question makes you look a little deeper! I created an instance of LevelManager in my LoseCollider. I then assigned it to the gameobject LevelManager. In doing this I could now call on the SubtractLife function without it being a static, which allowed me to remove the static declaration from LoadLevel. So, obviously the issue here is do you use a static to call on functions/methods from separate scripts, or do you make instances inside, assign them to the object, and leave the functions non-static? Hope that makes sense. I know in code you can skin a cat a hundred different ways, but when it comes to statics verses assigned instances to access other objects and there scripts, what is preferred and why?

Here is my new code BTW

using UnityEngine;
using System.Collections;

public class LoseCollider : MonoBehaviour {

	private LevelManager levelManager;
	
	void Start(){
		levelManager = GameObject.FindObjectOfType<LevelManager>();
	}

	void OnTriggerEnter2D(Collider2D trigger){
		levelManager.SubtractLife();
		Ball.has_started = false;
	}
}
using UnityEngine;
using System.Collections;

public class LevelManager : MonoBehaviour {
	
	private static int lifes = 3;
	//Restart Button
	public void LoadLevel(string name){
		Brick.brickCount = 0;
		Application.LoadLevel(name);
	}

	public static void LoadNextLevel(){
		Application.LoadLevel(Application.loadedLevel + 1);
	}
	
	public static void BricksDestroyed(){
		if(Brick.brickCount<=0){
			LoadNextLevel();
			LevelManager.LifeAdd();
		}
	}
	
	public static void LifeAdd(){
		lifes = 3;
	}
	
	public void SubtractLife(){
		lifes--;
		if(LevelManager.lifes == 0){
			LoadLevel("lose");
		}	
	}

}

Hi;

My question is, and what I would like help on is am I doing it right?

That’s fairly broad as questions go, as there are often many ways to accomplish the same task.

Should I make the LoadLevel function static, or am I just doing it wrong in creating multiple ways to accomplish the same thing?

I’ll answer this question with another question… why do you want to make the LoadLevel method static?

From your post, it seems that the desire to do so it primarily because of the error you were receiving.

I guess what I’m really asking is why can’t my SubtractLife function call on LoadLevel unless LoadLevel is static?

The reason for this is because you defined your SubtractLife method as static, anything trying to access this method does not need to instantiate an instance of LevelManager in order to do so, however… when you then call LoadLevel, this method is not static, therefore it does require an instance of the LevelManager in order to access it - which you do not have from your SubtractLife method.

My suggestion to you would be;

  • Roll back the functionality of LevelManager to what it originally was, just comment out your other code for the time being.
  • Check that it all runs as it did before the additional lives functionality.
  • Create a separate class which can be responsible for managing the player, and leave LevelManager responsible for simply changing the levels.
  • Within your new class, for managing the player, do your number of lives validation here, and then make a call to the LevelManager when you need to load a new level.

Perhaps something like;

using UnityEngine;

public class Player : MonoBehaviour
{
    private LevelManager _levelManager = null;
    private int _lives = 0;

    private void Start()
    {
        _levelManager = (LevelManager)GameObject.FindObjectOfType<LevelManager>();
        _lives = 3;
    }

    public void LoseLife()
    {
        _lives--;

        CheckLivesRemaining();
    }

    private void CheckLivesRemaining()
    {
        if(_lives <= 0)
        {
            _levelManager.LoadLevel("Lose");
        }
    }
}

You can attach this, or something similar, to a GameObject in your scene, perhaps named Player.

In your LoseCollider, get a reference to the Player GameObject, just as you do with LevelManager and then you can just call the LoseLife method.

The above will provide the player with 3 lives for each scene that it is attached to, if you wanted to persist the number of lives the player has, throughout the game then we would need to make a couple of changes again.

Note, this is a fairly quick response and not thoroughly thought out, for example, you may find in some cases you are dragging and dropping a LevelManager reference into objects, and in others, like the above, you are finding it.

If I were personally approaching this I would spend a little more time on the over-all design, probably creating a GameManager class, which would contain the references to the other specific things that I needed, the Player, the LevelManager and so on.

The main point of my reply was to try to get across the separation of responsibility, e.g. the LevelManager cares about loading levels, the Player cares about how many lives it has.

Hope the above is of use. Code un-tested but hopefully shouldn’t be too far out. :slight_smile:


Updated Wed Nov 22 2017 22:48

Sorry, whilst I was writing this you added your other posts, this was in relation to your first post :slight_smile:

1 Like

Hello again :slight_smile:

Fairly good definition of static members and static classes here:

1 Like

Ok, I appreciate that answer, and I agree with creating another class for the player. I think that would organize cleaner as well. I will do some research on your reference.

Back to your code example, you created an instance of LevelManager and then assigned it to the object LevelManager. Doing this now allows you to call on all non-static members in LevelManager.

If you were to make LoadLevel a static member of LevelManager, you wouldn’t have to do that, you just just call on it.

Do you have a preference and what drives your decision between these two ways to access members?

Exactly - spot on :slight_smile:

I wouldn’t say preferences no, I don’t tend to create many static classes, and dont use static members that often either, but a lot of this may come down to the specific projects/solutions.

As you will see from the link, a good use of a static class, which will only have static members, would be for performing perhaps some calculations, where the input parameters do not vary, although the values may.

So, calculating the area of a shape based on width and height for example.

Looking at Unity, Mathf is a static struct which has a set of static members, Min, Max,Round` etc etc - these will never change.

Looking at BlockBreaker, if LevelManager was only responsible for the loading of levels, this may be a suitable contender also, but I wouldn’t want to be adding code to it about Bricks etc.

Just had a quick play, of course, static classes cannot inherit from anything other than object, thus, you cannot inherit from MonoBehaviour.


See also;

  • Unity - Scripting API : Mathf

And there is the rub. I like the idea of LevelManager being static, but then I can’t use it in the game for buttons and such. Thanks for the link on statics, that helped a lot.

1 Like

For non-scene stuff there you may find more of a benefit.

For the most part, I tend to have classes which reflect a thing, holding data, and offering behaviours for that thing.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms