in , , , , ,

[GameMaker Tutorial] Introducing GameMaker Tutorial Series

GameMakerBlog Tutorials

Introducing the GameMaker Tutorial Series

As I am always trying to learn more about GameMaker; frequently I find myself visiting the great GameMaker Community tutorials section to see what new gems the community may have posted up. While I was browsing the forums a pattern emerged: Fundamental questions are always being asked and the responses are usually redirects to youtube videos. While I can and have learned from youtube video tutorials often, when I am looking for a specific answer to a question I prefer reading text; I just absorb that medium better than watching a video when it comes to understanding and processing code. It turns out that I am not the only one to want a more fundamental explanation as seen in this post by FrostyCat: Holding tutorial content accountable to novice aptitude on the GMC where it is described that instead of learning why you would use a function or snippet of GML it is instead just a quick “copy and paste this code” with no deeper explanations given; you cannot reapply that knowledge in other situations if it is not explained why you are using it in the first place.

To this end I have attempted to create a new Tutorial Series which you can find listed out on this page: GameMakerBlog Tutorials and it is my hope this will prove useful to someone.

Why not just use GameMaker Community forums?

The GameMaker Community forums is, as I described earlier, a great source of information from some truly smart and experienced developers. The tutorials are comprehensive, the reply’s to questions are in-depth; however, it can be daunting and time consuming to continually search through every post to try and piece together all the information you need. This leads us to the first objective of the series: To put related forum postings to the exact tutorial question being answered at the bottom of the post. The second objective is to try and explain (as best as I can) not only the “how” to do something but the “why” and give examples of extending what was learned to other gameplay ideas. The last objective, and this is a personal one, is to try and explain core fundamentals through real world analogies; and this will not be the first time this was done, FrostyCat has already incorporated this style into an excellent tutorial on Objects vs Instances: What’s the Difference: Objects and Instances in which the following was said:

“Using a physical analogy, “Human” and “Dog” are objects, but “Alice”, “Bob”, “Fido” and “Lassie” are instances.”

When learning a concept, I find it always supportive and easier to understand when I can relate the idea to something physical, something tangible that I already know; and while different people learn different ways I do not think this “analogy” style of reinforcement would ever degrade the learning process but only reinforce it.

A sample of things to come

So that this is not just an empty post, I wanted to give a small analogy based explanation of two very important data structures in GameMaker Studio 2: The List and Map data structures.

