Rossco1337 / 7demake

a unity game project that probably wont become anything
GNU General Public License v3.0
2 stars 0 forks source link

enemies: json or unity prefabs? #2

Open Rossco1337 opened 3 years ago

Rossco1337 commented 3 years ago

i started polluting my code with massive comments pondering this so i'm putting it here to tidy it up. i can't decide whether to store enemies as json or unity prefabs.

two schools of thinking here, not decided which i like better in the long run.

  1. define all enemies as json objects
    • pros:
      • all text - very compact, easy to mod+grep+automate
    • cons:
      • very difficult to work with - deserializing enemy data into a monobehavor directly (i.e the unit script attached to an object) is not possible
      • picking out an enemy requires loading the entire enemy list and name/id matching through all of them (AFAIK)
      • therefore requires 2 structs: list of enemy+data objects AND list of enemy gameobjects with passing data between both at runtime
  2. define all enemies as unity prefabs
    • pros:
      • extremely simple to spawn in by filename (1 line)
      • allows manipulating data via public object calls, no need to access data through a list index and copy to object
      • much easier to make changes in the unity editor (serialized fields).
      • allows for much more flexibility when adding things like animations or other gameobject components
    • cons:
      • increased storage/memory reqs - tradeoff for not having to build the entire enemy table object to call 1 enemy?
      • proprietary yaml format(?), not able to use data in other projects

i'm leaning towards prefabs. they're only 4kb-ish each. i love the cleanliness, minimalism, moddability and portability of a good object format though. i'm going to have to really pollute the json and write a lot of code if i want the same functionality as prefabs (having a sprite field in the enemy data was already a painful concession). both options are in the codebase for the time being because i can't let go of all the work that me and @EasyAI put into figuring stuff out.

Rossco1337 commented 3 years ago

i had a good train of thought going here but i lost it when my browser crashed with a scroll bug. anyway, main point was "why not both?"

devenv_232eFgrwh5

the main problem with this approach is deduplication. it's probably possible to create all the enemies in the inspector and then deserialze them to json. the code to serialize an enemy into a gameobject already mostly kinda works (as long as its not serializing directly into the Unit component) so writing an import/export button (like most web games have for saves nowadays) might be the order of the day. but it's not a priority until there's an actual game.

the more videos and talks i watch, the more i start to see the OOP big picture. why store enemy item drops as a string(itemname) when i could set it to an Item ScriptableObject reference and cut out the error prone (and expensive in a long list) json itemname->Item.name lookup? issue #3 is highly relevant here. having odin would make a super easy "write once, create 1000x" gui workflow but for $55, i might be able to do without.

Rossco1337 commented 3 years ago

slowly getting closer to closing this. i've learned more about OOP architecture and best practice in the past week than i did in 6 years of higher education (including a 6 month c# game development course, although that was a lot of fun!)

after doing a near full-refactor thinking about shared/non-shared data (based on the wisdom of ian dundore and others), my mind has been awakened to the power of using ScriptableObject .asset files for object based "cold" data storage: image i can just drag&drop an Item drop onto that bar instead of having to name-lookup and verify at runtime like i'd have to do with json.

doing this allows me to to create a new enemy with all the required fields from a right click menu image and save space inside the prefab for all of the "hot" current stats. image

i don't really think it can get much more performant than this while keeping a sane workflow. each "stored" enemy is <1kb (the more you store, the more you save thanks to assetbundle compression). the "live" enemy prefab/GameObject should be under 32kb each including sprite+overhead and stays stored until instantiated.

i'm keeping this open until it's final_FINAL_v3.txt because i'm messing about with giving both Stats and Statuses their own ScriptableObject and i'm worried i might be taking this pattern too far. i might still make an enemy import/export thing for fun and learning later but these .asset files are compact enough that they have all the pros that i listed for json (which wasn't necessarily true for .prefabs): image

Rossco1337 commented 3 years ago

i should have made this table before i made an IHaveBaseStats interface like a DAMN fool image

WHY JAPANESE PEOPLE? SURELY YOU PLACED THIS TRAP TO TRICK PEOPLE!

just when i finally think i'm saving work for myself by making all unit types implement the same base stats (but in different ways), they played me like a damn fiddle. enemy structs are built with a mix of the player's base stats and the player's derived stats.

atk% and def% should be "accuracy" and "evasion". enemies don't have these stats derived like players do, probably because they don't use equipment.

this isn't a total waste of time - Enemy and Player units can both implement IHaveBaseStats but these aren't the player's "base stats" at all - these are recalculated all the time based on their actual base stats. the name doesn't matter at all but maybe IHaveBattleStats would be better?

also considering renaming Unit to Actor at some point for convention's sake. architecture.