• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Question - General Script asset structuring/formatting post 2.3

HalRiyami

Member
For those who have published function heavy assets, pre and post 2.3. Pre 2.3, whenever you built a system it most likely looked like a bunch of scripts the user can see for every function your asset provides (whether you wanted to expose the function to the user or not) but now with 2.3, you get to choose how your functions are presented and have the option of using methods instead.

Suppose I have a collision system I am building, pre 2.3 it would look like this:
GML:
// Scripts
    // Folder: My Collision System
        // Folder: Exposed Functions
            collider_create()
            collider_set_scale()
            collider_get_scale()
            ...
        // Folder: Internal Functions
            __collider_update()
            __collider_get_centroid()
            ...
Now, I have multiple options:

1. Keep it the same way with minor adjustments to where internal functions are placed. This is a good way to maintain similarity with pre 2.3 scripts and any user should be able to pick up which functions are available to them at a glance. What's bad about this is that now you have a bunch of script assets and many of them do one thing, so you're not really utilizing the new system as much as you could.​
GML:
// Folder: My Collision System
    // Folder: Exposed Functions
        // Script Asset: collider_create
            function collider_create()
        // Script Asset: collider_set_scale
            function collider_set_scale()
        // Script Asset: collider_get_scale
            function collider_get_scale()
        ...
    // Script Asset: collider_internal_functions
        function __collider_update()
        function __collider_get_centroid()
        ...
2. Move all functions into appropriate script assets that indicate to the user where they should go to look for relevant functions. The nice thing about this is that functions are tucked nicely inside script assets. I can easily tell that all my collider functions are going to be under "collider_exposed_functions" but now I have to open the script asset to know what functions are actually available to me.​
GML:
// Folder: My Collision System
    // Script Asset: collider_exposed_functions
        function collider_create()
        function collider_set_scale()
        function collider_get_scale()
        ...
    // Script Asset: collider_internal_functions
        function __collider_update()
        function __collider_get_centroid()
        ...
3. Where appropriate use constructors and methods. "collider_create" now creates a struct that holds all the collider's information and gives it the appropriate method variables pertinent to the struct so it makes perfect sense to have it this way. One issue is that you're very likely to mix both named functions and method variables and this could create some confusion for the user. Another issue here is that for this particular style, the user can easily see all method variables so you have to explicitly tell them which to use and which to avoid.​
GML:
// Folder: My Collision System
    // Script Asset: collider_functions
        function collider_create() constructor
            // Exposed Functions
            static setScale = function()
            static getScale = function()
            ...
            // Internal Functions
            static update = function()
            static getCentroid = function()
            ...
Obviously there are more styles but you get the idea.

I am wondering how you guys decide which style to go for, how to present the functions, and what considerations go into your decision making. Do you simply go for what makes the most sense to you? Do you lean towards consistency? How about ease of use on the users' end? I am mainly wondering "how do I make sure the user can quickly and reliably find the functions I want them to use while at the same time hiding any code they don't need to interact with?"
I currently have two fully featured systems I built for myself and I believe they might be worth sharing/selling but they are written in GMS 2.2.5 and so I am trying to figure out if there is a standard you follow or if you lean towards one style over another.

Cheers
 

kburkhart84

Firehammer Games
The way I'm doing it(now with 2.3, and the asset tree being fully customizeable) is like this.

1. I have a group/folder called 3rdParty. Under that group I have "FHGames" and then under that, the asset itself("FHInput", "FHEase", and upcoming "FHAudio"). Any system's assets all will be organized under its own folder.

2. Each asset typically would have 3 groups, which are "Internal" "External" and "Examples". I do this so that the examples can be deleted later once the user knows how to use everything. This top level folder for a given asset may contain a couple files for configuration, where the user changes some macros to control settings(for example, debug mode).

3. The "External" folder contains all the functions that are user accessible. They are indeed one function per script, so it's easy to find what you are looking for. I also prefix each one of these user functions with the same name, "fhInput", "fhAudio", etc... This is so that they can type the prefix in code and it filters the auto-complete list properly.

4. The "Internal" folder contains a couple of folders, mainly to organize scripts, objects, etc...everything that my system needs that the user doesn't need to know or care about. I literally put a prefix on EVERYTHING in this folder as well, the same as the external stuff, but with a double underscore in front("__fhInput", "__fhAudio"). This serves to make all the variables, functions, objects, etc... avoid the auto-complete listing. In here, I'm perfectly willing to put several functions into the same script asset, although I still keep things organized and don't overdo it.

5. The examples folder contains basically that, examples. The idea is that this folder can be deleted at any time. I organize the folders in whatever makes sense, for example, putting things used by one example/room into the same folder.

One thing about my stuff as well, I personally use camelCase() for all of my stuff. However, for all of the external functions, I add a snake_case version. I call those "gml friendly" versions. I know there are others like me that like camelCase, and I also like how using camelCase sets a different standard from GML functions. But I know some people like snake_case, so I add those as well(even though they just call the originals). So I have something like fhInputActionGetLongDescription(), and the gml friendly version fhi_action_get_long_description() as an alternative.
 

HalRiyami

Member
@kburkhart84 The way you do it is how I expect most people to organize their scripts. I've seen a couple of assets that follow a similar pattern. Since you use both camelCase and snake_case for your functions, do you do the same for method variables?
 

kburkhart84

Firehammer Games
@kburkhart84 The way you do it is how I expect most people to organize their scripts. I've seen a couple of assets that follow a similar pattern. Since you use both camelCase and snake_case for your functions, do you do the same for method variables?
I use camelCase for method variables, and all other variables as well. I don't currently have any assets that need to expose methods just yet, but I would probably do the same thing with those, just having one call the other like I do with exposed global functions.
 

HalRiyami

Member
Have you gaged the performance impact of having one version call the other? One call might not be a noticeable but a hundred or so might leave a mark.
 

kburkhart84

Firehammer Games
Have you gaged the performance impact of having one version call the other? One call might not be a noticeable but a hundred or so might leave a mark.
I haven't felt the need to check it just yet. From tests I've seen, the performance loss on function calling is quite minimal to where it isn't noticeable until like thousands of calls. None of my assets have anybody calling functions near that often where that matters yet. It is however a consideration for the future if I write something that is more intensive. In those cases I'll likely simply duplicate the code to avoid the overhead.
 
Top