A Little Frustration
I took a break from this project out of misplaced frustration (and increased work load). What triggered this frustration was a misunderstanding I had about how Unity GameObjects were intended to be used.I had originally planned to have an inheritance structure between my GameObjects that went GameObject > Mobile > UnitMobile. However, when I went to implement the Interact (movement) action in my Mobile base class I had to create the inheritance. And to my annoyance Unity sealed the GameObject class which means it cannot be inherited. Bummer.
Coming back to the project several weeks later I decided to rework my design which led me to, at first, implementing a MobileInterfce interface which UnitMobile, extending MonoBehaviour, would implement. This had the benefit that MobileInterface could not be instantiated itself and would force UnitMobile to implement all methods that are necessary for it to work. However, this could lead to code duplication as I implement the Mobile interface for other classes. Then, thanks to a remark from a colleague, the idea of using Abstracts for the Behaviour was created. This satisfies all my requirements better than my original design. It allows me to implement base methods (such as Move) in the Mobile abstract class while requiring the extended class to implement all abstract methods (such as the InteractWith method) which must exist due to coupling between the UnitManager and UnitMobile classes. (I know coupling is usually a bad thing but direct method calls are far cheaper then a message system).
So, in summary, my misunderstanding was that I didn’t know Unity’s intent with the GameObject. Unity intends everything that exists in a scene to be just a GameObject. This means no casting or type checking is needed when working with GameObjects. When you need to interact with the behaviours/components of a GameObject you can get their instances from the GameObject directly. Then these behaviors/components are actually the driving pieces of the system and the game objects themselves are just, in essence, decoration as well as a container for instance objects (scripts and components).
Overview Of Changes
- I recreated the base scene so I could be sure that it’s easy to to implement the ClickController and Managers.
- I changed my naming prefix convention for my base empty GameObjects:
- ~ Managers and Controllers
- + GameObject, Cameras and Lights
- Changed my approach to the Mobile/UnitMoible relationship. I now use an Abstract class for Mobile.
- Added handling for interactions (default right click) into the UnitManager and Mobile classes.
- The Unit manager will issue an Interact command to all selected UnitMobiles
- The Unit manager will issue an InteractedWith command to any UnitMobile that was clicked when issuing the command.
- Implemented a simple Move method in the Mobile class which sets a target to move to.
- Implemented the FixedUpdate() method which will move toward the set target by looking at it and applying force to to move it forward. It will only do this if target is set and it is not within its set deadzone to that target.
- I added the GNU Licence to all source files. I chose this specific license because I’d prefer people not to have people make money off of my work without my consent. The intent of this project is for me and others to learn and contribute.
- Using a Mono profile I changed the coding standards to more conforme to my personal tastes. I included this profile if anyone else likes it and so I don’t have to recreate it if I format my system later.
Specifics
The Abstract Mobile
Here are the two defaults for speed and deadRange. For those, like me, who hate seeing Magic Numbers inside code I will be moving these to a cosntants solution once I find one that I like in Unity. Chances are this will be a manager class that extends MonoBehaviour so I can change the constants from Unity.
We want each mobile to handle an InteractWith action as it needs to exist to satisfy the coupling between the UnitManager UnitMobile
A Move is issued from the UnitManager and simply sets a target for each selected UnitMobile. The actual code that moves the unit is in FixedUpdate.
This is pretty straight forward. If target is set and we’re not too close to it (defined by deadRange) look at it and apply force forward.
UnitMobile
The InteractWith method must be implemented even if it’s just a stub as the UnitManager assumes it exists in all UnitMobiles.
UnitManager
If we have an Interact action (Right Click) tell our selected units and the GameObject that was clicked on.
Calls the Move method of each selected UnitMobile. Note that in the future I see this interaction being abstracted as the UnitMobile will have multiple responses to an Interact action depending on context (such as attack, patrol, repair, etc.)
We first get the UnitMobile compnent of the GameObject. If it did have a UnitMobile component then we call the InteractWith method on it, otherwise its not something we know how to deal with so we don’t do anything with it.
Other Notes
I’ve been thinking about the abstraction layer (mentioned in the note regarding the InteractWithSelectedUnits method mentioned above). My current thoughts are to create a Handler/Message system that is coupled with specific unit types. Then a Unit would subscribe (depending on the type of unit) to any handler that it can perform. The UnitManager would then tell the handlers to pass along the message to a specific UnitMobile and if that instance is subscribed to the handler the message will be passed. This removes the coupling between the UnitMobile and the UnitManager but does create a coupling between these handlers and their specific unit types. I’ll have to sit down and work though a few use cases but in the mean time if anyone who reads this has feedback about it leave it below.[Update] Next Steps: Simple Pathfinding
The next step is to implement a primitive pathfinding system. I have looked into several boxed versions of AI and Pathfinding implementations as well as the premium NavMesh feature of Unity.I’m leaning toward implementing this myself for several reasons:
- I’ll have full control to implement and modify this however I want.
- It’ll be free.
- It’ll be fun! And I’ll get to learn a thing or two about Game AI.