-
Notifications
You must be signed in to change notification settings - Fork 0
Coding Style Guidelines
Tony edited this page Jun 27, 2023
·
21 revisions
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.
- 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. |
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). |
| level- | Denotes intended layer of graphics and tilesets. |
| room- | Rooms. |
| ui- | User interfaces. |
Groups use TitleCase, with the word "Group" prepending each group.
Most nodes should not be renamed. The exceptions are listed below:
- Nodes are named with TitleCase.
- Filenames and node names should match. As a pair, they should be unique.
- ui-menu and UIMenu should be completely unique in the project.
- 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 TitleCase 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".
- 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".
- 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 Capital Case.
- Animations (in the AnimationPlayer and AnimatedSprite2D) are named with snake_case. Use _L or _R at the end for left/right facing sprites in 2D games.
- At the top of a class's script, the
toolkeyword, theextendskeyword, and theclass_namekeyword should go in this order and come first. - Next, a multi-line comment should be there to explain the intended purpose of the script.
- Usually contains a Description, Properties, Notes, and Debug Info (uses tab to clearly indicate part of a section).
- Properties, Notes, and Debug Info are all optional.
- Do not add Properties unless it's difficult to explain in one comment.
- Paragraphs will have a space in front of lines that are still associated with the first.
- Usually contains a Description, Properties, Notes, and Debug Info (uses tab to clearly indicate part of a section).
Here's an example:
"""
Description:
General behavior script for doors.
Properties:
Node _state_machine - A reference to the StateMachine node.
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.
-
onreadyvariables should go, next. They should be static typed and should use camelCase. -
constconstants 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. -
enumenumerations are constants. The name and the members of the enumerations use CONSTANT_CASE. -
@exported variables must come before all other public variables.- Example: `@export (type) var variable_name = value
-
varvariables are after that, static typed. They are named using camelCase. - Private variables are prepended with an underscore and use camelCase.
- Private variables will either be variables that are only used by the local class or involve the
setgetkeyword, which implies a side effect for if the variable is set or obtained. This means that the vast majority of your variables will be public. - Private functions will be named similarly, too (leading underscore). Note that leading underscores is how signal functions are also named. Any function that is used by an outside node is considered a public function.
- Private variables will either be variables that are only used by the local class or involve the
-
Exception: Variables that refer to nodes that are children of the owner the script is associated with are to be named with
nprefacing the node's name (andn_when the node is outside of the scene), orcif it's a Node that helps break up a long script into different parts (such ascSigfor c-signals.gd). These variables are named with camelCase and are always public.- Said node should have a unique name in
CapitalCaseas in the naming conventions. - An example of this would be
nA2DPlayer(child) orn_A2DPlayer(found elsewhere).
- Said node should have a unique name in
- Functions are placed below everything previously mentioned.
- Each function must be separated from the next using double-spacing (two empty lines).
- Functions intended to respond to signals are located at the top of the functions.
- Functions intended to specify setgets 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.
- 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 while doing any other cleanup.
- Before the function definition, if you have any comments or notes, use the multiline, even if it's a single line.
- 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:
( test ) - 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. The parameters should be detailed in a multiline comment above the function itself, not in the parameters.
- Have node variables be defined at the top of the function, even if they appear much later on in that function call.
- Exception: The node variable is only used in a branch in your code.
- 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 return statement and the non-related code.
- If the function is intended to return a value, the return statement should not be made in the middle of the function. 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
andandor. Use theandandorkeywords (and parentheses if necessary) to separate the conditions into multiple lines. 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 camelCase for its balance of compression and readability.
- 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.