-
Notifications
You must be signed in to change notification settings - Fork 0
Coding Style Guidelines
Program://Noir's coding style was developed at the same time as the infancy of the best practices of other developers. As a result, it's a little different from other Godot projects and addons.
By default, the entire project's primary language will be English. Any plans to expand retranslations to features should change the client presentation of text to the language of the player's choice without changing the English internals.
- A "component" will be the term for a scene that is intended to be instanced in another. The component itself can be comprised of an origin node, the child nodes, and even other components. The scenes that are not meant to be instanced in others will not be referred to as a component.
Folders use snake_case. The folder organization is as follows:
name | description |
---|---|
actors | Interactive entities for gameplay. Player, NPCs, doors, etc. |
addons | This is where plugins are developed. |
assets | Content files that are used by nodes in the game project. |
data | Information files that plugins may export or the game uses. |
info | Project management folder, meant for documentation. |
rooms | Scenes intended to serve as maps the player can traverse. |
ui | Scenes contaning GUI that the player interacts with. |
Folders within a folder using these naming conventions are encouraged to omit prepended or redundant parts of said names, so long as clarity is maintained.
Files, scenes, and scripts use hyphen-case with prepended terms.
prepend | description |
---|---|
actor- | Actors used in rooms. |
base- | Parent templates (scenes, classes, code); mainly for actors. |
global- | "Global" code (in scope). Autoloads are an example. |
level- | Denotes intended layer of graphics and tilesets. |
room- | Rooms. |
ui- | User interfaces. |
Files within a folder using these similar naming conventions are encouraged to omit prepended or redundant parts of said names, so long as clarity is maintained.
Groups use CapitalCase, with the word "Group" prepending each group.
Autoloads use CapitalCase and are prepended with the word "Global" due to its global scope.
- The .gd files of an autoload are prepended with global-.
- If the Autoload is also a UI, this is combined with the UI naming conventions listed later on.
Nodes are general not renamed. The exceptions (always in CapitalCase) are as follows:
- A container whose siblings are also referenced in code (usually in the form of a node path), so as to distinguish between them.
- The node has coded behavior. For example, a button with a signal that does something.
Filenames and node names should match. As a pair, we should try to make a unique naming scheme to distinguish it from other parts of the project.
- For example, ui-main-menu.tscn, ui-main-menu.gd, and UIMainMenu as a node name should each have one occurrence in the entire project. In other words, no "ui-main-menu.tscn/gd" in some other folder, no other nodes with the UIMainMenu name. Names are all as unique as possible.
- The exception to this would be Container nodes, as these can be super common.
- Another exception to this is if you're reusing a node and its functionality in another place. For example, copying the UI for changing settings from UISettings to UIFirstSetup.
- Actors should have either a CharacterBody2D/3D or a generic Node2D/3D node (if stationary) as the owner, prepended with "Actor".
- Area2D nodes are named with "A2D" prepending a CapitalCase name, ideally what the detection should be for. A door detecting a player would have A2DPlayer. Use a multi-line comment to detail what other things an area should detect.
- CollisionShape2D nodes work like Area2D but use CS2D instead (-3D uses same rules).
- Rooms should have a generic ControlNode as the owner, prepended with "Room".
- The exception to the prepend room is the main room, named Main. What can I say? I'm a C programmer.
- In 2D games, rooms should have two Node2Ds intended for grouping around the player actor, titled "Below Player" and "Above Player"
- Tilesets should use a unique, descriptive name.
- All rooms should have a Node node for grouping exit door actors ("Exits").
- UI scenes use a generic Control node as the owner, prepended with "UI".
- UI nodes found within these scenes do not use the UI prepend unless they are scenes.
- Rooms do not use the UI prepend.
- Layout nodes should be named preemptively when they are expected to be used in a script. Nodes must be named before referenced in code.
- Custom input names use Title Case. This is because GDTemplate uses that name in the keybinding. This may change if the project ends up being able to retranslate the keybinding UI.
- Animations (in the AnimationPlayer and AnimatedSprite2D) are named with hyphen-case, as they're considered like an asset. Use -L or -R at the end for left/right facing sprites in 2D games.
- At the top of a class's script, the
tool
keyword, theextends
keyword, and theclass_name
keyword should go in this order and come first. - Godot now supports native documentation tools for describing the class. You'll find that information here.
- Optionally, a multi-line comment should be there to explain the intended purpose of the script.
- Usually contains Notes and Debug Info (uses tab to clearly indicate part of a section).
- Paragraphs will have a space in front of lines that are still associated with the first.
Here's an example:
## A brief description of the class.
##
## A more detailed description of the class.
## More descriptions. These are merged by Godot into one line.
"""
Notes:
* owner.is_on_floor() only updates when calling move_and_slide().
Debug Info:
There is debug code for testing animations in the enter() function.
"""
- Try to make comments on their own line, positioned above the line or block of code being documented. This goes for multi-line comments, as well.
- Optional: It is common practice for a comment to look like
# this
, but I prefer to add a tab instead of a space so as to help make the comment stand out even more and hopefully improve its readability despite being dark text on a dark background. It's not really required.
- Declarations of signals (and the documentation commentary for it) should come next.
- Signals use snake_case.
-
onready
variables should go, next.- If an onready variable does not reference a node, use snake_case.
- If an onready variable references a node, use camelCase.
-
const
constants come after that. Do not use tab to align the values and signs and such. Constants should be static typed. Constants use the traditional CONSTANT_CASE.- Constants are not used to reference nodes.
-
enum
enumerations are constants. The name and the members of the enumerations use CONSTANT_CASE. -
@export
ed variables come next, and must come before all of the remaining variables.- Example: `@export var variable_name: type = default_value
- Preloaded PackedScene variables go here.
-
var
variables are after that, static typed. They are named using snake_case.- "Private variables" are not a feature of Godot and do not have style guidance.
- Any variable (not a constant) that references nodes...
- ... That are children of the node in a scene tree or being added as a child to that node must be prepended with an 'n'.
- ... That are or will be located in a parent, sibling, or elsewhere must be prepended with an 'n_'.
- Variables that refer to a preloaded PackedScene must be prepended with a 'p_'.
- The remainder of that variable must be the node's name. This is why unique names are encouraged in the project.
- Functions are placed below everything previously mentioned.
- Each function must be separated from the next using double-spacing (two empty lines).
- You can use a multi-line comment to visually show "groups" of functions to make navigation easier.
- Functions intended to respond to signals should be placed in a child node named Signals. If such a function must be in a different node, these functions are to be placed at the top of the functions, with the exceptions of _ready, _enter_tree, and _exit_tree.
- Functions intended to specify sets and gets are located immediately after.
- If a function references another one located within the script, then the order in which those two functions are arranged are:
- referred function
- referring function.
- Functions that call each other (recursion) should be avoided, but you can use whichever order makes the most sense.
- Because of this, the
_process()
and_process_physics()
functions would likely appear near the bottom of the script.
- A
destroy()
function should be placed at the bottom of any class that is meant to have a representation in the game as an interactive entity. This function allows us toqueue_free()
the entity after doing any other cleanup, such as signal disconnects.
- Functions use snake_case. Functions that define signal behavior are prepended with a '_', much like built-in signals in Godot.
- Functions should explicitly specify a return type, even when there is none (use void).
- A single space between each of the parentheses is required in function definitions and calls. Example:
( variable: type )
- All parameters need to be static typed. If there are too many variables to fit between 80 characters, then the definition should be split into multiple lines. If needed, the parameters should be detailed in a multiline comment above the function itself, not in the parameters, and should use one or two tabs for indentation.
- Have node variables be defined at the top of the function, even if they appear or are set much later on in that function call.
- Exception: The node variable is only used in a branch in your code.
- Exception 2: The variable is defined after defensive returns.
- Sometimes, we use defensive returns that cut the function short to respond to alternative circumstances than are typically expected. Please add either an empty line or a single-line comment to create a visual separation between the defensive return statements and the remaining code.
- If the function is intended to return a value, the return statement should not be made in the middle of the function unless it is being used defensively. It must be at the end and it should only be at the beginning if there is a necessary return statement for a particular circumstance.
- Always use parentheses, spaced. For example:
if( ( one + one == two ) or ( one * two == two ) ):
- When an if statement is long and needs multiple lines, first evaluate if you can simplify the if statement by rewriting the code structure. Always use
and
andor
. Use theand
andor
keywords (and parentheses if necessary) to separate the conditions into multiple lines. I traditionally use two tabs, but a single tab is fine, too, as long as the end parentheses is on its own line and matches the indentation of the if keyword. Below is an example:
if( condition1 - condition4 != 0.0
and etceteraEtcetera
and condition2
and ( condition5 or condition7 )
or ( condition3 + condition6 == 0 )
):
- General best practices:
- The use of self should be avoided in general.
- Try to arrange code and initialize it in the same places.
- Use static typing, it helps prevent avoidable bug-hunting situations.
- Use descriptive names for variables, using snake_case or camelCase as explained for their readability and identification.
- Use descriptive names for functions, using snake_case, for its high readability.
- Do not use comments to explain code on a machine level unless it's actually advanced enough to warrant it. Do use multi-line comments to help give general, natural-language context to a function. Explain what a function is for, not how it works. Use a clean and readable coding style to do the latter.
- In the same manner mentioned above, make a multi-line comment for the script as a whole to explain what it is intended for.
- It will sometimes be necessary, but if it can be avoided, then avoid null values for variables. When nodes access the properties of other nodes (and remember, Godot uses signals, so this can happen at any time), a value is expected. Avoiding null minimizes the work of keeping track of these things.