Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
Průvodce stylem GDScript¶
Tento průvodce stylem uvádí konvence pro psaní elegantního GDScriptu. Cílem je podpořit psaní čistého, čitelného kódu a podporovat konzistenci napříč projekty, diskusemi a kurzy. Doufejme, že to také podpoří vývoj nástrojů automatického formátování.
Protože GDScript je blízký jazyku Python, je tato příručka inspirována příručkou PEP 8 programovacího stylu jazyka Python.
Průvodci stylem nejsou zamýšleny jako striktní pravidla. Občas se může stát, že některé z níže uvedených pokynů nebudete moci použít. V takovém případě se řiďte vlastním úsudkem a požádejte kolegy vývojáře o radu.
Obecně platí, že udržování konzistentního kódu v projektech a v týmu je důležitější než dodržování tohoto návodu do puntíku.
Poznámka
Vestavěný editor skriptů Godot používá mnoho těchto konvencí ve výchozím nastavení. Nechte si s jím pomoci.
Zde je kompletní příklad třídy založený na těchto pokynech:
class_name StateMachine
extends Node
## Hierarchical State machine for the player.
##
## Initializes states and delegates engine callbacks ([method Node._physics_process],
## [method Node._unhandled_input]) to the state.
signal state_changed(previous, new)
@export var initial_state: Node
var is_active = true:
set = set_is_active
@onready var _state = initial_state:
set = set_state
@onready var _state_name = _state.name
func _init():
add_to_group("state_machine")
func _enter_tree():
print("this happens before the ready method!")
func _ready():
state_changed.connect(_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.player_state_changed.emit(_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")
state_changed.emit()
class State:
var foo = 0
func _init():
print("Hello!")
Formátování¶
Kódování a speciální znaky¶
Pro zalomení řádků používejte znaky pro posuv řádku (LF), nikoli CRLF nebo CR. (výchozí nastavení editoru)
Na konci každého souboru použijte jeden znak posuvu řádku. (výchozí nastavení editoru)
Použijte kódování UTF-8 bez značky pořadí bajtů <https://en.wikipedia.org/wiki/Byte_order_mark>`_. (výchozí nastavení editoru)
Pro odsazení použijte místo mezer Tabulátory. (výchozí nastavení editoru)
Odsazení¶
Každá úroveň odsazení by měla být o jednu větší než blok, který ji obsahuje.
Dobré:
for i in range(10):
print("hello")
Špatné:
for i in range(10):
print("hello")
for i in range(10):
print("hello")
K odlišení pokračovaní řádků od běžných bloků kódu použijte 2 úrovně odsazení.
Dobré:
effect.interpolate_property(sprite, "transform/scale",
sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
Tween.TRANS_QUAD, Tween.EASE_OUT)
Špatné:
effect.interpolate_property(sprite, "transform/scale",
sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
Tween.TRANS_QUAD, Tween.EASE_OUT)
Výjimkou tvoří pole, slovníky a výčty. Pro odlišení pokračovacích řádků použijte jednu úroveň odsazení:
Dobré:
var party = [
"Godot",
"Godette",
"Steve",
]
var character_dict = {
"Name": "Bob",
"Age": 27,
"Job": "Mechanic",
}
enum Tiles {
TILE_BRICK,
TILE_FLOOR,
TILE_SPIKE,
TILE_TELEPORT,
}
Špatné:
var party = [
"Godot",
"Godette",
"Steve",
]
var character_dict = {
"Name": "Bob",
"Age": 27,
"Job": "Mechanic",
}
enum Tiles {
TILE_BRICK,
TILE_FLOOR,
TILE_SPIKE,
TILE_TELEPORT,
}
Koncová čárka¶
V polích, slovnících a výčtech používejte na posledním řádku čárku. To vede ke snazšímu refaktoringu a lepšímu porovnávání ve správě verzí, protože při přidávání nových prvků není třeba upravovat poslední řádek.
Dobré:
enum Tiles {
TILE_BRICK,
TILE_FLOOR,
TILE_SPIKE,
TILE_TELEPORT,
}
Špatné:
enum Tiles {
TILE_BRICK,
TILE_FLOOR,
TILE_SPIKE,
TILE_TELEPORT
}
Koncové čárky jsou v jednořádkových seznamech zbytečné, proto je v tomto případě nepřidávejte.
Dobré:
enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}
Špatné:
enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT,}
Prázdné řádky¶
Definice funkcí a tříd obklopte dvěma prázdnými řádky:
func heal(amount):
health += amount
health = min(health, max_health)
health_changed.emit(health)
func take_damage(amount, effect=null):
health -= amount
health = max(0, health)
health_changed.emit(health)
Uvnitř funkcí použijte jeden prázdný řádek pro oddělení logických sekcí.
Poznámka
We use a single line between classes and function definitions in the class reference and in short code snippets in this documentation.
Délka řádku¶
Udržujte jednotlivé řádky kódu pod 100 znaků.
Pokud můžete, snažte se, aby řádky neměly více než 80 znaků. To usnadňuje čtení kódu na malých displejích a při otevření dvou skriptů vedle sebe v externím textovém editoru. Například při prohlížení rozdílové revize.
Jeden příkaz na řádek¶
Nikdy nekombinujte více příkazů na jednom řádku. Ne, programátoři v C, ani s jednořádkovým podmíněným příkazem.
Dobré:
if position.x > width:
position.x = 0
if flag:
print("flagged")
Špatné:
if position.x > width: position.x = 0
if flag: print("flagged")
Jedinou výjimkou z tohoto pravidla je ternární operátor:
next_state = "idle" if is_on_floor() else "fall"
Format multiline statements for readability¶
When you have particularly long if
statements or nested ternary expressions,
wrapping them over multiple lines improves readability. Since continuation lines
are still part of the same expression, 2 indent levels should be used instead of one.
GDScript allows wrapping statements using multiple lines using parentheses or backslashes. Parentheses are favored in this style guide since they make for easier refactoring. With backslashes, you have to ensure that the last line never contains a backslash at the end. With parentheses, you don't have to worry about the last line having a backslash at the end.
When wrapping a conditional expression over multiple lines, the and
/or
keywords should be placed at the beginning of the line continuation, not at the
end of the previous line.
Dobré:
var angle_degrees = 135
var quadrant = (
"northeast" if angle_degrees <= 90
else "southeast" if angle_degrees <= 180
else "southwest" if angle_degrees <= 270
else "northwest"
)
var position = Vector2(250, 350)
if (
position.x > 200 and position.x < 400
and position.y > 300 and position.y < 400
):
pass
Špatné:
var angle_degrees = 135
var quadrant = "northeast" if angle_degrees <= 90 else "southeast" if angle_degrees <= 180 else "southwest" if angle_degrees <= 270 else "northwest"
var position = Vector2(250, 350)
if position.x > 200 and position.x < 400 and position.y > 300 and position.y < 400:
pass
Vyhněte se zbytečným závorkám¶
Vyhněte se závorkám ve výrazech a podmíněných příkazech. Pokud to není nutné pro pořadí operací nebo obalení přes více řádků, pouze snižují čitelnost.
Dobré:
if is_colliding():
queue_free()
Špatné:
if (is_colliding()):
queue_free()
Logické operátory¶
Dávejte přednost jednoduchým anglickým verzím logických operátorů, protože jsou nejpřístupnější:
Místo
&&
použijteand
.Místo
||`
použijteor
.Use
not
instead of!
.
Můžete také použít závorky kolem logických operátorů, abyste odstranili případné nejasnosti. To může usnadnit čtení dlouhých výrazů.
Dobré:
if (foo and bar) or not baz:
print("condition is true")
Špatné:
if foo && bar || !baz:
print("condition is true")
Mezery¶
Kolem operátorů a za čárkami dělejte vždy jednu mezeru. Vyhněte se také dalším mezerám v odkazech na jednotlivé hodnoty slovníku a ve volání funkcí.
Dobré:
position.x = 5
position.y = target_position.y + 10
dict["key"] = 5
my_array = [4, 5, 6]
print("foo")
Špatné:
position.x=5
position.y = mpos.y+10
dict ["key"] = 5
myarray = [4,5,6]
print ("foo")
Nepoužívejte mezery k zarovnání výrazů na výšku:
x = 100
y = 100
velocity = 500
Uvozovky¶
Použijte dvojité uvozovky, pokud jednoduché uvozovky neumožňují v daném řetězci vynechání escapovaných znaků. Viz příklady níže:
# Normal string.
print("hello world")
# Use double quotes as usual to avoid escapes.
print("hello 'world'")
# Use single quotes as an exception to the rule to avoid escapes.
print('hello "world"')
# Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
print("'hello' \"world\"")
Čísla¶
U čísel s pohyblivou řádovou čárkou nevynechávejte počáteční ani koncovou nulu. V opačném případě se stanou hůře čitelnými a na první pohled se hůře odlišují od celých čísel.
Dobré:
var float_number = 0.234
var other_float_number = 13.0
Špatné:
var float_number = .234
var other_float_number = 13.
V hexadecimálních číslech používejte malá písmena, protože jejich menší výška usnadňuje čtení čísla.
Dobré:
var hex_number = 0xfb8c0b
Špatné:
var hex_number = 0xFB8C0B
Využijte podtržítka v literálech jazyka GDScript, aby byla velká čísla čitelnější.
Dobré:
var large_number = 1_234_567_890
var large_hex_number = 0xffff_f8f8_0000
var large_bin_number = 0b1101_0010_1010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12345
Špatné:
var large_number = 1234567890
var large_hex_number = 0xfffff8f80000
var large_bin_number = 0b110100101010
# Numbers lower than 1000000 generally don't need separators.
var small_number = 12_345
Konvence pojmenování¶
Tyto konvence pojmenování odpovídají stylu Godot Engine. Jejich porušení způsobí, že váš kód bude v rozporu se zabudovanými konvencemi pro pojmenování, což povede k nekonzistentnímu kódu.
Názvy souborů¶
Pro názvy souborů použijte snake_case. U pojmenovaných tříd převeďte název třídy PascalCase na snake_case:
# This file should be saved as `weapon.gd`.
class_name Weapon
extends Node
# This file should be saved as `yaml_parser.gd`.
class_name YAMLParser
extends Object
To je v souladu s tím, jak jsou pojmenovány soubory C++ ve zdrojovém kódu Godotu. Tím se také vyhnete problémům velkých a malých písmen, které mohou nastat při exportu projektu ze systému Windows na jiné platformy.
Třídy a uzly¶
Pro názvy tříd a uzlů použijte PascalCase:
extends CharacterBody3D
Při načítání třídy do konstanty nebo proměnné použijte také PascalCase:
const Weapon = preload("res://weapon.gd")
Funkce a proměnné¶
Pro pojmenování funkcí a proměnných použijte snake_case:
var particle_effect
func load_level():
K virtuálním metodám, které musí uživatel implementovat, soukromým funkcím a soukromým proměnným přidejte jedno podtržítko (_):
var _counter = 0
func _recalculate_path():
Signály¶
Pro pojmenování signálů použijte minulý čas:
signal door_opened
signal score_changed
Konstanty a výčtové typy¶
Zapisujte konstanty pomocí CONSTANT_CASE, tj. všemi velkými písmeny s podtržítkem (_) pro oddělení slov:
const MAX_SPEED = 200
Pro názvy enumů použijte PascalCase a pro jejich členy CONSTANT_CASE, protože se jedná o konstanty:
enum Element {
EARTH,
WATER,
AIR,
FIRE,
}
Pořadí kódu¶
Tato první část se zaměřuje na pořadí kódu. O formátování viz Formátování. Konvence pojmenování viz Konvence pojmenování.
Doporučujeme uspořádat kód GDScript tímto způsobem:
01. @tool
02. class_name
03. extends
04. # docstring
05. signals
06. enums
07. constants
08. @export variables
09. public variables
10. private variables
11. @onready variables
12. optional built-in virtual _init method
13. optional built-in virtual _enter_tree() method
14. built-in virtual _ready method
15. remaining built-in virtual methods
16. public methods
17. private methods
18. subclasses
Pořadí jsme optimalizovali tak, aby bylo snadné číst kód shora dolů, aby vývojáři, kteří čtou kód poprvé, pochopili, jak funguje, a aby se předešlo chybám souvisejícím s pořadím deklarace proměnných.
Toto pořadí kódů se řídí čtyřmi základními pravidly:
Nejdříve jsou na řadě vlastnosti a signály a poté metody.
Veřejné přijde před soukromé.
Virtuální zpětná volání jsou před rozhraním třídy.
Funkce pro konstrukci a inicializaci objektu,
_init
a_ready
, předcházejí funkce, které modifikují objekt za běhu.
Deklarace třídy¶
If the code is meant to run in the editor, place the @tool
annotation 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
GDScript reference.
Then, add the extends
keyword if the class extends a built-in type.
Following that, you should have the class's optional documentation 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.
##
## The description of the script, what it can do,
## and any further detail.
Signály a vlastnosti¶
Deklarace signálů, za kterými následují vlastnosti, tedy členské proměnné, zapisujte až za docstring.
Enumy by měly být uvedeny až za signály, protože je můžete použít jako nápovědu pro export ostatních vlastností.
Poté napište konstanty, exportované proměnné, veřejné, soukromé proměnné a proměnné onready, a to v tomto pořadí.
signal player_spawned(position)
enum Jobs {KNIGHT, WIZARD, ROGUE, HEALER, SHAMAN}
const MAX_LIVES = 3
@export var job: Jobs = Jobs.KNIGHT
@export var max_health = 50
@export var attack = 5
var health = max_health:
set(new_health):
health = new_health
var _speed = 300.0
@onready var sword = get_node("Sword")
@onready var gun = get_node("Gun")
Poznámka
Překladač GDScript vyhodnocuje proměnné onready těsně před zpětným voláním _ready
. Toho můžete využít k ukládání závislostí uzlů do mezipaměti, tj. k získání podřízených uzlů scény, na které vaše třída spoléhá. Právě to ukazuje výše uvedený příklad.
Členské proměnné¶
Nedeklarujte členské proměnné, pokud se používají pouze lokálně v metodě, protože to znesnadňuje orientaci v kódu. Místo toho je deklarujte jako lokální proměnné v těle metody.
Lokální proměnné¶
Lokální proměnné deklarujte co nejblíže jejich prvnímu použití. To usnadňuje sledování kódu, aniž byste museli příliš rolovat, abyste našli místo, kde byla proměnná deklarována.
Metody a statické funkce¶
Po vlastnostech třídy následují metody.
Začněte zpětnou metodou _init()
, kterou engine zavolá při vytvoření objektu v paměti. Následuje zpětné volání _ready()
, které Godot zavolá při přidání uzlu do stromu scény.
Tyto funkce by měly být na prvním místě, protože ukazují, jak se objekt inicializuje.
Další vestavěná virtuální zpětná volání, jako _unhandled_input()
a _physics_process
, by měly následovat. Ty řídí hlavní smyčku objektu a interakci s herním enginem.
Zbytek rozhraní třídy, veřejné a soukromé metody, následují v tomto pořadí.
func _init():
add_to_group("state_machine")
func _ready():
state_changed.connect(_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.player_state_changed.emit(_state.name)
func _on_state_changed(previous, new):
print("state changed")
state_changed.emit()
Statické typování¶
Od verze Godot 3.1 podporuje GDScript volitelné statické typování.
Deklarované typy¶
Chcete-li deklarovat typ proměnné, použijte <proměnná>: <typ>
:
var health: int = 0
Chcete-li deklarovat návratový typ funkce, použijte -> <type>
:
func heal(amount: int) -> void:
Odvozené typy¶
In most cases you can let the compiler infer the type, using :=
.
Prefer :=
when the type is written on the same line as the assignment,
otherwise prefer writing the type explicitly.
Dobré:
var health: int = 0 # The type can be int or float, and thus should be stated explicitly.
var direction := Vector3(1, 2, 3) # The type is clearly inferred as Vector3.
Include the type hint when the type is ambiguous, and omit the type hint when it's redundant.
Špatné:
var health := 0 # Typed as int, but it could be that float was intended.
var direction: Vector3 = Vector3(1, 2, 3) # The type hint has redundant information.
# What type is this? It's not immediately clear to the reader, so it's bad.
var value := complex_function()
In some cases, the type must be stated explicitly, otherwise the behavior
will not be as expected because the compiler will only be able to use
the function's return type. For example, get_node()
cannot infer a type
unless the scene or file of the node is loaded in memory. In this case, you
should set the type explicitly.
Dobré:
@onready var health_bar: ProgressBar = get_node("UI/LifeBar")
Alternatively, you can use the as
keyword to cast the return type, and
that type will be used to infer the type of the var.
@onready var health_bar := get_node("UI/LifeBar") as ProgressBar
# health_bar will be typed as ProgressBar
This option is also considered more type-safe than the first.
Špatné:
# The compiler can't infer the exact type and will use Node
# instead of ProgressBar.
@onready var health_bar := get_node("UI/LifeBar")
Mezery mezi komentáři¶
Regular comments (
#
) and documentation comments (##
) should start with a space, but not code that you comment out. Additionally, code region comments (#region
/#endregion
) must follow that precise syntax, so they should not start with a space.Using a space for regular and documentation comments helps differentiate text comments from disabled code.
Dobré:
Špatné:
Poznámka
In the script editor, to toggle the selected code commented, press Ctrl + K. This feature adds a single
#
sign at the start of the selected lines.