I’m currently using a script full of properties to get all the commonly used components, but isn’t this bad from a performance standpoint? I have 18 different components in that script and that properties script is probably instantiated 18 times which means that the script performs an insane amount of GameObject.Find at start. Correct me if I’m wrong :).
Edit: I made the properties script inherit from monobehaviour and added it to the main camera, which works fine.
I always say (and not everyone agrees) that if you’re using GameObject.Find at all, there’s almost always a better way.
I’m not sure I follow what you mean when you say you have 18 different components in the script and the script is instantiated 18 times. Could you post the code you’re asking about?
Sounds like you want a singleton pattern here. The most primitive would be something like
public class Spawner : MonoBehaviour
{
public static Spawner script;
void Awake()
{
script = this;
}
public void Whatever()
{
}
}
... then just call Spawner.script.Whatever();
:
Then you can just have all your components on a manager object that control and do things for your game. These are only intended to exist once. The singleton pattern above is not bullet proof, it assumes you’re smart enough to not have more than one instance of it. I do recommend you go in via one door only though and that’s why script exists. It can also be called Instance or singleton, depending on what programmers you meet.
I have just changed all the scripts to work in a new way, so the old properties script is changed. The script used to be instantiated in a lot of scripts before, like this:
propertiesScript = new PropertiesScript();
PropertiesScript now looks like this:
using UnityEngine;
using System.Collections;
public class PropertiesScript : MonoBehaviour
{
private FollowPlayer followPlayerScript;
private Transform playerTransform;
private CapsuleCollider playerCollider;
private IndicatorPanel indicator;
private IndicatorPanelSecond indicatorSecond;
private PlayerStats playerStatsScript;
private PlayerAttack playerAttackScript;
private AllEnemies allEnemiesScript;
private ArrowIndicators arrowsIndicator;
private WakeEnemiesLevel1 wakeEnemiesScript;
private CoroutineMaster coroutineMasterScript;
private OccupiedNodes occupiedNodesList;
private PlayerGrid playerGridScript;
private PlayerInputs playerInputsScript;
private PlayerMove playerMoveScript;
private TellEnemiesToMove tellEnemiesToMoveScript;
private ProjectileManager projectileManagerScript;
private ActivateSpells activateSpellsScript;
void Start()
{
playerTransform = GameObject.Find("Player").transform;
playerCollider = GameObject.Find("Player").GetComponent<CapsuleCollider>();
indicator = GameObject.Find("Panel3").GetComponent<IndicatorPanel>();
indicatorSecond = GameObject.Find("Canvas").GetComponentInChildren<IndicatorPanelSecond>();
playerStatsScript = GameObject.Find("Player").GetComponent<PlayerStats>();
playerAttackScript = GameObject.Find("Player").GetComponent<PlayerAttack>();
allEnemiesScript = GameObject.Find("Main Camera").GetComponent<AllEnemies>();
arrowsIndicator = GameObject.Find("Panel3").GetComponent<ArrowIndicators>();
wakeEnemiesScript = GameObject.Find("Main Camera").GetComponent<WakeEnemiesLevel1>();
coroutineMasterScript = GameObject.Find("Main Camera").GetComponent<CoroutineMaster>();
occupiedNodesList = GameObject.Find("Main Camera").GetComponent<OccupiedNodes>();
playerGridScript = GameObject.Find("Player").GetComponent<PlayerGrid>();
playerInputsScript = GameObject.Find("Player").GetComponent<PlayerInputs>();
playerMoveScript = GameObject.Find("Player").GetComponent<PlayerMove>();
tellEnemiesToMoveScript = GameObject.Find("Player").GetComponent<TellEnemiesToMove>();
projectileManagerScript = GameObject.Find("Main Camera").GetComponent<ProjectileManager>();
activateSpellsScript = GameObject.Find("Main Camera").GetComponent<ActivateSpells>();
followPlayerScript = GameObject.Find("Main Camera").GetComponent<FollowPlayer>();
}
public FollowPlayer FollowPlayerScript
{
get
{
return followPlayerScript;
}
}
public ActivateSpells ActivateSpellsScript
{
get
{
return activateSpellsScript;
}
}
public ProjectileManager ProjectileManagerScript
{
get
{
return projectileManagerScript;
}
}
public TellEnemiesToMove TellEnemiesToMoveScript
{
get
{
return tellEnemiesToMoveScript;
}
}
public PlayerMove PlayerMoveScript
{
get
{
return playerMoveScript;
}
}
public PlayerInputs PlayerInputsScript
{
get
{
return playerInputsScript;
}
}
public PlayerGrid PlayerGridScript
{
get
{
return playerGridScript;
}
}
public OccupiedNodes OccupiedNodesList
{
get
{
return occupiedNodesList;
}
}
public CoroutineMaster CoroutineMasterScript
{
get
{
return coroutineMasterScript;
}
}
public WakeEnemiesLevel1 WakeEnemiesScript
{
get
{
return wakeEnemiesScript;
}
}
public ArrowIndicators ArrowsIndicator
{
get
{
return arrowsIndicator;
}
}
public AllEnemies AllEnemiesScript
{
get
{
return allEnemiesScript;
}
}
public IndicatorPanelSecond IndicatorSecond
{
get
{
return indicatorSecond;
}
}
public PlayerAttack PlayerAttackScript
{
get
{
return playerAttackScript;
}
}
public IndicatorPanel Indicator
{
get
{
return indicator;
}
}
public PlayerStats PlayerStatsScript
{
get
{
return playerStatsScript;
}
}
public Transform PlayerTransform
{
get
{
return playerTransform;
}
}
public CapsuleCollider PlayerCollider
{
get
{
return playerCollider;
}
}
}
Edit: I could just make the script variables public and drag them there but I prefer to do it in a script :).
Well I’ve used that pattern in all my shipped titles and we don’t have any known bugs. Your method, if it works well for you, is fine.
Programming is an elitist thing, everyone thinks their way is best, and that’s fine. You use what gets you results. It only matters if you’re working with a lot of other programmers overlapping the same code areas, it’s then you need to start bullet proofing things and adhering to standards and conventions.
It’s not so much that “it creates bugs”, but more that if you use it when you shouldn’t, you can code yourself into a corner. For example, if you create a singleton to point to your Player, use it liberally, and later decide to add multiplayer support, you’ll have to change a lot of code.
That said, the exact same issue always applies to using GameObject.Find instead of a singleton, and GameObject.Find comes with its own potential bugs (what happens if you rename “Panel3” later? Or create a second object elsewhere in the scene named “Panel3”?), and the performance issues. In other words, there really isn’t a scenario where GameObject.Find is a favorable alternative to a singleton.
I discussed this a little more in depth in another recent forum post here; the last comment in particular you may find useful, as it offers a way to avoid the common pitfalls of using singletons.
If it works for you, then cool. Just be aware it’s probably going to be high maintenance. It’s going to be something that you have to come back to and possibly change it or reference it to change something else. As a general practice, this is usually avoided since most programmers would rather compartment code so that they can be “finished” with it and won’t have to look at it again (at least until they run into problems).
Ofc it only “creates” bugs if you’re making mistakes, but since I’m new to programming and still change a lot of my code on a regular basis I will skip it for now, especially since I would like to implement multiplayer into my game in the future. I will make sure to replace the GameObject.Find by creating public variables - to attach the scripts in the inspector. Thanks for your reply.
No, but most other methods require seriously understanding architecture to know the best ways to communicate between components/gameobjects. Using the singleton pattern just to enable scripts to communicate is a poor idea. The best use case for singletons is when you need one and only one instance and you are aware of that explicitly. Otherwise it can become very likely that you regret using singletons as a shortcut.
Thanks for replying. So many different opinions :). Do you mind just mentioning another alternative? Even if I can’t grasp it now I will bookmark it for when I have improved.
So if you want a manager class but you are not sure if you will have more than one of them, make another manager class (singleton) and manage it from there.
example player: create a player class with all necessary information.
Then create a GameState class which implements the singleton pattern. Inside the GameState class you can hold a list of all players.
then you can access the player you want like this:
int currentPlayerIndex = 0; // example value
Player currentPlayer = GameState.Instance.Players[currentPlayerIndex];