Lists and Maps are based off a base structure called an “Array”, which is described in-depth here

  • List: Documentation = DS_LISTS
    • A list is a sequentially ordered collection of items, each item is assigned an index based on the location in the list. The first item would be index 0, while this may seem strange at first, all lists begin at index 0. There are as many reasons as there are debates but suffice to say, just remember that list indexing starts at 0.
    • Real World Example
      • Picture a single file line of cars sitting at a red light, the car closest to the red light is at index position 0 and each car behind that first car increments the index by +1.
      • Now let’s pretend there is a traffic officer that inspects every car in the line, and in order to see if each driver is wearing a seat belt or not. At this point the officer does not care about what car is at what index, instead the only thing that matters is inspecting the next car in the line.
      • What happens when the officer finds a car that contains a driver not wearing their seatbelt? They will know what position (index) the car is at but that car needs to be uniquely logged in some way and later processed back at the police station for a ticket violation.
    • Beginner GML Code example
      1. *NOTE* I want to just take a few minutes to talk about code standards in general, there are many ways to achieve the same outcomes in code and a lot of it depends on personal preference. Some guidelines that should always be followed though:
        • Be consistent in your variable case style: There are two general styles when casing variables
          • lowerCamelCase: The first letter of the variable is lowercase and every letter of a new word is uppercase. Examples: firstName, streetAddress, postalCode, phoneNumber
          • underscore_notation (Snake Case): This is a style that uses an underscore to split up words. Comparative Examples: first_name, street_address, postal_code, phone_number
          • Using any case is really a matter of preference, no matter how passionate one developer may be about which is the “best”. The important thing is to remain consistent, if you use the same case throughout the entire game code every developer (including your future self) will know what to expect and not have to adjust their mindsets when encountering new variables
        • Use meaningful variable names
          • It is really easy to fall into the trap of quickly typing code using placeholder variables because you want to get the idea working. I cannot recommend this, even if the intent is to “go back after it is working and clean it up”, the chances are high that you will not. When it comes time to fix a bug you will spend mental resources trying to figure out what a certain variable was used for.
            • Example of confusing variables
              a = 16;
              if (a < 18)
              {
              m = "No Access Allowed";
              }
              else
              {
              m = "Access Granted";
              }
              
            • Now you may be able to guess what the above code block is logically testing but the problem is that you really need to think about and interpret the code. Imagine every line of code having this issue and you can soon see what a support nightmare this would become.
          • Example of meaningful variable names
            currentAge = 16;
            minimumAge = 18;
            if (currentAge < minimumAge)
            {
                 gameAccessMessage = "No Access Allowed";
            }
            else
            {
                 gameAccessMessage = "Access Granted";
            }
            
          • The code above should be self-explanatory as to what is happening logically, and yes it is more verbose and longer to type but anyone who attempts (even yourself) to modify, troubleshoot or just read through it will appreciate the extra time spent in making things clear.
      2. One more very small point to consider, if you look at the two code samples above you will see a slight difference in the way the IF statement appears; the bad example has all content on the left with no indenting. Now I am sure a lot of people reading this have seen the great show: Silicon Valley on HBO and watched or heard the famous Tabs vs Spaces debate scene in which Richard says tabs are superior for indenting over manually hitting the space bar.
        • Regardless of which you prefer, you should always indent nested code blocks for readability. Long blocks of nested code can quickly become confusing when trying to figure out what code belongs to a specific else or else if statement when each block is not indented.
        • On the same topic, there is another extremely heated debate that comes up: “curly brace style”. This is commonly referred to as using “cuddled elses”, and simply relates to where you put the curly braces: on the same line as the statement or a new line.
          • Cuddled Else (the one true brace style)
            if (currentAge < minimumAge) { 
                 gameAccessMessage = "No Access Allowed"; 
            } else { 
                 gameAccessMessage = "Access Granted"; 
            }
          • New Line Formatting (Allman style)
            if (currentAge < minimumAge) 
            { 
                 gameAccessMessage = "No Access Allowed"; 
            } 
            else 
            { 
                 gameAccessMessage = "Access Granted"; 
            }
          • Read about all styles in this Wiki post as there are many different ones to choose from
          • This comes down to personal preference again, neither way is inherently wrong and you will receive opinions from all style supporters equally (though the cuddled else crowd appears to be more vocal and unwavering); the most important practice is to once again remain consistent. As long as you stick to a style throughout the entire game code base it will remain as one conscious reading and you will not have to flip between different mindsets.
  • Create a list: carsInLine = ds_list_create();
    • This statement creates and returns a list object id that you will use later when manipulating the list
  • Add values to the new list: ds_list_add(carsInLine,”Car 1″,”Car 2″,”Car 3″,”Car 4″,”Car 5″,”Car 6″);
  • Go through the cars in order and look for a driver not wearing a seat belt (in this case all Odd number cars are not wearing a seatbelt because they are well, odd)
    noSeatBeltMessage = " -> Driver is not wearing a seat belt";
    carsInLineSize = ds_list_size(carsInLine);
    for(currentCar = 0;currentCar < carsInLineSize;currentCar++)
    {
    	if ((currentCar+1)%2 != 0)
    	{
    		carInViolation = ds_list_find_value(carsInLine,currentCar);
    		show_debug_message(carInViolation+noSeatBeltMessage);
    	}
    }
  • Now view the output on the console and you should see

    Car 1 -> Driver is not wearing a seat belt
    Car 3 -> Driver is not wearing a seat belt
    Car 5 -> Driver is not wearing a seat belt
  • I am going to skip ahead somewhat at this point as this is just supposed to be a sneak peek at what will come but I do want to touch upon the Map
  • Now that we found our cars that are in violation, how do we logically identify them later at the police station? We need to make note of them in a separate place and trying to just keep their current index would be impractical, after all we would not do that in real life. The answer is to use a Map.
  • Map: Documentation DS_MAPS
    • A Map is a structure that associates a Key with a Value and we use this everyday when we think of something:
      • When you hear someone’s Name (Key) you think of their Face (Value)
      • Asked for a login Password (Key) you supply your password value (Value)
      • Someone on the street asking your dog’s Breed (Key) and you say German Shepherd(Value)
    • Some pointers to keep in mind when working with a Map
      • Each Key in the map needs to be unique, but values do not
        • If our first example above, when you hear the name Jon you could start thinking of many different people all named Jon which is a problem. If instead you heard Jon Snow, you would immediately start thinking of Game of Throne’s Jon Snow, the one that knows nothing.
        • Similar to above, you could have someone say “Jon Snow” (Key) and picture the correct Face (Value) and if someone also says “Jon of the Night’s Watch” (Key), you will still see the correct Face (Value). The same value can show up linked to multiple keys without any issues.
      • Maps are not in any particular sorted order
        • It is important to know that maps are not in order and if you should need to find a specific key you would have to iterate through each entry looking for the one you require. This is bad practice and could result in a considerable performance impact.
        • On the other hand; if you know the Key for which you need the Value for, then looking this up in the Map is very fast
        • Using the second example, let us say that we stored our game’s admin password in a Map and want to retrieve it
          • If you do not know the Key that you specified, you would have to search through the entire Map element by element searching for a Key that might resemble something you named to the word “Password”
            • This is very slow and not a good idea
          • Instead, if you had used a Key called “Password”, you could easily just call ds_map_find_value(id, key)
            • The ID in this function is the id of your Map when you created it, the Key would be “Password”
    • Getting back to our original dilemma, we now have cars that have been identified with a driver not wearing a seat belt and in need of receiving a violation; what should we do?
      • Create a Map: carsNeedingViolations = ds_map_create();
      • *Slightly skipping ahead and providing a summary: Have the officer ask for the vehicle registration plate number and store that as the Key for the map and the car whose driver is in violation as the Value.
      • Later at the police station we can send out violation notices in the mail to violators we caught based on the vehicle registration plate which is a unique key
      • Full Code Example
        randomize();
        carsInLine = ds_list_create();
        carsNeedingViolations = ds_map_create();
        ds_list_add(carsInLine,"Car 1","Car 2","Car 3","Car 4","Car 5","Car 6");
        noSeatBeltMsg = " -> Driver is not wearing a seat belt";
        noSeatBeltFine = " -> Please Pay 20 gold coins";
        licensePlateMsgPart = " with License Plate: ";
        carsInLineSize = ds_list_size(carsInLine);
        for(currentCar = 0;currentCar < carsInLineSize;currentCar++)
        {
        	if ((currentCar+1)%2 != 0)
        	{
        		carInViolation = ds_list_find_value(carsInLine,currentCar);
        		show_debug_message(carInViolation+noSeatBeltMsg);
        		licensePlateFirstLetter = choose("A","B","C","D");
        		licensePlateSecondLetter = choose("E","F","G","H");
        		licensePlateLetters = licensePlateFirstLetter+licensePlateSecondLetter;
        		lincesePlateNumber = floor(random_range(100,250));
        		licensePlate = licensePlateLetters+string(lincesePlateNumber);
        		licensePlateMsgFull = carInViolation+licensePlateMsgPart+licensePlate;
        		ds_map_add(carsNeedingViolations,licensePlate,licensePlateMsgFull);
        	}
        }
        show_debug_message("Creating Mail Violation Notices");
        violatorsMapSize = ds_map_size(carsNeedingViolations);
        licensePlate = ds_map_find_first(carsNeedingViolations);
        driverInViolation = ds_map_find_value(carsNeedingViolations,licensePlate);
        for (currentCar = 0; currentCar < violatorsMapSize; currentCar++;)
        {	
        	show_debug_message(driverInViolation+noSeatBeltFine);
        	licensePlate = ds_map_find_next(carsNeedingViolations, licensePlate);
        	driverInViolation = ds_map_find_value(carsNeedingViolations,licensePlate);
        }
        
      • And the outputting result:

        Car 1 -> Driver is not wearing a seat belt
        Car 3 -> Driver is not wearing a seat belt
        Car 5 -> Driver is not wearing a seat belt
        Creating Mail Violation Notices
        Car 5 with License Plate: BH141 -> Please Pay 20 gold coins
        Car 1 with License Plate: BE132 -> Please Pay 20 gold coins
        Car 3 with License Plate: CF101 -> Please Pay 20 gold coins
      • Take special notice of the order in which the Mail Notices were created, they are not in order and if you run the game a few times you will see that the order can change every time it is run. This is because as stated previously, Maps are not ordered or sorted in any deterministic way, if you need that functionality you will have to create your own way to sort.

