1. Hey! Guest! The 34th GMC Jam will take place between August 22nd, 12:00 UTC (Thursday noon) and August 26th, 12:00 UTC (Monday noon). Why not join in! Click here to find out more!
    Dismiss Notice

OFFICIAL Best Practices GML (Follow up AMA#2)

Discussion in 'Announcements' started by rmanthorp, Jul 11, 2019.

  1. rmanthorp

    rmanthorp YoYo Games Staff Admin YYG Staff

    Joined:
    Apr 15, 2016
    Posts:
    287
    Hello, today we've got another remasted tech blog covering Best Practices Coding in GameMaker. You can read it over on our tech blog:

    https://www.yoyogames.com/blog/63/best-practices-when-coding-in-gamemaker-studio-2

    To follow this up, we are running another AMA with a focus on GML best practices. However, if you have a broader GML question we might also pick that up.

    If you have a question for Russell and the core tech team, please respond in this thread and preface your question with 'GML QUESTION:'. As we did the last time, we will be collecting the questions and returning with a follow-up post containing the answers.

    Thank you!
     
  2. curato

    curato Member

    Joined:
    Jun 30, 2016
    Posts:
    324
    I guess it is individual programing style, but I have the compelling urge to fix all the indentation in the programing style section.
     
    Kruno, Cpaz, Lonewolff and 1 other person like this.
  3. Guest

    Guest Guest

    GML QUESTION: An ancient dispute: to make a colored rectangle, is it more efficient to use the draw rectangle function or to stretch a one-pixel sprite? If the latter, does it negate the efficiency by using a white one-pixel sprite and coloring it with the draw sprite function?
     
    Last edited by a moderator: Jul 11, 2019
    Cpaz likes this.
  4. cchhiipp

    cchhiipp Member

    Joined:
    May 25, 2018
    Posts:
    4
    GML QUESTION: I have a DS Grid I need to export to json. What is the best practice for handling that?
     
  5. FrostyCat

    FrostyCat Member

    Joined:
    Jun 26, 2016
    Posts:
    4,327
    The answer to this question is actually not as straight-forward as most GMC responders around here may think.

    To start, here is the standard form that most GMC responders would attest to (assuming that grid is pre-initialized):
    Code:
    var json_data = ds_map_create();
    json_data[? "grid_data"] = ds_grid_write(grid);
    var json = json_encode(json_data);
    
    Code:
    var json_data = json_decode(json);
    ds_grid_read(grid, json_data[? "grid_data"]);
    
    Most people would say this is a universal solution, but I know better than that. I know that on the HTML5 export, ds_*_read() and ds_*_write() functions work in a different format than all other exports. So while the form is fine and dandy if you don't ever touch the HTML5 export or work in a cross-platform capacity, if you do touch it in a cross-platform manner it's far from fine. An example of this would be JSON data added as Included Files, or JSON data uploaded online and downloaded to clients on a mix of platforms.

    The cross-platform alternative would be this, which works in pure JSON space (i.e. maps and lists only):
    Code:
    var json_data = ds_map_create();
    var virtual_grid = ds_list_create(),
        virtual_grid_width = ds_grid_width(grid),
        virtual_grid_height = ds_grid_height(grid);
    for (var yy = 0; yy < virtual_grid_height; yy++) {
      var virtual_grid_row = ds_list_create();
      for (var xx = 0; xx < virtual_grid_width; xx++) {
        ds_list_add(virtual_grid_row, grid[# xx, yy]);
      }
      ds_list_add(virtual_grid, virtual_grid_row);
      ds_list_mark_as_list(virtual_grid, yy);
    }
    ds_map_add_list(json_data, "grid_data", virtual_grid);
    var json = json_encode(json_data);
    
    Code:
    var json_data = json_decode(json);
    var virtual_grid = json_data[? "grid_data"],
        virtual_grid_height = ds_list_size(virtual_grid),
        virtual_grid_width = ds_list_size(virtual_grid[| 0]);
    for (var yy = 0; yy < virtual_grid_height; yy++) {
      var virtual_grid_row = virtual_grid[| yy];
      for (var xx = 0; xx < virtual_grid_width; xx++) {
        grid[# xx, yy] = virtual_grid_row[| xx];
      }
    }
    
    It's more verbose to start with, but there are no HTML5 vs. non-HTML5 format differences to deal with. In addition, this format is human-readable and human-writable, unlike the binary-like format of native-export ds_*_write() and ds_*_read(). But here's the biggest question of all: Given that we're supposed to be using an engine, why does this still need to be man-handled?

    YoYo should think over this issue and reconsider how to harmonize data structure serialization going forward. The current default with ds_*_write() and ds_*_read() just doesn't work in a cross-platform setup and hampers the portability of GM data. The lack of built-in nested serialization support across all data structure types is also unhelpful on many levels and need to be addressed.
     
    Last edited: Jul 20, 2019
    Pfap and Catan like this.
  6. Musehill

    Musehill Member

    Joined:
    Jan 7, 2017
    Posts:
    3
    Thanks for the AMA. I've been using mp_grids not only for pathfinding, but also as part of a simple collision check system. I'm early in my project and haven't seen any problems with it but is there any disadvantage of getting a cell's status frequently through the mp_grid functions? As opposed to using a simple 2D array or the tile-based method mentioned in the blog?
     
  7. Catan

    Catan Member

    Joined:
    Jun 20, 2016
    Posts:
    650
    @FrostyCat Although I used the "ds_list of ds_lists" workaround you proposed many times in the past to get a json friendly output, I am totally guilty of suggesting ds_grid_write as a solution. I agree with you that this should be the proper answer, but I want to stress how bad it is that the default serialization methods are handled differently depending on the platform.

    It also bothers me that the output is some kind of obscure format that only GM uses (as far as I know), it may be useful for as a very basic form of obfuscation but it makes no sense to use it as such.
     
  8. iRhymeWithRawr

    iRhymeWithRawr Member

    Joined:
    Jun 21, 2016
    Posts:
    1
    GML QUESTION:
    I have a question about a limitation of GML. If I create a constant (e.g. #macro c_black32 make_colour_rgb(24,20,37)) GM will still error if I use it as a case for a switch statement.

    E.g:

    Switch (color) {
    case c_black32:
    dothing()
    break;
    }

    will throw an error stating that you can only use constants as cases. However, I was under the impression that macros were constants. Am I wrong or is this an oversights?
     
    Cpaz likes this.
  9. Cpaz

    Cpaz Member

    Joined:
    Jun 20, 2016
    Posts:
    308
    Macros are actually not constants. Macros are a snippet of code consolidated to a single keyword. At compile time the keyword is replaced with the snippet.
    So in terms of usage it's kinda like a constant. But ultimately, they're pretty different.
     
    Last edited: Jul 16, 2019
  10. GMWolf

    GMWolf aka fel666

    Joined:
    Jun 21, 2016
    Posts:
    3,363
    @iRhymeWithRawr
    To elaborate on @Cpaz s answer, macros are pretty much like a find and replace functionality.
    If you have '#macro foo bar' any instance of 'foo' will get replaced with 'bar'.
    Apparently this isn't exactly what really happens (something to do with it being done at the AST level) but in my experience that is how it behaves.

    That means you can even have part of statements as part of your macro.
    For example:
    Code:
    #macro testXGreaterThan if x >
    
    testXGreaterThan 5 {
      //Do something
    }
    
    Expands to
    Code:
    if x > 5 {
      //Do something
    }
    
    Of course I don't recommend you do this, it's pretty horrendous code. But it demonstrates how macros are not exactly constants.

    This makes macros more powerful as you can use them to define often used code.
    For example
    Code:
    #macro RANDOM_COLOUR make_colour_rgb(random(255), random (255), random (255))
    
    image_blend = RANDOM_COLOUR;
    

    Macros not being constants does mean you need to take more care designing them.
    Consider the following:
    Code:
    #macro sum1and2 1+2
    
    X = sum1and * 3;
    
    You may think the result would be 9. But in fact you will get 7. That is because the macro is first expanded, then evaluated. So you get 1+2 * 3. Which is equivalent to 1 +(2*3). Which is 7.
    So instead, define your macros with perens:
    Code:
    #macro sum1and2 (1+2)
    
    Of course all of this is not super well defined and could change at any moment. Perhaps even now what I wrote doesn't hold up any more. So try to keep your macros simple!
    GML QUESTIONS:
    1. Did I get this right?
    What is up with the AST level substitutions, and what does it mean for out macro definitions?
     
    Cpaz likes this.
  11. Nux

    Nux Member

    Joined:
    Jul 7, 2016
    Posts:
    371
    Macros are the most powerful tool in GML, in my opinion. There are some really cool things you can do with them, such as: overriding existing built-in functions, scripts, and variables/constants:
    Code:
    #macro show_debug_message scr_log
    /// @desc Writes this message to a file.
    /// @param msg {String} The message to write.
    var file = file_text_open_append("log");
    if (file != -1) {
        file_text_writeln(file);
        file_text_write_string(file, msg);
        file_text_close(file);
    }
    This will make all your show_debug_message calls be routed to this new script, which writes that message to a file instead of the console. Pretty amazing stuff.

    Another thing which I've found useful is overriding mouse_x and mouse_y to add gamepad support to a mouse cursor, without having to rewrite all your code to support that.
     
    Bart and Cpaz like this.
  12. wipeout2185

    wipeout2185 Member

    Joined:
    Feb 3, 2017
    Posts:
    42
    GML QUESTION: How expensive is creating and destroying objects in game. For single use instances such as bullets or FX is it better to destroy the instance or deactivate it. Should you create all required instances at room start and just deactivate/activate or recycle as needed. I know every situation is different but the garbage collector is a black box so I'm not sure which is best.
     
    Last edited: Jul 30, 2019
  13. rmanthorp

    rmanthorp YoYo Games Staff Admin YYG Staff

    Joined:
    Apr 15, 2016
    Posts:
    287
    Nux and wipeout2185 like this.

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice