jakubg1 / OpenSMCE

Game engine which allows creating a broad range of marble popper games.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Extract all JSON structures to their own classes

jakubg1 opened this issue · comments

All JSON structures that exist should be given their own classes. This will greatly improve code clarity and will make development easier.

Some insights:

  • All structure-related classes should be in a separate folder.
  • Some JSON files should have not just one class representation, but a few, due to some microstructures being bundled inside.
  • Introduce all needed getters and setters, but also some useful functions. For example, in a collectible generator data class, we could define a function which evaluates it and returns a randomly selected output.

We need to consider migrating some classes, too.
Here's a list of the more important resource types used in the engine:

Name Extension Located in Stored as Class name Loaded by Notes
Image *.png /images Class Essentials/Image ResourceManager
Sprite *.json /sprites Class Essentials/Sprite ResourceManager
Sound *.wav/ogg/... /sounds Class Essentials/Sound ResourceManager
Sound Event *.json /sound_events Class Essentials/SoundEvent ResourceManager
Music *.wav/ogg/... /music Class Essentials/Music ResourceManager
Font *.json /fonts Class Essentials/Font ResourceManager
Font File *.ttf /font_files LOVE2D Object (none) Font This is an example of very bad implementation!
Particle *.json /particles Table (none) ResourceManager
Color Palette *.png --- Class Essentials/ColorPalette ResourceManager Stored and loaded in Load List
Sphere *.json /config/spheres Table (none) ConfigManager
Sphere Effect *.json /config/sphere_effects Table (none) ConfigManager
Collectible *.json /config/collectibles Table (none) ConfigManager
Collectible Generator *.json /config/collectible_generators Class CollectibleGenerator/Entry CollectibleGeneratorManager
Color Generator *.json /config/color_generators Table (none) ConfigManager
Level *.json /config/levels Table (none) ConfigManager
Map *.json /maps/<map name>/config.json Table (none) ConfigManager Load list defined by Levels

Some insights I can see from the table:

  • Font files should be given their own resource and class, and be properly loaded by ResourceManager.
  • Collectible Generators should get rid of its manager and be handled like other similar structures.
  • Color Palettes should get their own folder.

Ideally, either:

  • everything should be stored in the ResourceManager, except for files and structures which appear exactly once in the config,
  • or everything what isn't JSON should be stored in the ResourceManager, and everything what is JSON should be stored in ConfigManager.

Also:

  • All resources should be accessed by just name, not the path such as sound_events/button_hover.json -> button_hover. Currently, ConfigManager does that but ResourceManager does not.
    • Rebuild ResourceManager so resources stored inside are keyed by names, not whole file paths.

Additional key benefits by introducing this change:

  • Data can be easily validated in a clean way, without messing with game classes themselves.
  • Data can be also patched over time, prepending or changing certain variables as they change their format over time.

An example involving the shooter data structure has been added in commit 77cf19b.
A few notable observations one can see in my approach:

  • All resource paths/Vector structures are immediately converted to a more useful form.
  • The structure of the original file is preserved.
  • Most of the variable types are rectified using the ---@type tags.
  • There are no getters. It's stupid as it doesn't add any value while adding lots of not-very-useful boilerplate code.
  • There is no data verification, as this would double the JSON schema checks performed by VS Code.
    • However, some checks can exist if for example an illegal type is provided.

API draft for the new Resource Manager:

  • ResourceManager:resolveAssetPath(path, [namespace]): Resolves and outputs the entire asset path starting from the game folder. As per #103.
    • Example: resolveAssetPath(":flame.spr", "Map1") will return "maps/Map1/sprites/flame.spr".
  • ResourceManager:resolveAssetType(path): Resolves and outputs the resource type of this file, based on its extension.
    • Example: resolveAssetType(":flame.spr") will return "sprite".
  • ResourceManager:getAsset(path, [namespace], [batches]): If the resource has been already loaded earlier, returns this resource. If not, immediately loads it in, optionally as a part of designated batches. (TODO: Decide whether to make variants for different asset types or not. LDoc might have a problem with this.)
  • ResourceManager:loadAsset(path, [namespace], [batches]): If the resource has been already loaded earlier, do nothing. Otherwise, load this asset (not immediately - treat this as queueing the resource to be loaded soon!), optionally as a part of designated batches. If many calls are performed in a quick succession, the load order will be preserved. This function will never return anything.
  • ResourceManager:loadAssetFromServer(path, [namespace], [batches], [cache]): Proposed way of loading resources from the server, for use in Cosmic Crash.
  • ResourceManager:releaseAssetBatch(batch): Removes all mentions of the given batch from all the loaded resources. For any resource, if that was the only batch, unloads it completely.
  • ResourceManager:startLoadCounter(name): Creates a counter which will be counting all resources queued from this point onwards. The total number of resources and the number of resources already loaded will be tracked.
  • ResourceManager:stopLoadCounter(name): Stops counting the resources that are queued. Does not destroy the counter: the counter will be hanging around forever. The number of loaded resources will still rise when they are loaded afterwards.
  • ResourceManager:getLoadProgress(name): Returns the percentage of loaded resources out of queued resources (between the startLoadCounter and stopLoadCounter calls).
    • Example of all three:
    function loadGame()
        resourceManager:startLoadCounter("main")
        resourceManager:loadAsset(...)
        resourceManager:loadAsset(...)
        resourceManager:loadAsset(...)
        ...
        resourceManager:stopLoadCounter("main")
    end
    
    function draw()
        local progress = resourceManager:getLoadProgress("main")
        if progress == 1 then
            drawStartButton()
        else
            drawProgressBar(progress)
        end
    end

Notes:

  • Use getAsset for loading the maps and the splash screen itself, and loadAsset for all game resources which are loaded during the splash screen.

A few Config Classes have been added which follow the most recent style: