I have worked with Unity for quite some time and have a good grasp of the fundamentals of OOP and coding practices related to Unity. I also have an understanding of design patterns.
I have never worked with large projects or projects involving intricate code architecture.
My current project is a 2d Platform game in which the player moves a chameleon around the level to catch prey and avoid enemies and other obstacles.
Each character can have states such as idle, run, jump, patrol, eat etc.
Each character has its own level navigation(movement), physics properties and math calculations, sfx trigger logic etc.
Characters need to communicate with each other (Ex: If the chameleon catches the prey, it need to change the prey’s state)
Some characters use special components such as line renderer which depends on the physics properties.
My goal is to create a flexible character system. Normally I would do something like having a script such as ChameleonBehavior, MonkeyBehavior etc for each character. They will be monobehaviors attached to the main gameobject/prefab and controls all the inputs and actions that a character can perform.
While this works, it will have a ton of dependencies to other scripts. Say polling for input, calling sfx on a soundmanager etc. Or if I create a LevelController class to take out certain dependencies, the dependencies move here.
I’d use Event-delegates and ‘Advanced C# messenger’ to mitigate several dependencies. But it still feels like I’m broadcasting a lot of messages which are aimed at specific object, which is counter-intuitive because its a global broadcast, and any script can catch any message.
Anyone with experience in designing such a system, please advise on a good approach.
I don’t know what will end up working with you, my way is not perfect, I’m just going to outline the approach I take to it. Hopefully you can get some ideas.
The way I go about it is I first structure out my various mobs into what I call an ‘Entity’.
All my entities seldom are just a single GameObject, but rather a hierarchy of GameObjects with a root GameObject and several child GameObjects that might be its graphics, its collision, etc. Furthermore, I started using these child GameObject for organizing all my scripts. I’ll have a child GameObject called ‘AI’ that holds all AI related scripts… and another called ‘CombatMotor’ that holds combat related scripts:
(note, picture is a little old… just reusing stuff I have sitting in a folder)
The ‘root’ of this entity I usually tag as ‘root’, and I attach a generalized script I call ‘SPEntity’:
You can of course inherit from it to add functionality, lets make a general purpose mob entity type:
public class MobEntity : SPEntity
{
public HealthStats Health;
public AIBrain Brain;
}
And most importantly I have a way to search all entities in the scene, as well as get the entity script for a given GameObject, no matter where in the hierarchy it is.
Now, lets say I have a ‘OnTriggerEnter’ script:
public class SomeBehaviour : MonoBehaviour
{
void OnTriggerEnter(Collider other)
{
var entity = SPEntity.GetEntityFromSource<MobEntity>(other);
if(entity == null) return; //collider is not part of some mob entity
//do stuff with the entity
}
}
Now I have a reference to the entity that entered the trigger. In this case all MobEntities should have some Health and a Brain, so you can easily grab access to them from this.
You could also search its hierarchy for potential scripts that may or may not exist on them using ‘GetComponentInChildren’. If nothing comes back, it doesn’t have that.
public class SomeBehaviour : MonoBehaviour
{
void OnTriggerEnter(Collider other)
{
var entity = SPEntity.GetEntityFromSource<MobEntity>(other);
if(entity == null) return; //collider is not part of some mob entity
var weapon = entity.gameObject.GetComponentInChildren<IWeapon>();
if(weapon == null) return; //this enemy doesn't have a weapon
}
}
Furthermore I often generalize my motor scripts to be modular.
Here for example is the extremely complicated movement style of the player in one of my games I’ve been working on for a long time. I have generalized a lot of her movement into multiple scripts that can be added or removed as the game progresses (turned on or off). As well as reused on other mobs that may have similar movement to the player, minus certain features.
For example, the ‘MovingPlatformResovler’ is used by a LOT of entities through out the game. The script is generalized to work with any entity… regardless of who:
I found your framework interesting. You approach to designing a character system is very detailed and is more accommodating for additively adding new behavior.
An example problem from my game:
Say there is a script called TongueMotor, it is attached to a child of the main chameleon gameobject. It controls various aspect related to the Chameleon’s tongue including rendering, physics, sound etc.
I want the user input/AI bot controller tell the tongue to be thrown in a particular direction.
The tongue does the calculations and say either: Hey I can latch onto a collider, here’s the location I’m moving to (OR) Nope! I can’t get there, sorry. I’m staying right here.
The response goes back and the source caller may or may not take an action.
I haven’t understood completely how your system works, so I apologize in advance if I say something wrong. Please do correct me in this case.
Your system seems to be follow the ‘Facade’ and ‘Chain of Responsibility’ design patterns.
My question is how do the sub-system scripts communicate between themselves. Do they use methods defined by SPEntity such as SPEntity.GetEntityFromSource ()
Furthermore, how do the scripts on the root (Ex: Chameleon Script, Monkey Script) communicate between each other(of course without any dependency between themselves).
I’d really love to see a basic version your framework in a unity project! Do you mind sharing a link/asset store download to an example project?
Btw, for the time being I’m using this as a base for my code:
Thanks for your well detailed response complete with screenshots and everything. Appreciate your effort! I hope to switch to your framework soon.
the scripts wouldn’t necessarily be on the root… like I showed, you can have the scripts anywhere in the entity.
They communicate by grabbing references by searching the entire entity (use GetComponentInChildren, or GetComponentsInChildren, though I use a ‘FindComponent’ function I created before those methods existed respectively).
You can use interfaces to generalize those scripts. Maybe you have an ‘IWeapon’ or ‘IAction’ interface that defines the general shape of those scripts, then each entity can implement their own specific version. Then some ‘AIBrain’ just cares that a IWeapon or IAction is defined for itself.
You can of course use properties to reference the scripts in the entity. The code in the script only has to be generalized, the structure of an entity is concrete.
I demonstrated the generalizing with the MovingPlatformResolver. It has no dependency to a specific entity, instead it just assumes there is an Entity script and a ApocMovementMotor (a very generalized motor, similar to CharacterController, that supports Rigidbody as well… you could conceptually think of them as one and the same). An entity that ‘moves’ on a platform would have a movement motor, so this level of dependency is expected… but no more than that bare minimum of dependency.
I of course go with a very extreme level of granularity in the dependency in my framework, but that’s merely because it’s intended to be more general purpose. You don’t need such a granular level, but the general idea is there.
The fundamental aspect of the structure IMO is the entire ‘entity’ aspect, creating that root GameObject and script to identify that entity as a whole. What you decide to place in that script to map out your entities is up to you… do all mobs have AI brains? Have an AI brain property on the entity script. Do all mobs have movement motors? Have a movement motor property on the entity script. Otherwise, you can search the entity hierarchy for scripts and react accordingly if the script exists or not.
Sorry, I do not have any basic versions as a unitypackage or anything available. I was more or less outlining my general approach to it. Haven’t really had the time to actually put a package together aside from sharing a large portion of the framework on github… and it’s technically not even the entire framework as you can see (SpacepuppyBase being what’s on github):