I’ve recently begun to learn more about the interesting nature of ScriptableObjects in Unity thanks to this wonderful Unity talk:
Up until this point I’ve been applying regular Software Design paradigms to solve my problems. Wanting to take a fresh look at the stuff I had done in a previous project I wanted to see what my board game would look like had I chosen to design the internals differently.
Surely the use of ScriptableObjects would have made the code base less rigid and would have enabled me to more freely swap out code belonging to a specific component at any given time without impact the other systems that much.
Wanting to see if and how I could implement this approach with my game board I came across two problems:
Who holds the GameBoard ‘reference’ ? If I am to shy away from using singleton patterns the closest approach would seem to be to implement a kind of messaging system that would simply tell the board that it wants an interaction. To put it in more concrete terms: If I want a gameTile to notify the board of changes it would simply emit a custom ‘onChange’ event like described in the talk. This does enable me to signal the board for changes but what if I need to communicate the other way around?
How does the GameBoard reference the tiles?
Would anyone care to share their experiences and ideas on how they went about implementing their GameBoards? Any things they tried and succeeded (or failed) with and what they learned from that? I’m wildly curious to see what you guys have come up with.
Hey sorry if I didn’t make that clear enough. By GameBoard I refer to the BoardController which houses the logic for all tiles in general (which is a pattern in and of itself I suppose). In an overly simple example it would be something like:
public class Board : MonoBehaviour {
// Public array containing all tiles
public GameObject tiles;
public GameObject getTile(int x, int y)
{
for (int i = 0; i < tiles.length; i++)
{
if (tiles[i].transform.x == x && tiles[i].transform.y == y) return tiles[i];
}
}
/// followed by some other specialised logic.
}
Naturally this wouldn’t necessarily be the most efficient method. There are probably dozens of different way to approach this depending on the scale and complexity of the game.
If you want something to picture for my specific case it’s akin to the new Civilisation game where tiles can be developed to become districts:
I’d say that the answere is in the video, just watch at around 14:00 again. He is talking about dependency injection for instance.
I watched it just a bit but I get two ideas here:
Do not use Singletons
Make use of “StaticData”.
Scriptable objects are only the vehicle here but StaticData is just data that you use to configure your game and which values don’t change. Those are part of the rules like maxhealth, buildign cost and the like. You can use Scriptable objects, make or use a game data editor or load it in from another source, I use xml for instance.
It seems that he goes beyond that topic as he starts using Player current health in a scriptable object. But this is just some way to use the Unity editor interface. The design pattern behind is just the model in an MVC pattern.
If you make a consistent MVC pattern than scriptable objects (stats) would be your model and you can share the model with your UI, and Controllers.
The overall idea is interesting and might be great for some kind of development processes. On the other hand if you are making an RTS and all entity variables are their own class instances then this should create an overhead and garbage.
So as explained above, the BoardController would always know about its tiles which are a model.
You should not (only) store the GameObject of your tile but also it’s model class in an array.
Maybe you have a seperate Board and/or Tile View class that knows about game objects and all visual related things.
It is rather the worst approach. Just store tiles in a 2D array
public GameObject getTile(int x, int y){
return tiles[x,y];
}
I understood his point about dependency injection but there are many ways you could do that within the Unity framework. If I were using regular code I’d simply inject everything an object needs when it is constructed using it’s constructor. (unless this dependency is dynamic)
Say I want to already put down the tiles for the board manually how would I inject the dependencies during runtime? give each Tile a public reference to the board so it can be set in the inspector? It seemed to me that the point of the talk was to avoid such GameObject dependencies and instead attempt to use a shared state ScriptableObject since it would ‘always’ be there without needing to drag and drop the ManagerClass X,Y and Z since they all somehow need each other.
Maybe I lost sight of this comparison to MVC, the way you pointed it out seems pretty obvious for UI related stuff.
When I think about it in GameObjects though would you say it’s fair to see them as the View? (Mesh data and shaders etc.)
I felt like that would be the case too but he references an RTS game his studio made using this approach and claims it works rather well.
My take away from this is that you would say that ScriptableObjects are an interesting approach but recommend sticking to regular SoftwareDesign patterns in general?
I am not much of a software engineer and I only know what works best for me.
I watched the video for a little longer and I think I get the idea behind.
The first question you have to ask yourself is do you actually have a problem that needs to be solved? And second do you see any benefit of that other workflow for yourself?
One thing again, this is not a pattern in my view, it rather is a Unity editor specific workflow. The important note is that you have models (scriptable objects) that you can share with other objects easily and which do not rely on other objects.
Speaking of your BoardTies there are a number of ways but I would avoid manually dragging each tile into the board manager, this is useless work which you want to avoid with your workflow.
You cann add all tiles under a transform, e.g. board manager and let it collect all tiles on start.
But I think here we can see the benefit: Instead of using BoardManager you could also just add a LoadTiles scrip which would reference only to the scriptable object holding all tiles and would load in all of it’s children into it.
Of course. Most game objects and standard components are views I would say. Except things like rigidbodies that actually affect the game.