Just find myself leaning towards a Component/Modular based approach to programming game systems in Unity.
e.g. A game object to represent a Unit, would have a Weapon component, Health component ect.
This is great for making varieties of Unit types and code reuse, but I find the linking code to get complex, e.g. the Unit has to check to see if it has health and a weapon to talk to it and ditto for the Weapon / Health to Unit.
The Awake functions end up being complex with lots of get component checks.
And that complexity increases if you want to pool existing resources for re-use, as the components need to be turned off and back on again and their states reset.
Is there a better way to do this kind of thing, any and all pointers, tutorial links welcome.
using UnityEngine;
[RequireComponent(typeof(Health))]
[RequireComponent(typeof(WeaponManager))]
public class Actor {
Health _health;
WeaponManager _weaponManager;
void Awake(){
_health = GetComponent<Health>();
_weaponManager = GetComponent<WeaponManager>();
}
}
I am also a huge fan of breaking things up into the smallest logical components - it makes debugging/modifying behavior much easier in the long run and also leads to significantly more reusable code.
The biggest suggestion I can make is to always treat components like “health” and “weapon” as dumb components. By that, I mean you’ll never call pooling operations on them directly. What I would do is have the highest level component (in this case Actor) call the appropriate cache/init methods on it’s sub components when the time is right.
The second piece of advice I can give is to not be afraid to split up the component setup into a tree of responsibility to lower individual component complexity. For example, let’s say we want to build a space ship. We may have the following components that all add up to make that space ship.
Ship
Thruster
Warp Drive
Laser Gun
Photon Torpedo Launcher
Health (hull strength)
Shield
Engine
These are just some basic components, you could have significantly more possible components that all add up to make tons of possible combination ships. Ship would end up being very complex if it was required to know about and manage all of these components - but what if we added some management classes?
Ship
Engine
Propulsion Manager
— Warp Drive
— Thruster
Weapon Systems
— Photon Torpedo Launcher
— Laser Gun
Defensive Systems
— Health
— Shield
Now no one component really has to know how to interact with too many individual components - need to modify the behavior of an individual weapon? Go to it’s class. Need to modify the behavior of how that weapon is used? Go to the WeaponSystems class.
You can obviously go even further and make those managers very dumb as well by putting most of the responsibility on the objects themselves. Weapons are a great example; instead of the WeaponSystems class having to know about different weapon types - what kind of slots those weapons can be equipped in, any limitations to those weapons… there’s no reason not to have the weapon itself handle that, and all weapons extend from a base class. As far as WeaponSystems is concerned, all weapons are the same - just with different parameters.
Set up correctly everything, including your GUI, can be very dumb - even in situations where it doesn’t seem likely (like vastly different weapons for example).
I don’t think it’s worth the time, but there’s no reason you couldn’t create some sort of “Add Component Dependency” wizard that generated the three required steps (RequireComponent, variable, cache in Awake) for whatever IDE you use.
Ie, for any vehicle, all I have to do is add a VehicleSystem script.
The vehicle script then goes through an initialisation process which loads an xml file which tells the script the vehicle type, from that it starts adding all the required scripts. As each script is added, it adds whatever it needs to add and so on. Means I dont need prefabs. All the variables required by the scripts are kept in the data classes
Just be careful to make sure you are not reimplementing the component approach that unity itself already does.
The more you can offload to Unity’s internal workings, the better.
You extend a custom attribute to a custom class and then you can access it so in theory could you could link your RequireComponent to a LinkedComponent Attribute that can access the RequiredComponent but use generics to bypass the cost of conversion?