singles scripts vs multiple scripts

hey, im looking for a good work flow, i have watch quiet few video tutorials and there are alot of different workflows i have noticed . i want to know which is best?

  • use one script that controls everything like for example a game manager that controls the score, player health, gui, npc
    like a singleton

or

  • multiple scripts like have one for character movement and character health a different script for gui and score and so on

what i notice about the multiple scripts is that when you want to make all the scripts communicate with each other i keep having to call the getComponent<> should i just make all the scripts static? so i can have a singleton of each and just call them directly

which method is better?

also another thing i have noticed is no one uses encapsulation? is it not good practice in unity? what are the standards when it comes to it?

The way I prefer is having a gamemanager gameobject, and have a script for healt, one for score etc.

Generally lots of small scripts that do one thing really well. Yes, you have to do “GetComponent” which is cumbersome, but I usually do that during the Start() function.

With regard to encapsulation, if you are referring to fields and properties, Unity’s Inspector cannot see properties, only public fields, so people don’t encapsulate all that much. For me, generally I create both a field that can be set in the editor, and a property in the code which can be set by a programmer and is set in the Awake() function by copying the field value to the property value. I do this if, and only if, the values of the field property need to be sanity checked for valid ranges or other pre-computation needs to take place once the field value has been set up, e.g. calculating the absolute world position of something from the relative position, which I need to do for the paths in the example given below.

One of the small projects that is linked in my signature breaks down something like the layout below. I only have one “singleton” class in my entire game and that is just purely because almost all scripts need to reference it, so it maintains a static reference.

Unity Structure For “The Core”
Player Prefab

  • Player Input Controller

Reads the keyboard and mouse, calls the appropriate functions on the Player Movement Controller and the Player Fire Controller. Absolutely no logic in this module determines whether the player can move, it merely translates input to movement commands. (~55 lines of code)

  • Player Movement Controller

Determines the speed at which the player can move, whether they can move, how they should move. (~115 lines of code)

  • Player Fire Control

Determines the speed at which the player can fire, whether they can fire, how many shots can exist at once, and instantiates new projectiles. (~90 lines of code)

  • Player Zapper Control

Handles the super zapper recharging, fires the super zapper, generates visual and audible effects. (~40 lines of code)

Standard Projectile Prefab

  • Follow Path

Follows a pre-determined path (~163 lines of code)

  • Standard Projectile

Handles the collision between the projectile and an enemy, destroys itself when it leaves the playfield, plays sound effects. (~75 lines of code)

Enemy Fodder Prefab
Your standard enemy to be mown down in large numbers by the player wantonly shooting at them. There’s nothing special about these guys, they just die.

  • Follow Path

Follows a pre-determined path. The same script exists on all types of enemies and all projectiles that can follow a path. (~163 lines of code)

  • Enemy Fodder

Handles collision with a projectile, determines what happens when the enemy reaches the player’s base. (~79 lines of code)

CPU

  • Path Controller

Stores a list of paths that projectiles and enemies can travel along. (~48 lines of code)

  • Firing Position Controller

Calculates the firing positions that the player can move to. Draws a gizmo on the screen to represent where those positions are at edit time. (~114 lines of code)

  • Enemy Spawn Controller

Spawns waves of enemies for the player to shoot. (~250 lines of code)

  • Bonus Spawn Controller

Spawns bonuses that the player can pick up or shoot. (~200 lines of code)

Camera

  • Follow Player

Tracks the player and moves to pre-calculated positions based on the player’s firing position. Displays an editor gizmo to show where the tracking positions will appear. (~120 lines of code)

Game Controller game object
This game object exists only in the scene and lasts for as long as the game is in progress. Tracks lives, score, and other types of data that must be persistent from one level of the game to the next.

  • Lives Controller

Tracks the player’s lives, increments up when they collect a free life bonus, decrements down when they get killed. (~65 lines of code)

  • Level Controller

