1. Hello Guest! It's with a heavy heart that we must announce the removal of the Legacy GMC Archive. If you wish to save anything from it, now's the time! Please see this topic for more information.
    Dismiss Notice

GMC Forums GMC_Archive - Tutorials

Discussion in 'Community Chat' started by Nocturne, Nov 23, 2019.

  1. Nocturne

    Nocturne Friendly Tyrant Forum Staff Admin

    Joined:
    Apr 13, 2016
    Posts:
    7,045
    This topic is for storing any tutorials that are still relevant to GameMaker Studio 2, culled from the defunct old GMC Archive. When posting please follow these simple rules:

    1) All tutorials must be 100% compatible with GameMaker Studio 2. If you wish to post a tutorial that is not compatible, then update it to be compatible before posting.

    2) Please only make ONE post per tutorial. If the source material is too long, then we will permit consecutive posts, but try to keep it all as concise as possible (verbatim copy/paste is not required). Use code blocks and spoilers where appropriate to keep post size down.

    3) Please give credit to the user that originally posted the topic or post that you are culling the tutorial from. Simply putting "OP: {name}" at the top of the post is sufficient.​

    Anything that is posted here that does not adhere to these rules will simply be removed. We want this to be a repository of the best of the knowledge contained in the old archive! Keep in mind that (as always) posts are subject to the usual forum rules and moderator discretion will be used to decide what is permitted and what is not.

    NOTE: For general code from the Programming or Advanced Programming forums, please go here: https://forum.yoyogames.com/index.php?threads/gmc-archive-programming.69625/

    Thank you all for participating in this, and we hope that it will be useful to future users for years to come!

    Nocturne
     
    Last edited: Nov 23, 2019
    Amon and Cpaz like this.
  2. FrostyCat

    FrostyCat Member

    Joined:
    Jun 26, 2016
    Posts:
    4,699
    OP: torigara

    What's the Difference: Collision Functions
    Original by Torigara
    GMS 2 Expansion by FrostyCat

    GM Version:
    GMS 2
    Target Platform: ALL
    Download: N/A
    Links: GM5+ original

    Overview
    There are a number of functions to check if there is something at the specified location. This tutorial describes differences of those functions to let you choose an appropriate one (especially between the common misused two, place_meeting() and position_meeting())

    Summary
    The table below summarizes the characteristics of GMS built-in collision-checking functions:
    Code:
    Function                   Checks using   Checks against              Returns
    
    Standard 4
    ------------------------------------------------------------------------------------
    place_meeting              sprite/mask    specified object/instance   true/false
    position_meeting           point          specified object/instance   true/false
    instance_place             sprite/mask    specified object/instance   instance id
    instance_position          point          specified object/instance   instance id
    
    collision_*() series
    ------------------------------------------------------------------------------------
    collision_point            point          specified object/instance   instance id
    collision_line             line           specified object/instance   instance id
    collision_rectangle        rectangle      specified object/instance   instance id
    collision_circle           circle         specified object/instance   instance id
    collision_ellipse          ellipse        specified object/instance   instance id
    
    *_list() series
    ------------------------------------------------------------------------------------
    instance_place_list        sprite/mask    specified object/instance   length of list
    instance_position_list     point          specified object/instance   length of list
    collision_point_list       point          specified object/instance   length of list
    collision_line_list        line           specified object/instance   length of list
    collision_rectangle_list   rectangle      specified object/instance   length of list
    collision_circle_list      circle         specified object/instance   length of list
    collision_ellipse_list     ellipse        specified object/instance   length of list
    
    Mask-free geometric series
    ------------------------------------------------------------------------------------
    point_in_rectangle         point          rectangle                   true/false
    point_in_triangle          point          triangle                    true/false
    point_in_circle            point          circle                      true/false
    rectangle_in_rectangle     rectangle      rectangle                   0/1/2
    rectangle_in_triangle      rectangle      triangle                    0/1/2
    rectangle_in_circle        rectangle      circle                      0/1/2
    
    Solid-checking series
    ------------------------------------------------------------------------------------
    place_free                 sprite/mask    solid instances             true/false
    place_empty                sprite/mask    any instance                true/false
    position_empty             point          any instance                true/false
    
    Place vs. Position
    The most important difference is whether the function name contains place or position Functions starting with place always put the current instance that is executing the code at the specified position and checks if its sprite (or mask if assigned) collides with another object.

    On the other hand, functions named position checks the exact point, regardless of the instance that is executing the code.

    placevsposition.png

    Here are some consequences:
    • place_*() functions don't work at all if the instance executing the code has no sprite or mask.
    • place_*() functions never detect collision with itself, whereas position functions can be used with self.
    Code:
    // This one doesn't work no matter whether the instance has a sprite.
    if (place_meeting(mouse_x, mouse_y, id)) { ... }
    
    // This one correctly checks if the mouse is on the instance.
    if (position_meeting(mouse_x, mouse_y, id)) { ... }
    
    Meeting vs. Instance
    When you use a function that has meeting in its name, it just tells whether there is a collision and returns either true or false. On the other hand, functions starting with instance returns the colliding instance's id, or the special value noone (-4) when there is no collision.

    You may sometimes see that the return value of instance_place() or instance_position() is used as if it is boolean.That works because Game Maker's if statement evaluates values greater than or equal to 0.5 as true and all others as false. Therefore, if (A) behaves like if (A >= 0.5), and if (!A) behaves like if (A < 0.5). It enables you to use instance_place() or instance_position() in place of place_meeting() or position_meeting(). However, note that the opposite doesn't work.
    Code:
    // This checks if there is an instance of object0 under the mouse,
    // and store its instance id to the variable inst.
    // The result is noone (-4) if there is no collision.
    inst = instance_position(mouse_x, mouse_y, object0);
    if (inst) { // This is equivalent to saying "if (inst > 0)"
        inst.selected = true; // Manipulate the instance's variable
    }
    
    With _list vs. Without _list
    The following functions come in two variants, one as-is and one with _list added to the end. Note that they all start with instance_ or collision_.
    • instance_place / instance_place_list
    • instance_position / instance_position_list
    • collision_point / collision_point_list
    • collision_line / collision_line_list
    • collision_rectangle / collision_rectangle_list
    • collision_circle / collision_circle_list
    • collision_ellipse / collision_ellipse_list
    The variants without _list at the end return an instance ID upon finding at least one collision, or noone (-4) if there are none. If there are multiple collisions, one of the colliding instances will be returned. They are typically used in one of 3 forms:
    Code:
    // Local variable form
    var inst = instance_place(x, y, obj_enemy);
    if (inst != noone) {
        /* Use inst here to refer to the colliding enemy instance */
    }
    
    Code:
    // with block form
    with (instance_place(x, y, obj_enemy)) {
        /* Code here runs from the perspective of the colliding enemy instance */
        /* The instance calling this code is other */
    }
    
    Code:
    // with block form 2 (imitating collision events)
    with (instance_place(x, y, obj_enemy)) with (other) {
        /* Use other here to refer to the colliding enemy instance */
    }
    

    In contrast, the variants with _list at the end are characterized by 2 additional properties:
    • They return the number of collisions found.
    • They take 2 more arguments than the variant without _list: 1) The list to insert into, and 2) whether to order the list by distance.
    These variants are used when multiple collisions are likely, and you want an action applied to all of them. When you use these functions, you should create a list to deposit instance IDs into (don't forget to clean them up when appropriate), then use a for loop with the returned number of collisions to run through the list.
    Code:
    // Local variable form
    for (var i = instance_place_list(x, y, obj_enemy, list, false)-1; i >= 0; --i) {
        var inst = list[| i];
        /* Use inst here to refer to the colliding enemy instance */
    }
    
    Code:
    // with block form
    for (var i = instance_place_list(x, y, obj_enemy, list, false)-1; i >= 0; --i) {
        with (list[| i]) {
            /* Code here runs from the perspective of the colliding enemy instance */
            /* The instance calling this code is other */
        }
    }
    
    Code:
    // with block form 2 (imitating collision events)
    for (var i = instance_place_list(x, y, obj_enemy, list, false)-1; i >= 0; --i) {
        with (list[| i]) with (other) {
            /* Use other here to refer to the colliding enemy instance */
        }
    }
    

    Point-in vs. Rectangle-in
    Functions starting with point_in and rectangle_in work on abstract shapes only. These are useful when neither party of a collision to check are actual instances with masks, such as the mouse cursor, drawn graphics, views, or other regions of a room not demarcated by the presence of instances.

    The most important difference between these two kinds of functions is their exact return value. Functions starting with point_in return true when the point is inside the shape, and false otherwise. In contrast, functions starting with rectangle_in return one of 3 numeric values: 0 for no collision, 1 for when the rectangle is entirely within the shape, 2 for when the rectangle is partially inside.

    Like instance_place() and instance_position(), functions starting with rectangle_in are also commonly used as though they're booleans to denote any collision (i.e. counting both complete and incomplete overlaps). 0 is less than 0.5, and is thus considered false. 1 and 2 are greater than 0.5, and are thus considered true.

    Free vs. Empty
    As summarized above, the difference between place_free() and place_empty() is rather trivial. The former checks if there is a solid instance, while the latter checks for any instance including non-solid ones. Note that they return true when there's not a collision, as opposed to place_meeting() and position_meeting(). There is position_empty() but no corresponding function position_free(), though.

    Troubleshooting Tip #1: "Don't Compare Apples to Oranges."
    A common novice mistake with collision checking is to compare the results of functions to the wrong values, most notably true and false. Here are some examples:
    Code:
    if (instance_place(x, y, obj_enemy) == false) {
      ...
    }
    if (collision_line(x, y, x, y-400, obj_enemy, false, false) == false) {
      ...
    }
    
    The above can be easily avoided by memorizing the return value types of collision functions, and more generally by using implicit booleans instead of comparisons to true and false.

    Troubleshooting Tip #2: "Don't Use Apples to Make Orangina."
    A related mistake is using the return value of place_meeting() or position_meeting() to access an instance. Here are some examples:
    Code:
    with (place_meeting(x, y, obj_enemy)) {
        ...
    }
    with (position_meeting(mouse_x, mouse_y, obj_button)) {
        ...
    }
    
    This mistake can be especially insidious in smaller novice projects and proof-of-concepts, where the first 2 objects may play important roles or are the only objects in the project. When this happens, the mistake runs quietly and have a high chance of appearing to work, only beginning to fail out loud in larger projects. To make sure you aren't committing this mistake, test your proof-of-concepts with 2 unused, error-showing objects at the top of the resource tree (i.e. show_error() in their Create events). Accidental references will always be called out this way.

    Troubleshooting Tip #3: "Check the Mask of the Collision Checker."
    Another common novice mistake is to forget that a place function uses the sprite of the current instance that is calling the function. See the following example code: it is executed by a controller object and is meant to create an enemy in a random place that doesn't collide with walls. This likely fails to work. Why? A controller object generally has no sprite or mask, so place_free() detects no collision for the controller.
    Code:
    // This code is meant to create an enemy in a free place
    // but doesn't actually work.
    var xx, yy;
    do {
        xx = random(room_width);
        yy = random(room_height);
    } until place_free(xx, yy);
    instance_create_layer(xx, yy, "Instances" obj_enemy);
    
    To make it work properly, we have to either give the controller object a sprite, or make the enemy object itself check for collisions.
    Code:
    // Create the instance first (the initial position doesn't matter.)
    // Then make it find a free place itself.
    with (instance_create_layer(0, 0, "Instances", obj_enemy)) {
        do {
            x = random(room_width);
            y = random(room_height);
        } until place_free(x, y);
    }
    
     
    gnysek and Desert Dog like this.
  3. FrostyCat

    FrostyCat Member

    Joined:
    Jun 26, 2016
    Posts:
    4,699
    OP: paul23

    Dos and Don'ts in GameMaker
    By paul23


    GM Version: GMS 2
    Target Platform: ALL
    Download: N/A
    Links: Original

    Summary
    This “tutorial” will describe many guidelines on how to write clearer, “more elegant” code. More experienced programmers will find this tutorial to be “stating the obvious”. That’s exactly correct: this tutorial IS stating the obvious. However too many times I’ve seen people not knowing this, or at least not following the obvious better code. With this tutorial I hope people realize what a mess they make and how to improve it.
    During this tutorial several patterns and anti patterns will be listed. They’ll be described why they should (not) be made and what better alternatives there are. I'll often talk about "functions", while -apart from build in- functions don't strictly exist in gamemaker, for all purposes scripts are functions, and should be handled as such.
    This tutorial is not about teaching new code, instead it is about improving your own programming skil. Hence a basic knowlenge of gml is required. The tutorial is also not only for gamemaker, the rules for elegant code apply to almost any language.

    Dos and Don’ts in gamemaker

    This “tutorial” will describe many guidelines on how to write clearer, “more elegant” code. More experienced programmers will find this tutorial to be “stating the obvious”. That’s exactly correct: this tutorial IS stating the obvious. However too many times I’ve seen people not knowing this, or at least not following the obvious better code. With this tutorial I hope people realize what a mess they make and how to improve it. I'll often talk about "functions", while -apart from build in- functions don't strictly exist in gamemaker, for all purposes scripts are functions, and should be handled as such
    During this tutorial several patterns and anti patterns will be listed. They’ll be described why they should (not) be made and what better alternatives there are.

    First you have to realize that good code matters (heck there’s a book even named after this). On these forums coding often gets thrown to the background, with the argument “designing is most important once you design the code is simply a piece of cake”.
    WRONG
    Programming is not a piece of cake, it requires experience, careful planning and good designing. There are people who advocate that tools such as gamemaker make the need for programming (skills) obsolete. This is wrong, there will always be a step between your design, and a formal language a computer understands. And as long as such a translation is being made, it is important to write clean code. A good programmer is not somebody who knows all commands and how to apply them, that is what reference material is for (and as we see through this tutorial it is often not necessary). A good programmer is someone who writes elegant code and know how to design code.
    To start off let’s look at an example of how we shouldn’t write code:
    This entry for the iocc.. Found at the IOCCC (international obfuscated c code challenge). Now if you desire to create such code I suggest you stop reading. Those who like to write "slightly" more readable code should read on.


    What is clean code

    To understand what clean code means, we have to notice the audience of your code. Let us look at the standard life-cycle of a program (and hence code): First you design your code, you work out algorithms, proof that those are working, than you write your code, after which maintenance starts. Maintenance takes generally more than 80%[1] of the project time. So if you manage to cut down on maintenance time it will pay off greatly. For this reason we want to write clean code.
    So we write code for future maintenance programmers.
    This is important to realize and you should never forget this, you don’t write code for yourself. It’s written for someone who has not the experience you had during writing. And he or she shouldn’t spent more time than necessary. Even if you’re completely staying “indie”, a term popular around this community, you should still plan to write code for someone else. By the time you’ll review your old code you will have forgotten what you were thinking and you’re a “newcomer” to your code.
    The main problem with maintenance of bad code is, is that it always lead to “code rot”. Code rot means that code becomes harder and harder to understand. Trying to “fix” code leads to more bugs elsewhere. Ugly code hence leads to maintenance programmers fearing changes and instead of fixing, the problem is patched – Which leads to even worse code. Eventually code is so ugly that no one understands it anymore and the only sensible thing to do is a complete rewrite. I’ve experienced this myself, “gpathfinding” is currently in a state where I can only rewrite the code from base.
    The alternative, writing clean code from scratch, means that maintenance can operate without patching, instead maintenance programmers can fix the code. And by fixing instead of patching code rot is prevented and no rewrite is needed.

    But but…

    There are lots of counter arguments people bring up here to not write clean code. Some even more absurd than others.
    The weirdest thing I’ve encountered was the argument “but I want people to not understand my code, so they can’t steal it”. This is a complete anti-argument, and it shows you have a complete misunderstanding of for whom code is meant – code is meant to be read. If you must hide your source for certain people use a tool as the final step to obfuscinate/compile your code. But that is not the code-base you store or share.
    Another reason I’ve seen popping up often is “but it’s faster”. This argument shows some kind of ignorance. It shows that you are busy micro-optimizing during the programming phase. Not only do you not know the bottle necks, but you might actually prevent future optimizations on certain more important parts. On top of that you have to realize what tool you are using: game maker. Game maker itself isn’t the fastest tool, and if your aim is writing fast good in spite of readability I suggest not starting with game maker.
    …but…
    Now of course there are exceptions, I’m well aware of this. Gamemaker itself leads to quite a few ugly forms of code. (Not being able to pass arrays around, not having member function, terribly long arguments lists on functions, no scope definitions etc etc). You’ll often have to choose between two evils, but that doesn’t make it good. Always try to aim for clean code, look if you can’t redesign the code in a better manner, and keep the ugly parts to a minimum.

    Clean code tips and tricks
    As I started with programming is all about writing clean code. Programming can’t be learned in 1 day, and it requires experience. Clean code isn’t a “fail/pass” thing, it’s very subjective, difficult to define. During this part I’ll try to name some hang up points which you can use as guidelines to write clean code. Following these should make the code at least “readable”. Rmember these are guidelines, and not hard rules, always choose what works best in your specific case. I ordered the guidelines by their importance and easy-to-implement. The top are very important, yet are straight forward to implement in your program (and hence should always be applied).

    Now before we go into detail first discus a few generic things. As we know by now, code is written for maintenance programmers. Your goal is to make it these people as easy as possible. Just like writing an article this means that everything is expected, follow the rule of the least surprise[2]. People shouldn’t have to look into the implementation details when only working on the interface. And when looking at the implementation everything should be at the location where it is expected.
    • DO indent your code.
      The very first and most important rule. Many articles never state this. Simply because it’s so obvious. Indent your code in a concise matter. The actual way isn’t too important, as long as it’s generally accept to be good. I myself use the K&R style as it reduces vertical scrolling.
    • DO use whitespace well
      Whitespace is the most powerful tool you as programmer have to create readable code. Whitespace allows you to let your users focus, it draws attention to things. Just like in an article adding paragraphs make code readable so does whitespace in programming. However too much and too little whitespace is bad. Too much whitespace leads to incoherent code, you don’t know what belongs to what. Too little whitespace lead to an overwhelming feel a “TL;DR” moment. Both are terrible.
      Now what is good whitespace? Well there are a few simple guidelines one can follow
      • always use whitespace around assignment/comparison/mathematical punctuation.
      • use a whitespace symbol before an opening (left) parenthesis, and after a closing parenthesis
      • exception to above is when the opening parenthesis is the start of an argument list of a function. As the argument list belongs to the function closely they should be tied together
      • after a comma/semicolon follows whitespace.
      These rules will give the basic white-spacing, though it requires a lot of experience and testing to see what is really “best”.
    • When in rome DO as the romans do.
      This saying counts for many things, and it goes hand in hand with the rule of least surprise. It means that whenever you use a certain engine or work with a team, you should adapt your style to what the engine/team have decided. Don’t mix & match styles.
    • DON’T use magic numbers in code.
      This is one of the most often violated anti pattern in gamemaker. And it’s also one of the oldest paradigms in programming. There is not a single reason to use a hard-coded magic number in code. An example is the “map_width”, ie let’s consider you make a chess game, now to see if a move would be legal we first check if it is “on the board”:
      if (x < 8 && y < 8)
      But what later, when you are maintaining your project, what did you mean by “8”, does it hold a special value? – Why “8”?. Much more readable would be:
      if (x < MAP_WIDTH && y < MAP_HEIGHT)
      It describes exactly what you mean, hence making code more readable. It also means maintaining the project (maybe you want a bigger map?) easier. As it prevents errors when forgetting to update.. This counts for any constant, way, way too often I see questions about blend modes, and then the user puts something like:
      draw_set_blend_mode(3, 4); What does that mean? Nothing, nobody can understand that code without reading the help file. (which increases maintenance time).
    • DON’T use "=" for comparison.
      This isn't quite as trivial as it looks though. But in gamemaker "=" can be used for comparison, yet at the same time it also can mean an assignment. Assigning & comparing are 2 very distinct operations, having nothing to do with each other. So they shouldn't share the same symbol. Consider the following piece:
      var a, b;
      a = 0;
      b = 0;
      a = b = 1;

      What will "a" hold afterwards? - Most people with other programming background, but also most without any knowledge of programming, will interpret the last line as “set b to 1, set a to b”. – thus both hold “1” afterwards. Gamemaker will not do this, gm’s engine will read this as:
      a = (b = 1) which equals a = (b == 1) thus a would be “0”. Using “==” for comparison would’ve immediately removed the problem, we see that it is a comparison followed by an assignment.
    • DO name your variables and functions well.
      Nomenclature is a matter of making decisions as a team. Here the saying “do as the romans do” counts doubly hard. A good name should always describe what a function does. And as gamemaker lacks scope/namespaces the name should also implement the scope of the function/variable. A person who is adding code to your engine should never have to think about the name of a variable/function when adding/using it. The naming should immediately follow from the purpose. Of course names shouldn’t be bigger than necessary, using “i” as counter in loops is well understood by everybody and should be used as such.
      Doing this prevents naming collision. But much more importantly: it makes code that is self-commenting and hence makes reading code a lot faster. An example nomenclature I use I’ll quickly describe:
      • Any script/resource/constant I use for an engine starts with a 2 or 3 sign for the engine, followed by an underscore: ie “MLP_” for my mathematical/logical parser
      • all resources except scripts are then followed by an undercase abbreviation for the type of resource
      • Then the resource is named with separate words separated by capitals (not underscores). So a typical object would be: MLP_objUnaryOperator. A script would be: MLP_ParseFunction())
      • constants are typed in all capitals, words separated by underscores.
      • global variables start with 2 underscores followd by the engine ID: __MLPParserNumber
      • Local variables don’t have any prefix, but each word is separated by a capital.
      • script variables never use capitals, and use underscores to separate words.
      These are just 1 way of describing a nomenclature, but you can see that whenever I add something I immediately know how the thing should be named. Rests only what they are named. And that I know from the description of the function.
    • DO comment, DON’T overcomment
      Anyone who followed education in a computer science related field, and got programming courses will immediately remember how the tutor would hammer on commenting more and more. However this isn’t useful to production level code, the tutor wants comments as he can grade you on that, he has to know if you understood what you wrote.
      Clean code is self-commenting. The variable names, function names etc should explain the flow, not the comments. Commenting has always many, many drawbacks, and should be used sparingly.
      Comments lead to distraction of the actual code when used too often. They will require extra time spent on maintaining/updating the comments when fixing parts of your code. Always with the problem that a comment might not get updated due to time constaints or simply missing one. And then comments not only become a distraction, they are wrong and describe things that aren’t there. Also be very careful that you keep the text in arguments minimal. You are not writing a book and shouldn’t write your thoughts down. (Keep a separate document for this).
      Remember for who you are writing code: for maintenance programmers, programmers indeed. You can take it for granted that they know the language (GML) just as good/better than you know it.
      Take the following abysmal use of commenting, it looks up the phone number of a student (id) (if the student id is value) and returns that number:
      var student_id, student_phone_num, student_map;
      student_id = argument0; //initialize student id
      //if student exist
      if (student_id < NUMBER_STUDENTS) {
      student_instance = students[student_id];
      //this is done after the checking for the correct number of instances
      //to prevent out-of index errors on the arrays.
      //The student_instance can be accessed for all the other data, like phone numbers, courses etc.
      //Remember you need to check if the instance is still active before accessing this data.
      if (instance_exists(student_instance)) {
      //student exists
      phone_number = student_instance.phone_number;
      return phone_number; //return
      }
      }
      return 0; //returns 0 when non existing user number
      //initialize student id – No really? We can read too yes, we SEE you are assigning argument0 to student_id variable, don’t state the obvious
      //if student exist We see you’re doing an if-statement here, and with a little rewriting the code would be self commenting.
      //set the student_instance to the student used
      //this is done after the checking for the correct number of instances
      //to prevent out-of index errors on the arrays.
      //The student_instance can be accessed for all the other data, like phone numbers, courses etc.
      //Remember you need to check if the instance is still active before accessing this data.

      You like writing books? This just distacts
      //student exists Yup, we’re inside the if-statement, indentation told us that already.
      //return Orly? What else would you expect from a line starting with “return”
      //returns 0 when non existing user number a little rephrasing (maybe a constant) would mean the code is self commenting. Consider the following:
      var student_id, student_phone_num, student_instance;
      student_id = argument0;
      if (student_exists(student_id)) {
      student_instance = student_get_instance(student_id);
      phone_number = student_instance.phone_number;
      return phone_number;
      }
      return INVALID_STUDENT_ID;
      //student_exists(student_id)
      var student_id, student_instance;
      student_id = argument0;
      if (student_id < NUMBER_STUDENTS) {
      student_instance = student_get_instance(student_id);
      if (instance_exists(student_instance)) {
      return true;
      }
      }
      return false;
      //student_get_instance(student_id);
      var student_id;
      student_id = argument0;
      return students[student_id];
      Notice how 0 comments are necessary, everything explains itself, the function names help you following the execution path and there is nothing to withdraw attention of what is important – the code.

      Now what are good comments? Comments should speed up reading through functions, so the first commenting should always be a description of how a function is used, and what the arguments are. It should also describe the returned value. Make sure the header stands out clear from the rest of code, and follows an easy to read format. (For bigger engines I tend to use gmlscripts system of headers example).
      Apart from that “header” argument you might wish to add “TODO” type comments, to notify the user that a certain feature is yet to be created. Other comments inside code are generally bad and you ought to rephrase/refactor your code.
    • DO one thing in a function. Do one thing only.
      This another of those programming paradigms that has been with programming forever. It means that a function should do 1 thing, and only act on 1 level of abstraction. This is a bit more difficult to implement, it requires the programmer to carefully think about his functions. What is 1 thing, what do I want my function to do.
      Take for example our original student-phone number code, that script did 3 things in 1 function. Instead of splitting it up into 3 parts (which was done later). Our main goal is to get the phone number of a student (1). To do this we have to check whether a student exists (2), and also get the student_instance from the id (3).
      Clean code[3] had a good method to describe what is “one thing”. They described the “TO”-method, where we describe the function starting with “TO <functionname>”
      TO get_student_phone_number, we check if student exists. Then we get the student_instance which we use TO get the phone number
      It should now be clear this function did 3 things.

      A particular offense I see popping up quite a bit are that people pass “behaviour” variables to a function. I’ve seen an extension once that had a Boolean as second argument of an initializing function. If the Boolean was “false” the function would free the memory instead of initialize.
      Initializing and freeing are 2 distinct things. And should never be in the same function.
    • DON’T repeat yourself,
      DRY, another of those base principles created at the very beginning of programming. And still valid today. If you have to do a certain action multiple times, do never ever copy-paste the code at two (or more locations). Put the code into a function such that you call this function.
      Failing to do so will lead to confusion & maintenance problems, if you wish to change a certain feature you fear the functions go “out of sync”, and you’ll have to be very careful of fixing bugs.
    • DO keep function bodies small.
      A function in itself should be understandable without reading it more than a few times. It should completely fit in the “human working memory”. And as such don’t write function that are pages long. A small function leads immediately to self-commenting code. And as explained, self-commenting code leads to clean code.
      The question is: what is “small”. There is no answer for this question, and it is completely personal preference. However when you reach the triple digits functions are getting long already and you should be on the lookout.


    And a final, last but not least action everyone should undertake:
    DO learn another langue.
    Not because gamemaker is a bad language, but simply because it makes you a better, more complete programmer. By learning multiple languages you won’t stay around bad programming practices endorsed by a certain language. You will learn about other ideas and you will learn the morals of other programming languages. This means that when you look back to gamemaker you can make a well-funded reason why something should or should not be written in a certain way.

    Following these rules will benefit you always in the long run. Some principles might require a bit of planning and careful design, all require discipline and efford. But if you follow above rules and write your code clean, programming won't ever be an annoyance that "must be done", it'll feel like a breeze when reading previous code!


    How to write clean code

    The remaining part is: how do you write such code as described above. It seems a terribly long list with lots of things you have to take notice from. This is indeed true, and being very chaotic myself I can fully see the problem. It is also never meant to say that you should write code "clean" from the go, first it is important that you get to fix the problem. As only after you fixed the problem you see what was necessary to fix it, and only then you can decide how the code would look best.
    After that you should just "proof read" your code a few times. Be critical, maybe keep above list as a reminder next to you, and delete all those temporary comments. Refactor code so it is self describing. (Often this means splitting up functions into several parts). Rename variables so they're actually meaningfull. This requires indeed discipline and time, there's no direct gain from this. But I hope you understand by now the importance of clean code.


    Conclusion

    When writing code the first thing one should notice is that the audience are maintenance programmers, maintaining programmers typically takes about 80% of the programming time. For these programmers code should hence be clean and well written. Clean code will always share some traits, it means code is readable and self-commenting. It also means that code is "obvious", doesn't hold weird structures and where it must be done it is explained throughly. Finally clean code also means that code is formatted in a specific manner.

    With this tutorial I hope you will now look into your code and start refactoring it, with each look over your code try to make it a little bit better. I especially hope that examples written are actually clean code. It's a mindset you should have. Programming is not something you should just as a quick obligatory thing to design a game. Instead it's a field which requires just as much designing, planning & experience as any other design.



    [2]http://en.wikipedia.org/wiki/Principle_of_least_astonishment (june 2011)
    [3]Clean code: a Handbook of Agile Sofware Craftship, Robert C. Martin et al, 2009
     
    Desert Dog and cgPixel 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