Typically i like my data to be centralised in some kinda manager rather than each object having its own data. That way GameObjects are just visually representations of the data and the manager controls the link between the two, such that if i delete some data in the manager, it will destroy the associated game objects etc.
The problem i find in unity is this approach seems rather difficult when wanting to design levels in editor. Because data has to be added to a manager and then instantiates relevant game objects, but the managers only really work and make sense at run time not in editor.
How does everyone seperate their data from game objects in a clean way like this and still find an easy way to design levels? It feels like an uphill battle to do that in Unity, and whilst DOTS seems like a solution, the whole API for that is just a headache and not easy to get into so i donāt particularly have much interest in DOTS for time being.
Game Objects are data containers, so separating data from GameObjects seems a bit odd!
Generally if you want to be more data oriented, but still live in a GameObject paradigm so you donāt have to live with the abstraction olympics that is ECS, you use your MonoBehaviours as just data containers (ie. just serialized fields, no Update calls), which would register themselves (or be collected) into some sort of central data lookup, and then you have systems that act on that data, which you can spin up either using some kind of auto-instantiating singleton, or the PlayerLoop, or something like that.
Data does not belong into a āmanagerā. A manager āmanagesā (whatever that means, itās an extremely vague term) but data ābelongsā or in other words, data demands a collection type. A database even.
One great way to design and store data in Unity is to put your data container class (ie a struct with various fields but minimal to no logic) in a ScriptableObject asset. This is edit-time data. Every time the game launches, that data will have the values you set in the editor.
Then, to make that data persist, youād simply load/save that SO struct at runtime perhaps as json file, so thereās no need to serialize the SO itself but rather the SO will start life using the design-time values and at any time you can request it to load any persisted runtime values.
If you need a different set of ādatasā for each scene (levels), then you could have an editor script that creates this SO asset for you when you start creating a new level. This would also associated the SO with the level, usually by assigning it to a MonoBehaviour in the scene and yes, that then could be your āmanagerā type.
But donāt call it DataManager ⦠this would rank among the most nondescript names I can think of.
I also donāt name my classes SpaghettiScript or GameBehaviour or hey, why not have a SingletonManager since thereās so many of them. Iām a stickler for good names (actually: meaningfully defined purpose such that the name of the type derives easily from that) so please excuse my rant.
Since you remain very vague in your description or the purpose of this manager class, I donāt see why this would be a problem. Thereās a clear deliniation: either you need something to work in the editor or at runtime. Ideally these should still be separate types albeit the editor script depending on and interacting with the runtime script.
If this distinction isnāt made clear, for instance by throwing public runtime and editor methods into the same class, you are going to set yourself up for big headaches no matter what the purpose of the class may be. If this trickles outwards, say your DataManager has some editor-only methods that other code must either enclose in #if UNITY_EDITOR or the enclosing happens within DataManager but thereās no clear indication that this method wonāt do anything at runtime, this spells trouble.
Not sure if this is what you meant with āmakes sense only at runtimeā. Iām actually seeing the opposite most of the time.
It depends on what you mean by ādesign levelsā. This could be a tile-based level on a small grid, or a streaming open world terrain. And a gazillion other ways of designing levels.
But for the most part it boils down to defining what data you need, and what part of the data will be const meaning it can only be designed in the editor and never changes at runtime. The other types of data are either values that can change at runtime but need to be reset on occassion (ie start of game/level), and those that reflect progression (ie experience points, score) and should be persisted between runs/levels.
Coming back at this I wonder if perhaps you have a double-edged sword. In one way, youāre changing data which changes the level. But you also want to design the level, which means creating new entries or perhaps also altering data.
Is that the kind of problem space youāre in?
It would be best if the system primarily worked in one or the other direction. Ie you can generate a level from your data, and you can continue to edit the level through the data. Say for simplicity you have an array in a SO which represents the tiles on the grid and (ignoring how awkward it would be to edit) then this would generate your tiles in the scene.
But when you also edit these tiles by manually placing objects onto them, youāve created yourself a problem. Actually itās the same problem as having a code generator, and then manually making edits to generated scripts.
I think you need to be more specific as to what kind of levels you are designing and the specific ways this is failing on you. Iām sure we can give some good practical advice, but as it stands the problem space is far too vague.
^ Iāve definitely used this approach, ranging from the trivial to the complex, such as tools that let me emplace level geometry and parts of a level via a PNG file (special-colored pixels) and then bake it into a GameObject collection that I can further edit. I like this, itās great for gamejams, but it is also a step-destructive process, mostly, but it can be made to be back-and-forth lossless with some work. An example of my PNG-to-GameObject process can be found here:
Colors are specified as a particular RGB (see the B2GConfig class) and considered as a collection to find the best match, precluding errors from slight color changes / tints.
^ This seems to be the one way that scales the best, but it also saddles you with the task of maintaining whatever editor you make AND your game⦠twice the work! But like I said, with proper design, it scales almost infinitely. And again with proper design, you share a lot of common functionality and your editor becomes simultaneously a testbed for level design / layout.
But it lets you ālive closer to the dataā so that you can edit the data representation (perhaps JSON?) directly, reading / writing it while simultaneously using the same code your game would use to visualize it, turning it into GameObjects, etc. to be seen, either in the editor or in the game itself, even do gross validations on it, like verifying that every level has a player start and player exit and a boundary.
And you can keep layering validation on top of that, and when your game grows far enough, make a robot program to play all of your levels through, letting you verify that they are all passable / playable still.
Implement a Load method that converts data into game objects
Implement a Save method that converts game objects into data.
Add editor tooling that make it possible to load chunks of your data in Edit Mode, and then save those back into your data container.
So basically rebuild Unityās prefab system.
When you go out of your way to avoid using all the great tools that Unity offers, you generally have to reinvent the wheel and build (probably much worse) replacements for everything from scratch. You would make life for yourself much easier if you embraced the Unity way of doing things instead
Prefabs and ScriptableObjects. That handles like 90% of anything related to āDatabase-likeā stuff I need. The other 10% would be either proprietary files that are loaded on demand or actual databases.
If you want a ācentralizedā place to manage data then ScriptableObjects are the goto. If you want more granular control over individual GameObjects then prefabs. Itā pretty rare that I ever place raw GameObjects into a scene anymore except when testing or for completely trivia one-off things.
Another possibility that occurred to me overnight: build the data at the beginning of runtime. This will probably require some editor tooling that establishes a bunch of references to objects in the scene. Then when entering playmode/entering the scene, you can build your backing data.
Also I think people here are overlooking that in certain types of projects you do really need your own separate data layer.
Thatās a good point. On one MMO project I worked on level data came from the backend, which meant that they could only be loaded at runtime.
In the earlier phases of that project level designers used external software that could export to JSON (Tiled) for creating levels.
Over time we invested a lot of effort into building good in-game editing tools, and later on our level designers worked with basically the same set of tools as our players did for creating UGC.
I tend to think the other way around for systems with alot of data. For example we have a ballistics system. The ballistics is simulated with a struct so many simulations can be alive. Then scene related stuff like supersonic sonic cracks, fly by sub sonic sounds, richochet splashes, etc are done using reusable game objects.