Tracks the current level, increments up when all of the enemies for the current level have been killed. (~36 lines of code)

  • Score Controller

Tracks the player’s current score, totals up the score and any bonuses and multipliers, also increments the score over time by LERPing between two values so that the on-screen GUI score does not instantly jump from the old value to new value whenever something is killed. This LERPing score adds more dynamic feedback to the GUI. (~90 lines of code)

  • Game Controller

Enables and disables the enemies, player input and other systems at appropriate times such as during a cut-scene or when the player has died. Handles the player reaching the base, game over, and level transitions. (~200 lines of code)

  • In Game HUD

Displays the in-game HUD to the user consisting of score, level #, lives, enemies remaining, etc. Only one in-game GUI script exists for the HUD to save on script calls. Individual game play elements such as bonuses and enemies handle their own GUI rendering. (~60 lines of code)

Below is the editing environment showing the firing position and camera tracking gizmos, along with a parallax field and a selected path.

1 Like

Thanks for writing that Justin, it is very interesting to see how people actually structure their games.

When I first started with Unity I was following the traditional method with how I designed classes that I had used over many years with some very big classes and lots of inheritance. The “Unity way” seems to be for small scripts that do just one or two things, and you attach these small scripts to your game objects, usually a few at a time. My most complex game objects to date have had 11 scripts on them, but usually it is between 3 and 5 scripts. Most of the scripts are under a few hundred lines too. My most complex script to date is probably around 2,000 lines, with three levels of inheritance in it, the 2,000 lines including all the code in the inheritances, but it is that way because I wanted a versatile and robust script that could be re-used on many other games.

Looking through some of the plugins you can buy on the asset store, those scripts can get large, but they are attempting to solve problems in a generic way and be user friendly with it, so there’s a lot of sanity checking in the code for rogue values or protecting the user from themselves. Game specific scripts do not need to be that way.

A lot of the power of small scripts attached to game objects and prefabs seems to come through good use of inspector values, and the occasional editor script (that is usually quite large) to manipulate scene data.

One word of warning: The OnGUI() function is pretty expensive, even if it does nothing. So a function like

function OnGUI(){
if(blah){
//do something
}
}

Will be expensive even if blah is false. In that case you should have one or two GameObject with a script dedicated to doing GUI stuff throu only one OnGUI() function. Everything that needs to have a GUI should tell the GUI object what to draw, rather than drawing a GUI itself.

With that said my setup usually consost of several scripts, one of them being some sort of “master” script (or manager as I call it). For example my player can use power ups, walk, swim and climb. Each of those are their own script and the manager script is the one that decides which script to activate. So if I find a power up, then the manager is the one to decide what will happen to my player. If I jump into a pool of water, then the manager makes sure to turn the walking script off and set the swimming script p the proper way. If my player dies the manager script deals with it.

The benefit is that I in each script I can concentrate on what is actually important. I don’t have to worry about power ups in my swimming script. Also, if I need to fix something about, say swimming, I don’t have to dig through tons of code that have nothing to do with it. Everything is easier to maintain (and time is the most valuable ressource you have)

Another note is that the GetComponent() function is expensive as well. If you want to reference a certain script vey often, then you should cache it. Let me give you an example: This will be somewhere at the beginning of the manager script:

var walkingScript: PlayerWalkingScript;
var swimmingScript: PlayerSwimmingScript
var boomerangScript: PlayerUseBoomerangScript;
var myTransform: Transform;

function Awake(){
	walkingScript = GetComponent.<PlayerWalkingScript>();
	swimmingScript = GetComponent.<PlayerSwimmingScript>();
	boomerangScript = GetComponent.<>(PlayerUseBoomerangScript);
	myTransform = transform;
}

Now every time you refernce one of those you use the variable. This also applies for the transform, rigibody and any other component that has some sort of shortcut. Using

transform.position = Verctor3.zero;

is EXACTLY the same as using (and thus just as expensive)

GetComponent.<Transform>().position = Verctor3.zero;