In this section i will walk you through creating a custom system in CCBK. The system itself is not especially useful but it contains implementations of all the most important parts of the framework.
A completed, playable version can be found in the Custom folder of the CityBuilderManual project within the CCBK asset.
We will start off with a pretty basic building component.
All it will do is keep a building effective for a certain duration and then disrupt it until it is reset.
Before actually creating the component let’s add an interface for the component so we can easily add other components for the same purpose or switch out the implementation. This step can be skipped, using the component directly instead of an interface works too.
Next add a script that inherits from BuildingComponent and implements the interface.
As you can see i’ve added the reset logic and some GameObjects for visualization.
The Code is mandatory for building components and is used to identify the component in save/load.
We’ll add the save logic next, put the following at the end of your script.
As you can see saving usually contains a data class that stores all the runtime data of the component. The building component base class provides overridable save and load methods that are called by the building. Saving is optional, if the methods are not overridden or return empty not data will be saved.
The last thing we need is for the component to influence the buildings efficiency. We do this by implementing IEfficiencyFactor
A BuildingTrait is a special BuildingComponent that always creates a Reference an is stored in a special collection within the BuildingManager. This is done so they can be retrieved without having to iterate through all the buildings on the map. The interface for it looks like this.
The minimal implementation for the Trait is similar to a normal component.
The major difference to normal components is that we have to manage a reference throughout the lifecycle of the component. It has to be created when the building is build, passed on when it is replaced and removed when the building gets demolished.
The purpose of the trait is to spawn walkers so we will return to it after creating some custom walkers.
The first walker we will add is a roaming walker that resets every ICustomBuildingComponent it passes.
CCBK has a base class called BuildingComponentWalker for exactly this purpose.
Thats it, whenever the walker passes a ICustomBuildingComponent onComponentEntered will be called. The base class also provides onComponentRemaining in case you want to do something as every frame the component is in reach instead of just the first one.
The second most prevalent kind of walker in city builder are destination walkers.
The one we’ll add is fairly simple since it gets its path passed to it from outside.
It will follow that path it is given and call a method on its target when it is finished.
The _state variable is only added so the walker can be correctly restored when loading. The _target is stored so the walker can call DoSomething on it when it is reached.
The WalkerData field in the save data contains all the basic data of the walker like current position and path which enables you to just call continueWalk in Load.
To actually spawn walkers from buildings CCBK uses something called in WalkerSpawner.
These come in Cyclic, Pooled and Manual varieties and are serializable field that are added to building components.
In Unity versions prior to 2020.1 generic fields do not get serialized which which is why you will see concrete spawner implementations at the end of all the walker classes in CCBK.
If you are using such a version add the spawners to the walkers.
Now add the spawner to the building trait we created earlier.
Initialization and updating of the spawners is handled by the owning building component.
You might have noticed that StartWalker on the destination walker is not called here.
While that would be the easier thing to do we’ll move that responsibility out into a manager for the sake of this example.
Managers are central scripts meant to orchestrate different systematic behaviors. Once again we’ll start by creating an interface.
The manager will be responsible for calculating a total from the CustomValue field of our traits. It will also provide the paths for our destination walkers. Lastly it will be notified when oue of our traits gets built or demolished. Let’ look at the implementation.
To calculate the total value the manager pulls all the traits from the BuildingManager and applies a Multiplier. As mentioned before BuildingManager keeps track of all traits for quick retrieval.
For the path we just loop all traits and return the first one that PathHelper is able to calculate.
The Add and Remove Methods don’t have and function in this demonstration so we’ll just log to check if they work.
To be able to use the total value calculated by the manager in visuals and win conditions we will now create a score. Scores are pretty straightforward ScriptableObjects that are implemented like this.
To see the score add some kind of text to the scene in combination with a ScoreVisualizer. For the score to be calculated you need a ScoreCalculator in the scene. The ScoreCalculator pulls the Scores it has to calculated from a set of scores it retrieves from the Dependencies. To register your custom score for that add an ObjectRepository to the scene and give it a ScoreSet that contains the instance of score used in the visualizer.