GDScript style guide: add suggested code order (#3022)

* GDScript style guide: add suggested code order

Following the discussion in #2997

* Update the code order example in the GDScript style guide
This commit is contained in:
Nathan Lovato
2020-01-14 18:23:24 +01:00
committed by Hugo Locurcio
parent 22c16e0f94
commit 9a64d9a5c5

View File

@@ -3,21 +3,90 @@
GDScript style guide
====================
Description
-----------
This styleguide lists conventions to write elegant GDScript. The goal is
to encourage writing clean, readable code and promote consistency across
projects, discussions, and tutorials. Hopefully, this will also
encourage development of auto-formatting tools.
This style guide lists conventions to write elegant GDScript. The goal is to
encourage writing clean, readable code and promote consistency across projects,
discussions, and tutorials. Hopefully, this will also support the development of
auto-formatting tools.
Since GDScript is close to Python, this guide is inspired by Python's
`PEP 8 <https://www.python.org/dev/peps/pep-0008/>`__ programming
styleguide.
style guide.
Style guides aren't meant as hard rulebooks. At times, you may not be able to
apply some of the guidelines below. When that happens, use your best judgment,
and ask fellow developers for insights.
In general, keeping your code consistent in your projects and within your team is
more important than following this guide to a tee.
.. note:: Godot's built-in script editor uses a lot of these conventions
by default. Let it help you.
Here is a complete class example based on these guidelines:
::
class_name StateMachine
extends Node
# Hierarchical State machine for the player.
# Initializes states and delegates engine callbacks (_physics_process, _unhandled_input) to the state.
signal state_changed(previous, new)
export var initial_state = NodePath()
var is_active = true setget set_is_active
onready var _state = get_node(initial_state) setget set_state
onready var _state_name = _state.name
func _init():
add_to_group("state_machine")
func _ready():
connect("state_changed", self, "_on_state_changed")
_state.enter()
func _unhandled_input(event):
_state.unhandled_input(event)
func _physics_process(delta):
_state.physics_process(delta)
func transition_to(target_state_path, msg={}):
if not has_node(target_state_path):
return
var target_state = get_node(target_state_path)
assert target_state.is_composite == false
_state.exit()
self._state = target_state
_state.enter(msg)
Events.emit_signal("player_state_changed", _state.name)
func set_is_active(value):
is_active = value
set_physics_process(value)
set_process_unhandled_input(value)
set_block_signals(not value)
func set_state(value):
_state = value
_state_name = _state.name
func _on_state_changed(previous, new):
print("state changed")
emit_signal("state_changed")
Formatting
----------
@@ -254,12 +323,14 @@ characters in a given string. See the examples below:
# Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
print("'hello' \"world\"")
.. _naming_conventions:
Naming conventions
------------------
These naming conventions follow the Godot Engine style. Breaking these
will make your code clash with the built-in naming conventions, which is
ugly.
These naming conventions follow the Godot Engine style. Breaking these will make
your code clash with the built-in naming conventions, leading to inconsistent
code.
Classes and nodes
~~~~~~~~~~~~~~~~~
@@ -326,6 +397,162 @@ are constants:
FIRE,
}
Code order
----------
This first section focuses on code order. For formatting, see
:ref:`code_formatting`. For naming conventions, see :ref:`naming_conventions`.
We suggest to organize GDScript code this way:
::
01. tool
02. class_name
03. extends
04. # docstring
05. signals
06. enums
07. constants
08. exported variables
09. public variables
10. private variables
11. onready variables
12. optional built-in virtual _init method
13. built-in virtual _ready method
14. remaining built-in virtual methods
15. public methods
16. private methods
We optimized the order to make it easy to read the code from top to bottom, to
help developers reading the code for the first time understand how it works, and
to avoid errors linked to the order of variable declarations.
This code order follows four rules of thumb:
1. Properties and signals come first, followed by methods.
2. Public comes before private.
3. Virtual callbacks come before the class's interface.
4. The object's construction and initialization functions, ``_init`` and
``_ready``, come before functions that modify the object at runtime.
Class declaration
~~~~~~~~~~~~~~~~~
If the code is meant to run in the editor, place the ``tool`` keyword on the
first line of the script.
Follow with the `class_name` if necessary. You can turn a GDScript file into a
global type in your project using this feature. For more information, see
:ref:`doc_gdscript`.
Then, add the `extends` keyword if the class extends a built-in type.
Following that, you should have the class's optional docstring as comments. You
can use that to explain the role of your class to your teammates, how it works,
and how other developers should use it, for example.
::
class_name MyNode
extends Node
# A brief description of the class's role and functionality.
# Longer description.
Signals and properties
~~~~~~~~~~~~~~~~~~~~~~
Write signal declarations, followed by properties, that is to say, member
variables, after the docstring.
Enums should come after signals, as you can use them as export hints for other
properties.
Then, write constants, exported variables, public, private, and onready
variables, in that order.
::
enum Jobs { KNIGHT, WIZARD, ROGUE, HEALER, SHAMAN }
const MAX_LIVES = 3
export(Jobs) var job = Jobs.KNIGHT
export var max_health = 50
export var attack = 5
var health = max_health setget set_health
var _speed = 300.0
onready var sword = get_node("Sword")
onready var gun = get_node("Gun")
.. note::
The GDScript compiler evaluates onready variables right before the ``_ready``
callback. You can use that to cache node dependencies, that is to say, to get
child nodes in the scene that your class relies on. This is what the example
above shows.
Methods and static functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After the class's properties come the methods.
Start with the ``_init()`` callback method, that the engine will call upon
creating the object in memory. Follow with the ``_ready()`` callback, that Godot
calls when it adds a node to the scene tree.
These function should come first because they show how the object is
initialized.
Other built-in virtual callbacks, like ``_unhandled_input()`` and
``_physics_process``, should come next. These control the object's main loop and
interactions with the game engine.
The rest of the class's interface, public and private methods, come after that,
in that order.
::
func _init():
add_to_group("state_machine")
func _ready():
connect("state_changed", self, "_on_state_changed")
_state.enter()
func _unhandled_input(event):
_state.unhandled_input(event)
func transition_to(target_state_path, msg={}):
if not has_node(target_state_path):
return
var target_state = get_node(target_state_path)
assert target_state.is_composite == false
_state.exit()
self._state = target_state
_state.enter(msg)
Events.emit_signal("player_state_changed", _state.name)
func _on_state_changed(previous, new):
print("state changed")
emit_signal("state_changed")
Static typing
-------------