Search Unity

Component to component communications? How do you handle this

Discussion in 'Game Design' started by BIGTIMEMASTER, Aug 2, 2022.

  1. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    Suppose you have two components in a survival game. BodyTemperature component and Clothing component.

    BodyTemp component should calculate your characters current body temperature. Two factors in this equation should be the ambient world temperature and the insulation provided by your current equipped clothing.

    The Clothing component might be responsible for equip/unequip clothing which would be changing some visual elements but also getting data about the clothing.
    This data about the clothing - it's insulation value in this example - might be stored as float that could be used as an input over in the BodyTemp calculations.

    This seems to create a problem however. As I understand it, the point of using this ECS (entity component system) design is that different components have broad usage and aren't tied to a class. But if one component needs data from another... that sort of defeats the point of separation?

    I guess you can make some branching checks, like check if the entity is using the Clothing component, and if so, then get the insulation value to be used for calculating temperature. I don't see any problem with that, but I just wonder if that isn't sort of going against the premise of how it's all meant to work.

    To my mind, even if the clothing component and bodytemp component would always be used in conjunction, it still seems consistent and logical that these two components remain separate. In thinking about designing a game framework that will be easy to maintain, I think consistency is probably the most important thing.

    Just looking for any thoughts, opinions, advice.
     
  2. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
  3. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    that article helped clear things up.

    So after further consideration I think my original idea best fits the situation.

    The full-blown ECS structure seems like it will be a lot of added verbosity with no benefit, because in the case of a survival game, it's not like the rules of the game or capabilities of any entities is going to change. It is all known ahead of time.

    For instance, in many of these examples, the benefit of ECS is something like, "imagine I start production and I think, entity X will be able to shoot, explode, and whirl around. But hten later I decide it should also have ten other behaviors..."

    But in case of a realistic survival game, I just can't imagine a situation like that. I think a simpler approach that doesnt separate data from systems is going to be fine.
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,866
    The way I use components is more of a 'does this object have this property, and if so, I can act upon that or get certain information from it' pattern. Unity's
    TryGetComponent<T>(out T component)
    method kind of symbolises this.

    Think an item/inventory system. If your item's properties are component based, you can just check if something has a particular property and act on that. Does an item have a consumable component? Then I can eat it! Naturally my survival-craft project has it's own
    TryGetItemComponent<T>(out T itemComponent)
    method.

    Same in your instance. This body temp component can just does, "Does this thing have clothing Y/N?" and act on that.

    This works well when you're dealing with components that are mostly just bags of data.
     
    BIGTIMEMASTER likes this.
  5. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Let's put this to bed first: It won't be long before some evangelist advocates dependency injection (inversion of control) and suggests some overly complicated DI framework. It's not necessary for what you describe.

    To add to spiney199's answer, keep in mind that you can also use GetComponent<T> and TryGetComponent<T> with C# interfaces that so you don't have to tie your code to specific implementations.
     
  6. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    @spiney199 @TonyLi

    thanks guys. So after I read that article and a couple others a few more times, I realize I had a few misconceptions. Probably not worth the effort to try and identify what they were.

    But my understanding now is that this is actually a pretty simple setup:

    components are just data. If we strip away game engines and specific languages, all this means is that there is some sort of container which has some data nuggets in it, and the container is a thing that might be easily identified.
    Unity already has some inbuilt tools for that so it's no problem, but some of the articles I was reading were using c++ or other languages to demonstrate so I was trying to just get the principle.

    Systems is just some sort of logic that is going to identify components and then do stuff with the data.

    And entity is just a pointer to some object in the game which might hold onto various components.

    It seems the major benefit of this structure is flexibility in a game where I am likely to make many imaginitive changes to game rules during production. In a survival game with pretty much set in stone rules I dont see an immediate benefit - however this example where i need to get data from a couple components and do something with it definitely seems like the cleanest solution.

    Is it worth setting up an ECS architecture when I can only identify one time it is going to benefit? I'm not sure, I'll just try it out. I had another, simpler solution in mind already that I can fall back on if it seems that a full-blown ECS might be overkill.

    The major thing I aim to accomplish is framework that is as streamlined as possible - or in other words, as consistent as possible, with as few caveats as possible.

    Thanks guys it's really helpful to get some feedback on this - I think I've finally properly understood this idea. What seemed to be confusing me was ambiguous language. Sometimes it is hard to know if a specific technical word is being used specifically or broadly, and then you see it used in different ways in different places. That article I linked above removed a lot of the confusion though because it provides a step by step practical example.
     
    Last edited: Aug 3, 2022
  7. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    gotcha. so with that power, you might be able to do something like, "get every entity within X area that uses componentA and apply some logic".

    Or the player might shoot a raytrace which hits another gameobject, and just check for a component to see what effect should happen. But it's class dependent, the interface just communicates a fail-safe function call if the target is subscribed to the interface.
     
    TonyLi likes this.
  8. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    great example.

    Yeah i think for the clothing, it makes sense that we basically just add/remove data in an array when equip/unequip. And then a "temperature" system might take that clothing component data into consideration.

    But this way, separating data from logic, the temperature system can also act on food, or water, or anything else that might freeze or spoil.

    I like this idea a lot - very clean and logical. My only question is whether it makes sense to build an architecture around this idea if I am likely only going to use it in one area. But that should be simple enough to answer with some test.
     
  9. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    I agree; build out something bigger only if you need it. The best approach is to build something straightforward and exactly what you need to accomplish your immediate goal and nothing more. Use a C# interface or a minimal set of public methods to isolate the implementation from the rest of your code. That way you can change the underlying implementation later without having to touch any other code. Most of the time when people overengineer a complex, extensible architecture, they end up not needing it to do anything more than the original task you wanted to accomplish. That's just a lot of wasted time, not only upfront time designing, coding, and testing the architecture, but also time understanding the complex code later on when you come back to it.
     
    Martin_H and BIGTIMEMASTER like this.
  10. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    yeah I think I might be getting into the "overengineering" zone a bit - though intent here is just try and research different possibilities as a lightly seasoned programmer, so even if I find a method that isn't right for my project, I don't consider it time wasted.

    I am away from my workstation so i can only pseudo-code at the moment, but I am thinking that trying to implement a full blown ECS system is just going to be overkill for the sort of project I'm prototyping. Because there just isn't going to be much overlap behaviors between different classes and the behaviors of different entities are more or less written in stone (just the nature of a "realistic" game).

    i think this basic idea of letting components just be data and then having logic which operates on the data somewhere else is still going to be helpful in at least a few cases, and I think that the principle is consistent enough with how the rest of my project is working that it should contribute towards this goal of overall project consistency, so I'll try it out a bit.
     
    Martin_H likes this.
  11. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    ECS approaches are typically most useful when you have large numbers of similar objects which need to be operated on, and it's most beneficial when you can design to standardise them as much as possible and minimise communication between individual entities.

    If you're looking at cases where there are small numbers of objects which require explicit communication with each other I suspect that's a far better fit to a standard Object Oriented or Component Oriented design.
     
    spiney199 likes this.
  12. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    In case it helps you understand things better... the idea of ECS is that component data is also "somewhere else", we just display it with the associated entity because it provides a much nicer design experience.

    To use a physical analogy, imagine I'm doing some research in a library. I need to gather some facts about some super interesting subject. If those facts are spread across different books in different areas then I need to keep looking up where the next book is, walking across the library, finding that book, opening it, looking up the correct page, then writing a note. There's very a good chance that I'll spend more time finding and collecting books than actually taking notes from them! This is roughly analogous to the Object Oriented approach - the CPU has to spend a long time reading different chunks of memory to get the values it needs before it can operate on them, and then has to do it again for the next operation.

    Now imagine I'm doing the same research, but someone has conveniently written about the exact topic which interests me and thus all of the info I need is in one book. Now I don't have to walk around the library at all. I now just open to the first relevant page, take a note, and turn the page until I'm done. This is roughly analogous to the ECS approach - the CPU reads one chunk of memory which has all of the relevant values together in it, and can then operate straight away. Better yet, when it's done an operation there's a high chance that the chunk of memory it already read has the values for the next operation, too.

    With that in mind, the idea of an ECS-style system is to design our code so that for any bulk data operations we do the data is all in one place. Of course we can still display the component data associated with a particular entity when we select that entity in our editor, but the data is probably stored with the "system", all together in a single, conveniently accessible collection.
     
    BIGTIMEMASTER likes this.
  13. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    Thanks, that paints a clearer picture for me.

    Somewhere else a person explained to me that ECS was really more about low-level optimizations (or at least, what I define as low-level, meaning deeper than I ought to go), more so than just project-management for sake of ease of use. What you are explaining here seems like the version of what they explained in a way that I can immediately understand.

    And after reading that article, my conclusion was that I had confused ECS with composition architecture in general. I had thought ECS = Composition. But Composition is a broad category that covers many methods, ECS is one method within that category. If I understand correctly.

    For my project (a realistic survival game), where the rules of the game are set in stone and gameplay balance changes will come more from adjusting what resources the player has or data assigned to those resources, it seems that a more abstract, compositional approach would increase time to create framework and make debugging more work as well, but I wouldn't reap the benefits because I doubt I would have many situations where sharing behavior across classes would be useful.
    I can point to a couple cases - like temperature management - but there are so few, I think just copy-paste or make a globally accessible function is a simpler approach which solves the problem and doesn't require me to make a more complex framework to support just a couple edge cases.

    Of course I may discover something unknown though, so I'm glad that I have a clearer picture how I might use some of these principles. Another tool in the toolbelt.
     
  14. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    You understand correctly. Unity has always used Component Oriented Design since ~2005, but it's only in the past few years it's moved towards employing an Entity Component System approach to things.

    From my perspective, I doubt it. I've found it to be a super easy way to do things, because it encourages splitting things into distinct units with clear responsibilities and planned routes of communication.

    That said, by the time I reached that conclusion (by experimentation) I was a somewhat experienced programmer in general, and had experienced the problems you typically run into with this approach:
    The question is, could I have done things well with a Component Oriented design if I didn't have first-hand experience of the problems that it solved? I have no idea. For starters, I likely needed to understand the problems I was solving in order to make good decisions about where to split responsibilities. I suspect that you could just get that by direct practice and experience, but your mileage may vary.

    Certainly after using CO design for a couple of projects I had zero interest in going back to heavily inheritance driven approaches as I'd used in the past. Inheritance is fantastic in its own way, it's just a matter of using the right tool for the job.
     
    BIGTIMEMASTER likes this.
  15. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    Yeah I think that if I committed to implementing composition for the project as completely as I could, I'd get it down and it wouldn't be so much energy to conceptualize how to do it in the first place. But I am still working within confines of visual scripting and trying to use unreals given framework as much as I can - as far as I can tell this does not preclude using a composition approach. Basically, i just make use of some singleton classes ( I think you'd call something like a game instance a singleton) because they come with a known lifespan, scope, and lots of helper functions/events.

    But I am sure I don't have a full understanding of what a composition based project might look like. Right now, to me composition just means "put data and/or behavior into class-independent, reuseable containers." This is a pattern that I can easily use for gameobjects in the world, but there is a lot of other stuff going on as well besides things moving around in the world that if I try to imagine handling with composition, it seems to introduce tons of caveats.

    One example - the Inventory system: sometimes inventory items are displayed as gameobjects in the world, other times they are just an object class ( i think unity equivalent would be scriptable object). This object is held in an array by the player controller (a persistent input handler, also handles state changes for any player-controlled gameobject, and holds data to be saved for player states). The save game object will then reference the player controllers "inventory array" when it's time to save the game.

    In other words, the lifespan of an inventory item is not tied to a gameobject. Thus, if I want to use composition pattern to manage data and behaviors for inventory items, then I need a special class of components and/or systems. That may be a limitation of using unreal blueprints though. Basically I have access to actor components (aka gameobject components - they live and die with the gameobject instance that they are attached to), or I can make my own component class which doesn't require a gameobject, but then I introduce a system that has a lot of caveats. If I was part of a larger team and my scope of responsibility didn't extend beyond this system, it might not be a big deal. But I also have an art pipeline to manage and everything else, so if I can get my code simple enough that there are very few or no, "except in this case, things work slightly differently" situations, that's my ideal.

    In unity, can we attach a component to classes that aren't gameobject? Like a scriptable object (just a data holding class that can also hold behavior script), can that get coupled with components? If so, that would allow me to have the sort of consistency that I'd prefer. But if not, then I think "components" in unreal seem to have similar reach.

    But what I learned from reading about ECS is that component can just mean "data." Like it could be a struct, or an object class, a string - whatever. And a System could be whatever as well. I could have a manager gameobject class that handles my systems, or I could put my systems into a singleton... doesn't really matter so long as I understand the lifespan and scope.

    I believe that I can make a system like that within the confines of visual scripting - it is not true ECS where we are managing at a low-level how data is chunked - but for the purpose of making life easier for the human, I can see some benefits. If I was making a shooter or strategy game where I know that I want a lot of freedom to work with behavior in a modular way, I think I'd probably try to base my framework from that.
     
  16. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    You don't necessarily have to look at it this way. There are really three (or more) different types of things:
    • A data-only item (e.g., in your inventory)
    • The physical representation of that item (e.g., a sword model shown in the player model's hand)
    • The physical representation of a pickup (e.g., a sword lying on the ground that you can pick up)
    If you combine all three, then each one carries around the baggage (functionality) of the other two.

    Instead, it's fine (and better in my view) to separate them. The pickup doesn't need the item's functionality or physical representation. It can be as simple as its own physical representation (or a reference to the item's physical representation), a reference to the data-only item, and a trigger. When the player interacts with the trigger, it can add the data-only item to the player's inventory and then despawn itself. The pickup doesn't need to know how to place a physical representation in player's hand, or the item's cost or what status effects it has when it's in the player's inventory, etc.
     
    angrypenguin and BIGTIMEMASTER like this.
  17. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    I believe that the way you described it is close to how I am handling thing - I probably explained poorly or I might be missing something. But let me try to explain my inventory setup more clearly because it's certainly an area that I could benefit from some outside perspective on:

    My inventory system involves a few classes:

    • data only item - an object class that has reference to a data table. This is the object which is referenced by the save system.
    • data table - defines any static data for each type of item. Includes references gameobject class to spawn if the item is dropped into the world.
    • pickup gameobject class - just a generic gameobject class with a sprite component and a trigger collision.
    • for equip items like clothing i also have a reference to attachment slots in the data table, and reference to a static/skeletal mesh in the data table
    The data only object class has a special derived child class for some items. Food can spoil and/or freeze. So each time data about the object is presented to the player, I needed some function to figure out it's current state.

    So the lifetime of an item like an apple might be like this:
    1. spawned into the world as pickup gameobject class.
    2. the gameobject class creates the data object when it is spawned, and the data object records the time that it is spawned (if it is of a class that will freeze/spoil).
    3. Player collides and picks up the item. This despawns the gameobject after getting the data object and transferring to the players inventory (array of data objects).
    4. Player drops item - spawn a new pickup gameobject and transfer the data object out of player inventory array and sets the pickups data object variable.
    So the way that I can't figure out how composition works here is because this "item" is only a gameobject sometimes. But when it exist only as a data object in the players inventory, it still needs that Temperature/Spoilage functionality. To my mind it makes sense that the functionality is tied directly to the class.

    The player character might also share temperature functionality, though the implications of that are going to be quite different from if an apple freezes. So the only shared code I can see is like a two line thing of "get outside temp to determine temp loss rate, subtract temp loss rate from..." you get the idea.

    So then I think, I can spend a couple hours trying to figure out a way to make that tiny bit of code reuseable for this one case, or I can just make a global accessible function or just copy paste temperature calculation between these two classes.

    Does that make sense? This setup uses one layer of inheritance - there is a base pickup class and a child for food pickup class, which has the temperature/spoilage functionality. This was the simplest way I could imagine things.

    Actually simplest thing would be to just have the gameobject never die, then it can hold onto a temperature/spoilage component. The only problem there is that it seems wasteful to be saving/loading entire gameobjects when all we really need is some data.
     
  18. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    You could do that, but I suggest that an item never be a GameObject. It should always be just one thing: a data-only object. It can be associated with a separate object that's the physical representation GameObject when it's visible in the scene, but that physical representation GameObject isn't the item itself.

    Your item will then always be an item in your item database (aka data table). The item can have attributes (spoilage, weight, etc.).
     
    angrypenguin and BIGTIMEMASTER like this.
  19. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    Where does the item live then? Do you mean to say that there is like an array of all items in the world held somewhere, and each item gameobject in the world would just reference to some item in that pool? I have read a little about object pooling but it wasnt super clear to me. It seemed like the idea is you just load everything you need all at once and store references in a single place, then anybody interested just references that pool. This was more of an optimization pattern as I understood it, so I figured I wouldn't bother with it unless I needed to.

    And functionality like spoilage/freezing, where would that functionality live?

    I believe it is possible to write to data table at runtime, though when I looked at it it wasn't something natively supported so I avoid it. This means that for me, data tables are read only, just used to get static data about an item.

    A variable like "percent frozen" of an apple then, if I cannot easily write to a data table, has to be held somewhere. If I just have an array of data objects, is that what you call a database? The data base could live somewhere like a manager class?

    I think I understand the principle but I'm not sure what actual implementation looks like.
     
    Last edited: Aug 9, 2022
  20. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Yes, that could work. Of course there are always optimizations on top of optimizations that you could apply, but the important thing is the underlying concept. So you could start with a big list of data objects, each representing an item (data only), with all of its composed attributes (location, weight, cost, edge sharpness, percent frozen, calories, etc., whatever you've added to the item). Your data table, on the other hand, could be a list of abstract item definitions. When you create a new item, create it from one of the template item definitions in the data table and add it to your manager class's database.

    With thousands of items in your game world, you'll eventually need to optimize how this list is handled. But for a first pass you could iterate through all of the items every world tick, giving each item a chance to update (e.g., change percent frozen, percent spoiled, etc.). If you're in a multi-scene world, the big list means you don't need to worry about which items are in which scenes; you just run through the list and update everything. The big list also makes saving and loading easier since you can save it as a whole.

    The item in your database could also have a pointer to a GameObject for its physical representation in the world, if the item is currently visible in the world. The point here is that the physical representation GameObject doesn't need to know about weight, cost, percent frozen, etc. The item tells the physical representation GameObject where to be and how to look, and it just does it. Likewise, the item doesn't need to worry about how the physical representation should exist in the scene or how to look a certain way. It just tells the physical representation to take care of that.
     
    BIGTIMEMASTER likes this.
  21. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    @TonyLi

    I think I get the general idea. When I was researching ways I might do all of this, I had considered something along these lines of basically putting data into one big "pool", and then having a manager handle all of it, but I didn't get into the details of implementing because I didn't see immediate benefit.

    so the major idea here is that we are chunking a ton of data into one place. That is going to:
    • benefit the computers ability to look things up
    • simplify the save/load management because a single database informs what items there is, where they are, and what state they are in
    • make humans life a little easier because anything to do with inventory items/pickups is held in this one database, and any logic to be applied to data base items is abstracted so it can easily be used anywhere else.
    Does that sound right?

    As an example, in addition to item info like weight, name, etc, an item in the database might hold reference to a gameobject class (if i need to exist in the world, what do i look like?), a bool for IsSpawnedInWorld, and a location (a vector) for where the item should be if it is spawned?

    I don't expect there to be thousands of items ever, because anything the player collects is usually consumable or has shelf life. And stuff like trees can spawn data objects procedurally. But, a basic filtering system might be accomplished simply by organizing data objects into arrays based on item type (a string or enum or tag).

    So, all the food items or items that might freeze can go into their own array, and maybe whenever the player interacts with an item from this array, then run it's IsFrozen? or IsSpoiled? functions to display the latest info (as opposed to per frame or a timer update). But point being, if we group like items with like items, that should be a simple way to add some efficiency.


    additional: And items that should be available in the players inventory could just be put into a special array for that, or maybe just have some flag or something to identify the fact. But perhaps putting into an array may make certain player inventory checks more efficient. E.G., the building system may want to know if player has enough nails and boards to make a wall. It could just query the playerInventory array to determine that?
     
  22. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    The additional arrays are along the lines of those optional optimizations I was talking about - they're essentially like indexes in a traditional database application. I might take a slightly different approach, but that's the idea.

    The more important thing about this approach is that it separates concerns. An item object's single responsibility is managing its data, not anything related to appearance in the scene. A pickup's single responsibility is appearing as a trigger in the scene that gives an item to a player when walked on (i.e., the physical representation of an item sitting in the world). An equipped model's single responsibility is appearing in the player's hand (i.e., the physical representation of an item equipped on a character). Giving each of these a separate, single responsibility means each one has a smaller, simpler, more straightforward set of tasks it has to do.
     
    BIGTIMEMASTER likes this.
  23. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    @TonyLi

    and that would then allow you to separate behaviors into components that have a single-responsibility too, right?

    The actual database, it just needs to live somewhere with appropriate lifespan? e.g. tied to the controller or persistent level?

    And now the behavior is able to be separated out to components, but would you attach them to the same place as the database? Do you use something like a single manager to hold all the components which might be used by the database?
     
  24. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Sorry about the vagueness. I've avoided specific implementation discussions because I'm not sure what engine or programming language you're using.

    Just like writing is rewriting, programming is refactoring -- essentially, for any new problem, it's a process of exploration until you arrive at a solution that meets your requirements -- performance requirements, understandability and maintainability requirements, scalability requirements, etc.

    This is like that. I think we discussed earlier about the brute force "god class" approach to items, where an Item class handles all functionality -- weapon damage, percent frozen, etc. Honestly, if that meets your requirements, might as well stop there and use it.

    Now we've eventually landed on a component idea that, organizing it the way I've been describing above, gets close to the idea of an entity-component-system architecture. You might have a list of items, where each item contains a list of data objects such as WeightData(kg), FrozenData(%frozen, thaw rate), SpoilageData(%spoiled, spoil rate), etc.:

    Code (text):
    1. Item  | Data
    2. ------|-----
    3. Knife | Location(hand) Weight(0.4kg), Damage(5hp)
    4. Ice   | Location(pond), Weight(0.5kg), Frozen(95%, 5/minute)
    5. Apple | Location(ground001), Weight(0.2kg), Spoilage(1%, 1/minute)
    6. Fish  | Location(rucksack), Weight(1kg), Spoilage(5%, 3/minute)


    It should be pretty easy to save this data in a saved game. And you have easy access to every item.

    Each type of data object might be associated with another class (really, a system in ECS) that knows how to update that type of data object. In each world tick, you'd just tell these classes to update the data on all of the corresponding data objects in the database. For example, a SpoilageUpdater could go through the list, see that Apple and Fish have Spoilage data objects, and update their spoiled percents. (In practice, SpoilageUpdater would already have a list of data objects, but that's an optimization, not the core concept itself.)
     
  25. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    @TonyLi

    I think I got a pretty clear picture now - I was curious where you might put the actual behavior because, while a god class is probably fine for my needs, I figured there is a more sophisticated approach you might know about. The major problem of a god class is that when you load it, it's going to also load all class references, and that is just generally inefficient if you don't actually need to load the references at the moment, right?

    It looks like what you've described would lead to an easier-to-maintain system compared to what I've got.
    Once I get back into the code of this project, I think I'll refactor the system to use this idea.When I first tried to imagine a system like this I wasn't able to understand how to abstract the behavior in a clean way but it seems pretty simple now the way you've described it.

    Well, if I had to pay for all the advice here in the thread I don't think I could ever afford it. Definitely has helped me get this project off with a better foundation. :)
     
    TonyLi likes this.
  26. GimmyDev

    GimmyDev

    Joined:
    Oct 9, 2021
    Posts:
    160
    If spoilage do not vary, you wouldnt need to tick it, you can derive the time at evaluation only by just storing the time when the item is created. In fact, even if it varies, you could just offset the time at variation event evaluating the object.
     
    TonyLi likes this.
  27. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    yes that's right.

    I havent written the entire calculation but it should just be a matter of subtracting current time from the time that the thing was spawned. This function could only be called at select times when info needs to be displayed to player.
     
    GimmyDev likes this.
  28. BIGTIMEMASTER

    BIGTIMEMASTER

    Joined:
    Jun 1, 2017
    Posts:
    5,181
    I made this thread to explore some ideas while prototyping a new project. I got everything I wanted (+ a lot more). Now I am back to finishing my current project and I just wanted to report, what I learned here has helped me a lot in refactoring some systems and it has made a big improvement towards my ability to "hold the projects architecture in my head."

    Even though I am not completely using a composition design in this case, my thinking about how to design my code has been influenced so that I have a lot more logical consistency in my code.

    Now the main thing I ask when adding or changing some system is, "who should be responsible?"

    As long as I can answer that, then it's pretty easy for me to jump right into any system without having to remember any caveats. I can make some tooltips to attach to each class giving a brief overview of what the responsibilities are - this has helped a great deal in ameliorating the number one problem I have: getting too much fatigue when changing gears between different areas of the project.

    Well, just adding closure, thanks again everybody who has helped.
     
    angrypenguin, GimmyDev and TonyLi like this.