In my game I have AI workers that can be assigned jobs like baker, blacksmith. They can also have jobs that include gathering resources from trees, stones, berries. They can also work as builders whom construct buildings.
In the State machine for these workers there are several scenarios where I need to access the worker’s current work place. Here is my issue, since currently the components looks like this:
Bakery, blacksmith, Inn etc. Have a WorkPlace component attached, these are jobs where the worker stays for a longer time.
Trees, stones, berries etc. Have a Gatherable component attached, these are jobs where the worker constanly changes back and forth between gatherables.
Buildings have a Constructable component attached, this is a job that is finished fairly quickly.
Currently a worker can only have one of the above assigned at a time, since they all count as a job, and their differences are too great to share a parent class. So my worker has:
public WorkPlace workPlace;
public Constructable constructionPlace;
public Gatherable gatherable;
This seems wrong since 2 of these will always be null. Should I replace these with a simple GameObject reference instead, and just use GetComponent when I need specific info from the different components?
I understand this can differ depending on the scenario, and while I use the above references a lot, its never in any type of loop.
If two of those are indeed always null i would consider them sharing a base class, like “WorkActivity”. This may as well be empty, but that wouldnt really gain you anything. I cant say too much about it since i dont know your code, but these look like they may share something like a Work() function, or some common functionality to get information about the activity. If you can gain any benefit from using a common base class, i would go that route.
If the base class would actually be empty, you may as well use a GameObject and see which of those it contains. To answer the main question: GetComponent is pretty fast. As long as you dont use Find() or any of its variations you are fine performance wise.
As a rule of thumb, only worry about performance problems once you can actually profile them. A lot of things you may worry about will never become a problem. And when they do, you can quickly find out which one it is with the profiler. Thus, generally always chose the path that is most readable / maintainable / easy to use in the future. Unless you know beforehand that something will be performance critical, like for example some compute/shaders or realtime mesh alteration scripts. But when attempting such a thing you usually know beforehand that they end up being expensive on the CPU/GPU.