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žijte and.

  • Místo ||` použijte or.

  • 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 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é:

# This is a comment.
#print("This is disabled code")

Špatné:

#This is a comment.
# print("This is disabled code")

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.

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:

  1. Nejdříve jsou na řadě vlastnosti a signály a poté metody.

  2. Veřejné přijde před soukromé.

  3. Virtuální zpětná volání jsou před rozhraním třídy.

  4. 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")