-
Notifications
You must be signed in to change notification settings - Fork 0
Coding Standards
Welcome to the jam2020 wiki!## Contents New to Github
- Indentation
- New Lines (Vertical Spacing)
- Spaces (Horizontal Spacing)
- Braces
- Parentheses
- Logical Operators
- Commenting
- Some Other Notes
- To begin contributing, download the github desktop app here
- Clone the desired project: File>Clone Repositiory>Enter the URL of the repository page
- Create a branch as your name
- Hit publish branch
- Hit open this repository in explorer in the middle of the app
- Open the project in Gamemaker Studio 2
- Make your edits and save the game
- On the GitHub desktop app, commit the changes in the bottom left, adding a title and description of your changes
- Click Push Origin at the top right
- Go to GitHub online and create a pull request under the Pull requests tab.
- Wait for your request to be reviewed
Fame Games' Dust: Fire and Ice is coded using GameMaker Language or GML. Coding can be done in text format but syntax highlighting is featured on GameMaker Studio 2. When programming, it is important you follow these standards to increase readability and avoid errors.
The following prefixes should be used when creating any resource (e.g. spr_player or rm_main)
- Spites: spr_
- Paths: pth_
- Scripts: scr_
- Rooms: rm_
The following are all object types
- Objects: obj_
- Parents: par_
- Particles: prt_
When reasonable, an object should share the same name as its sprite, using the obj_ prefix instead of the spr_ prefix. This will help coordinate what belongs with what.
There are many types of variables and seperating which type is which will greatly increase readability. In general variable names should be short and concise. However, if a slightly longer name will increase readability, typing long variable names will not be the death of us. Follow these guidelines:
A local variable is a variable that is only used in the current event (create, step, etc.) and then discarded. These variables should follow the standards below but should also be preceded by the var keyword, e.g. var game_over. This keyword discards the variable at the end of the event, increasing game speed.
The typical variable you define, such as player_health or room_width should be written in all lower case, with words seperated by underscores. Numeric suffixes can be added directly to the end such as block3. Note: variable names can never start with number. Not 2_player but player2.
Variables that need to be accessed outside the event or by another object can be named according to the standards below. To access another object's variable, simply use obj_name.variable_name. Use of the keyword global should be minimized.
Constants are definitions that do not change, ever. For example pi or player_speed (If the player always travels at the same speed). Think, "Am I ever going to change this variable? Ever?" If the answer is no, it's a constant. Constants are written in all caps, e.g. PI or PLAYER_SPEED.
Counters are variables that get incremented or decremented in a for or while loop. If the variable is being used specifically for counting in that loop, i j and k should be used. For example:
var i
var j
for (i=0; i<10; i+=1){
for (j=0; j<10; j+=1){
//Code
}
}
Recall that for variables used only in one event, the var keyword should be used. This will always be the case with counters.
Instances are occurences of objects. If you make a obj_wall and place 10 in a room you have placed 10 instances of that object. Each instance is assigned a unique id. Sometimes you will want to refer to a specific instance, such as the last wall placed. When doing so, you assign that instances id to a variable.
Instance variables should begin with a capital letter, e.g. Last_wall if you're referring to the instance of the last wall placed.
The style guide is not standard, that is, it's not required, but following it will make your code easier to read, especially for reviewers. The guide may seem nitpicky but getting into the habit of good coding will make things easier for you and others.
Indentation is not required in GML but is highly recommended. Every level or loop of code that is nested should be indented one further than the level right above it. Here's an example:
switch (state){
case 0:
//Do first thing
case 1:
//Do second thing
}
Note how each block of nested code is indented one more level than the last. It's easy to see where all the cases are for the switch statement because they're all at the same indenation or column.
Also note the usage of new lines in the above example, one after each block of code. In general, add a newline after each loop or statement that doesn't contain the "final level" of code or the code that actually does something: action code, if you will. The exception is that there is no new line needed when the next line is indented further than the previous line. That is, a new line of code should not be both indented and have a blank line. There is no line after the switch statement because the case statement is already indented. There is no line after the case 0: or case 1: because they contain the action code. However! There is a line between the case statements because they are not action code. Here's another example:
//Other code, unrelated to the for loop
var i
for (i=0; i<10; i+=1){
//Do something each time
if i < 5{
//Do something else when i<5
}
}
Basically code is broken into blocks by newlines. The declartion var i is related to for loop so there is no blank line after it. The "Other code" however is unrelated and therefore there is a blank space after it. The action code is directly under the for loop and if statement. The if statement is not, itself, action code though, so it has a line seperating it from the action code above it since it is on the same indentation as it. Don't be stingy with space! Each statement deserves it's own line. Don't use x = 1; y= 1;, instead do:
x = 1
y = 1
GameMaker also allows for control statements to all be on one line if there is only one line of code to execute. For readability, it is preferred this is avoided unless a long list of control statements all execute one line of code each.
//Bad
if lives < 1 destroy_instance
//Good
if lives < 1{
destroy_instance()
}
//Also good
if condition_1 action_1
if condition_2 action_2
if condition_3 action_3
if condition_4 action_4
There's endless amounts of space and since GML is not a whitespace language, it just ignores all the unnecessary lines anyway. It's just for readability!
Space can convey meaning and make what is happening in the code much clearer if we use it consistently. I'll try to list as many examples as I can to show you when a space is appropriate and when it is not.
| Situation | Needs spaces? | Example(s) |
|---|---|---|
| Variable Assignment | Yes | speed = 5 |
| Operators | Yes |
speed += 5 height * room_height speed < 5
|
| Parameter Assignment | No | function(param=1) |
for loop assignments |
No |
for i=10 etc. |
for loop sections |
Yes |
for (i=10; i>0; i += 1) (After semicolons) |
| After commas | Yes | fuction(param1, param2, param3) |
| Around parentheses | No | grid = (room_height / 64) * 5 |
| After statements, before braces | No | if x == true{ |
Include immediately after the statement, regardless of its size. End them on a different line, aligned with the beginning of the statement they refer to. GML does this automatically when you type a close brace. Essentially don't go against GML's flow.
if statement{
if statement{
//Do stuff
}
}
Don't add an extra line between braces, just keep the all on different lines.
Use parentheses when necessary or when they improve readability, try not to overuse them. GML follows order of operations so 2 + 2 * 2 = 6 not 8, no parentheses required.
In regard to control statements, they are not required! However, do use them if the statement is lengthy or has multiple conditions.
Good:
if speed < 5{
speed += 1
}
Also good:
if (speed < 5 and energy > 5) or health < 10{
speed += 1
}
Use the keywords and, or, and not as opposed to the symbols &&, ||, and ! to improve readability. Parentheses may be needed with these if you're applying them to multiple terms.
if lives > 0 and health <= 0{
//Restart
}
No parenthesis needed
or:
if not (speed == 5){
speed = 5
}
Note: The parentheses here are needed or the not will only negate the speed and not the comparison speed == 5. No parenthesis are needed for negating boolean values, e.g. if not is_alive{ would work if is_alive is a boolean (true or false).
Comments are critical for code that will be reviewed, shared, and edited. As is our quest in coding, knowing when and how much to comment will improve readability and manageability. There are a few different types of comments.
These comments take place at the beginning of a function or script in GML's case. The Documentation should include the function's purpose, paramaters, and what it returns. For documentation the /// is used. A scripts documentation of Docstring might look like this:
/// @function convert_string_to_decimal(str_num)
/// @description Convert a string to a decimal
/// @param str_num | A string containing a decimal number
// Returns a decimal number
//Actual Code
The @function, @description, and @param are special documentation keywords that allow the user see information about the function while coding. For example, the @function keyword will autocomplete the function and show the parameter (str_num) when someone tries to use it. The @param line is set up as follows /// @param ARGUMENT_NAME | ARGUMENT_DESCRIPTION; ARGUMENT_RANGE(if applicable). 2 spaces should be used after these keywords for better readability.
Events also have descriptions at the top which is shown on the left side where the events for an object are listed. This is automatically added to each event and looks like /// @description Type description here. A brief summary of the actions completed in the event should be included here. This will allow programmers to find what is happening in extremely length events (such as step events) without scrolling through the whole thing. For example, if there is 100 lines of checks and then at the end of the step in 3 lines the object checks to see if the player has died, that might be tough to find. However, with a description at the top of the event like /// @description check this, check that, check another thing, check if the player has died then a programmer can do less searching when that snippet of code needs to be found two months later.
Finally in-line comments can help supplement code. In general, if the style guide and standards are followed, code should be fairly readable on its own. However, if further explanation is needed besided what is obvious, in-line comments are required.
In-line comments use the // at the beginning of the line and should always take place before the code they are referring to on a seperate line, (except in the case of a long list in which each line needs a comment, then they can go on the same line).
//Comment about code
//Code itself
In-line comments should say why a certain thing is being done instead of simply restating what is happening in the code. For example:
//Check to see if the speed is less than 5, then set it to 5
if speed < 5{
speed = 5
}
This is bad. The comment states the obvious. So what do you comment here? Nothing. Comments are for explaining the ambiguous, if they don't make something clear that wasn't clear before they shouldn't be included. Here's a good comment to reduce ambiguity:
//If the player is below the kill line, kill him
if obj_player.y < ((room_height / 64) + 8) * level{
instance_destroy(obj_player)
}
The random coordinates may not be obvious to someone looking at this code for the first time. The comment explains why the coordinate is used. Of course, even better might be to use a variable: kill_line = (room_height / 64 + 8) * level since the line if obj_player.y < kill_line would be self explanatory. Anyway, use in-line comments sparingly, always asking yourself if you could still explain this code in a month, or year. If not, add a comment.
A short inline comment should be included before a section of code (more than 1 or 2 lines) to identify the purpose of the code, even if the code is self-explanatory. This is to allow programmers to quicky break down what's going on in an event without reading all the code.
//Release object
with instance_destroy(other){
inventory -= 1
sprite_index -= 1
}
- Use
==as a comparison operatorif speed == 4. GameMaker doesn't require this but do it anyway.
You see a problem with the game. Or maybe something you think should be added? Time to create an issue.
This is done in the issues tab by hitting new issue. Several templates will appear to help you construct a well formatted issue. If you have a bug, fill out the bug template. If you don't have a specific idea but want to discuss something or brainstorm ideas, fill out the discussion thread template (don't forget to label this issue as discussion).
After you fill out the issue form, label it with the correct issues, add it to related projects, milestones, etc. Well categorized issues are important if someone is trying to find issues related to a specific problem.
We use Markdown for all our issue formatting. For a very succinct lesson in Markdown formatting visit this site.
Generally leave closing an issue to the person who opened it or the code reviewer who pulled the solution to the issue. If an issue has had no attention and seems it will not be dealt with, submit a comment mentioning the issue creator (@USERNAME using their username) asking if it can be closed.
If an issue is tagged bug and you follow the reproduce steps but can't reproduce the bugs do not close it. Instead label it with could not reproduce and wait for another user to confirm it.
If you're searching for an issue and can't find it, don't forget to search the closed issues as well.
See the labels page for a list of labels.
A pull request is how code is added from your branch to the main (or master branch). You request that the code be reviewed, and once it is approved, you can merge it into the master's branch. It is important you read and follow the steps listed below as they help protect the master branch from bugs and glitches, as well as give order to the whole process.
So how do you submit a pull request? Well, once you have completed an issue:
- Carefully test your code. It should compile without errors, perform the intended action, and not alter unrelated sections of the game
- Commit the changes to a branch named after you (if you don't have one go to
Branch>New Branchon the GitHub Desktop) - Submit a pull request on the pull request tab
- Type in relevant information including issue number into the prompt
- Go to your issue and label it with the green
pull request submittedlabel so others know you have completed that issue and are just waiting on a review - Don't close the issue. The reviewer will close it when he/she has ensured your code can be safely added to the
masterbranch. In the meantime others will want to know that your code has not yet been added
Your pull request will be reviewed and the reviewer will almost always find something that needs to be changed, whether it be a small bug or something larger. Once you receive change requests and make the changes, they will be automatically added to your pull request as you commit them to your branch.
Be sure to update the Changes Requested category if a change alters the content of your pull request. If you feel a reviewer has requested a change that is unnecessary or incorrect, politely point this out in a comment on the pull request. If the the reviewer still denies your change and you still believe it to be a good one, you can request an additional review. If your reviewer(s) do not approve of the change, don’t lose heart! Not every change suggested will get into the main game. Move on to another change.
Note: If you do have to remove part of a pull request, don't delete it! You can just make the change on your branch and make another commit to override the previous one.
Reviewing submitted pull requests is a necessary step to building a successful project. It's not always fun, but its necessary. If you want to review code, there are some requirements you should meet:
- You should be familiar with GML in general
- You should be aware of what makes code efficient or inefficient so you can make suggestions for submitted code
- You should be familiar with the project in general to prevent subtle bugs from occuring in other project sections due to code that has unintended consequences, such as an effect on networking.
Reviews can take 3 forms:
- Approve - Submit the review without requiring changes to the code
- Request Changes - Submit the review requiring changes to the code
- Comment - Make a suggestion without approving or rejecting the code
The comment is just a note for the reviewee, and may be submitted by anyone as it does not give or reject approval.
Reviews should be clear and concise to save time for both the reviewer and the reviewee. When writing a review you can type suggestions directly attached to lines of code by going to the files changed and clicking on the blue comment below the line. You can also suggest a correction here using ctrl-g that the reviewee can use to automatically change the code without submitting another commit. In-line comments should be used when possible for smaller errors, one-time mistakes, and typos. If a larger issue is present such as:
- Code that raises numerous errors
- Code that effects unrelated portions of the project
- Code that is generally inefficient
A general comment can be submitted at the end of the review. Reviews should always be kind and respectful. Suggest rather than demand changes and be open to different ways of doing things, even if they aren't how you pictured the solution to an issue.
Reviews must be thorough, even if just to educate the reviewee. Every pull request is a learning experience. Here's what to point out to reviewees (based on guidelines written by ryanmcdermott here)
- Styling issues - improper indentation, new lines, spaces, variable capitalization, etc. Just mention it and/or propose the proper style, don't dwell on it. If someone consistently has improper style, politely direct them to the Style Guide in the general review
- Ambiguous function names - If a function name is very confusing, mention it, suggest a better one if you can.
-
Long functions - If a function is very lengthy, make sure the function is only completing it's specified task. For example if the function is
scr_deal_damage, make sure the function only deals damage, and doesn't also check if the player is dead. That should use a different function, even if thescr_check_if_deadfunction is called duringscr_deal_damage - Lacking comments - If code is complex and you have to spend a lot of time interpreting it, it should be commented. Fuctions should all have proper documentation. If a strange approach is taken, a comment should explain that approach to avoid confusion later. Follow the style guide. Don't worry too much about unneeded comments unless they are excessive.
- Side effects - If code is going to cause obvious complications or is needlessly limiting, a suggestion should be made for more flexible code
- Error catching - Code should catch exceptions to prevent fatal errors. What if the modified object is destroyed? What if a different room is entered?
-
User input - Along the same lines, errors regarding unexpected user input should be handled. What if the user enters a
stringwhere you expeceted anint? What if the user inputs an unexpected control or escape key? - Inefficient Code - Correct code that puts unecessary strain on the processor or server. Consider networking in all codes application.
-
Document TODOs - If code includes
TODOcomments, ensure those comments are documented either in new issues or somewhere else, don't allow the code to be too cluttered withTODOcomments. -
Avoid submitting general game ideas or possible enhancements - Did the code submitted fulfill the issue it was connected to? Then your ideas for improvements belong in a new
enhancementissue.