This ended up longer than I thought

As I was creating the preview for describing a List and Map using a real world example it was tremendously difficult to not just continue and go line by line explaining every function but this post is not for that. This is simply a launching post to show what will be happening and the above is only one example from the Analogy Learning series. It is my hope that these examples help to shed light on some of the methods and GML code that can be used and applied in many different ways; I do want to point out that the code examples are just that: Examples. They contain not only verbose variables but are designed to help identify steps, these are not efficient production ready snippets and while I encourage everyone to use them in a New Project for testing and learning I cannot put my stamp of approval on putting this code in an actual game.

Everyone is in this together

If I can get the GameMaker Community involved, this could turn into a great place for beginners using GameMaker Studio 2 for the first time and if that happened I would be over-joyed; more game developers would mean more ideas, more marketplace assets and of course more Games. It is not easy to start anything new, whether a game or even posts like this and if I can make life just a little bit easier for someone than that would be Mission Accomplished. If you want to help in any way or just have pointers, tips, corrections or criticisms please leave me a comment below or use the Contact Us form.

As said in the beginning of this post, I will put every tutorial listed in a separate page for ease of organization: Tutorials

Good Luck in all things,
Hikati Games

Hikati Games LLC Logo

What do you think?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

GameMaker Studio 2 Adds Console Export Modules

[GameMaker Tutorial] Top Down: Make an enemy unit face the player