Memento GDScript & Godot Engine

Estimated reading time:9 minutes 42 seconds
programmation développement python godot engine GDscript

Memento GDScript & Godot Engine

Voici une liste des différents codes source et syntaxes courantes utilisés en GDScript dans Godot engine.
Ce fichier est un canevas qui s'étoffera au fur et à mesure de mes expérimentations sur ce langage .

Références/Sources:

  • Doc Godot: Gdscript
  • Certains exemples de code sont basés sur la version 3.0 de Godot et peuvent être obsolètes

Syntaxe de base

Les variables et les types simples

  • exemple

    # full syntax with new gdscript version (static typed from Godot 3.1)
    var variable_name : type = value
    # type is fixed but determine by the value with new typed gdscript
    var variable_name := value
    # type is dynamic (all gdscript versions)
    var variable_name = value
    # Examples
    var my_var # no value assigned, no type either
    my_var = 5 # assigning value, type is int but can be changed
    my_var : String = "Hello world" # my_var is a string and ONLY
    var empty = null
    var my_float := 5.0 # my_var is a float and ONLY
    var bool_true = true or false
    # constants (use same syntax as variables)
    const MY_CONST = 200
    const MAX_PLAYER_SPEED : int = 500
    const MAX_PLAYER_SPEED_MAX := 1000
    # string concatenation
    var full_name = "First" + " " + "Last"
    var full_nameV2 : String = "First2" + " " + "Last2"
    # Containers (list of values)
    var empty_list : Array = []
    var my_array = [1, "cat", false, 18.56, 'whatever']
    var my_dict = { "one": 1, "two": 2, "three": 3, "four": 4 }
    my_dict = {one = 1, two = 2, three = 3, four = 4 }
    my_dict.four = 4
    my_dict["five"] = 5

Les chaînes formatées

  • exemple

    # Define a format string with placeholder '%s'
    var format_string = "We're waiting for %s."
    # Using the '%' operator, the placeholder is replaced with the desired value
    var actual_string = format_string % "Godot"
    # output: "We're waiting for Godot."
    print(actual_string)
    
    # Multiple placeholders
    var format_string = "%s was reluctant to learn %s, but now he enjoys it."
    var actual_string = format_string % ["Estragon", "GDScript"]
    # output: "Estragon was reluctant to learn GDScript, but now he enjoys it."
    print(actual_string)
    
    # Define a format string
    var format_string = "We're waiting for {str}"
    # Using the 'format' method, replace the 'str' placeholder
    var actual_string = format_string.format({"str":"Godot"})
    # output: "We're waiting for Godot"
    print(actual_string)

Les conditions et les boucles

  • exemple

    if user_answer == "B" and my_var == 1:
    # leave the condition
    pass
    elif user_answer == "C" or my_int = 5:
    do_something()
    else:
    do_something_else()
    
    # tests if a value is in an array
    var my_array = [1, "cat", false, 18.56, 'whatever']
    if my_var in my_array:
    do_something()
    else:
    do_something_else()
    
    # test for an empty array
    if not my_array:
    # my_array is empty
    
    for quote in quotes:
    quote.capitalize()
    
    var y = [1,2,3,4]
    for x in y:
    print(x)
    
    for x in range(8):
    print(x)
    
    while x < 10:
    print(x)
    
    # skip the end of a block (on an if or in a loop)
    continue
    # BREAK the block and go to next line (on an if or in a loop)
    break

Les fonctions

  • exemple

    func get_random_item_in(my_list)->void:
    # TODO: get a random number
    item = my_list[0] # get a quote from a list
    print(item) # show the quote in the interpreter
    return "program is over" # returned value

Les Classes

  • Création d'une classe: un fichier GDscript est considéré comme une classe

  • Exemple d'héritages possibles:

    # Inherit/extend a globally available class.
    extends SomeClass
    ...
    # Inherit/extend a named class file.
    extends "somefile.gd"
    ...
    # Inherit/extend an inner class in another file.
    extends "somefile.gd".SomeInnerClass
  • Exemple de classe dérivée

    extend Sprite
    var max_health := 100
    func take_damage (amount:int)->void
      health -= amount
  • Vérifier si une instance donnée hérite d'une classe:

    # Cache the parent class.
    const Enemy = preload("enemy.gd")
    # Use 'is' to check inheritance.
    if entity is Enemy:
    entity.apply_damage()
  • Appeler une fonction dans une classe parente (c'est-à-dire une extension dans votre classe actuelle), ajoutez le préfixe. au nom de la fonction

    .a_function(args)
    
    func my_function(x):
    # Calls the same function on the parent class.
    .my_function(x)

Les objets

  • Enregistrer un script comme une nouvelle classe (permet de créer de nouveaux types)

    extends Node
    # Declare the class name here, add a comma and an optional path to an image to use as an icon
    class_name ScriptName, "res://path/to/optional/icon.svg"
    func _ready():
    var this = ScriptName           # reference to the script
    var cppNode = MyCppNode.new()   # new instance of a class named MyCppNode
    cppNode.queue_free()
  • Instantiation d'un objet

    var object := Object.new()
  • Forcer le type d'un objet (casting), utile pour accèder aux méthode propres à classe dérivée

    ($AnimationPlayer as AnimationPlayer).play("walk")

Les noeuds (Node)

  • Création d'un noeud

    var s:Sprite
    func _ready()->void:
    s = Sprite.new() # Create a new sprite!
    add_child(s) # Add it as a child of this node.
  • Suppression immédiate d'un noeud

    func _someaction()->void:
      # delete s (object previous defined)
      s.queue_free()
      # delete current node
      queue_free()
  • Supression plus sécure d'un noeud

    func _someaction()->void:
     # delete s (object previous defined)
     s.queue_free()
     # delete current node
     queue_free()

Les scenes

  • Instantiation d'une scene existante

    # loading the scene method 1: Will load when the script is instanced.
    # var scene = load("res://myscene.tscn")
    
    # loading the scene method 2 (préféred):Will load when parsing the script.
    var scene = preload("res://myscene.tscn")
    
    # create the scene instance as a "special node" (PackedScene)
    var node : PackedScene = scene.instance()
    
    # add it to the current scene root
    add_child(node)
  • Création d'un noeud et ajout d'un script attaché.
    Le code d’un script comme celui ci est beaucoup plus lent que le C++ du moteur.
    Il est préférable de créer la scéne dans l'éditeur, de la créer en tant que PackedScene et de l'instancier.

    extends Node
    func _init():
    var child = Node.new()
    child.name = "Child"
    child.script = preload("Child.gd")
    child.owner = self
    add_child(child)

Écran & Images

L'affichage

  • Connaître la taille de la fenêtre

    position = get_viewport_rect().size.y:
  • Changer la taille de la fenêtre

    # TODO
  • Passer en Plein écran ou mode fenêtré

    # TODO

Le GamePlay

Notions utiles

  • Récupérer une référence vers un noeud

    get_node("first/chid/child")
    # $ is a shortcut for get node
    $first/chid/child
  • Créer et émettre un signal

    # code in the signal emitter script (player)
    signal direction_changed
    func _physics_process(delta:float)->void:
    emit_signal('direction_changed', input_direction)
    #...
  • Recevoir et gérer un signal

    # code in the signal receiver script
    player_node = get_node('player')
    player_node.connect("direction_changed", self, '_on_Player_direction_changed')
    #...
    func _on_Player_direction_changed(direction)->void:
    #...
  • Exemple d'implémentation d'un évènement sur un bouton

    extends Panel
    func _on_button_pressed()->void:
    get_node("Label").text = "HELLO!"
    func _ready()->void:
    get_node("Button").connect("pressed", self, "_on_button_pressed")
  • Helpers pour les collisions

    # first method: delta is already applied and a slide movement is also applied on collision
    velocity = input_direction.normalized() * speed
    move_and_slide(velocity)
    # second method: just a basic collision
    var motion = input_direction.normalized() * speed * delta
    var collider = move_and_collide(motion)

Le Delta time

  • La fonction _physic_process est indépendante du delta time et est appelée à frame constante (60 FPS)
  • Le delta time est directement passé à la fonction _process

    extends Label
    var accu := 0
    func _process(delta:float)->void:
      accu += 1
    var text := str(accu)

Gestion des entrées utilisateur

Le clavier

  • Intercepter une touche du clavier

    input_direction := Vector2()
    input_direction.x = int(Input.is_action_pressed("move_right")) - int(Input.is_action_pressed("move_left"))
    input_direction.y = int(Input.is_action_pressed("move_down")) - int(Input.is_action_pressed("move_up"))
  • Déplacer un sprite au clavier

    extends Sprite
    export var SPEED := 100
    func _process(delta:float):
    var direction := Vector2(0, 0)
    if Input.is_key_pressed(KEY_UP):
        direction.y -= 1
    if Input.is_key_pressed(KEY_DOWN):
        direction.y += 1
    if Input.is_key_pressed(KEY_LEFT):
        direction.x -= 1
    if Input.is_key_pressed(KEY_RIGHT):
        direction.x += 1
    direction = direction.normalized() # To make sure diagonal movements will be in the same speed
    direction *= SPEED * delta         # Multiply by speed (pixels per second) and the time passed (seconds)
    translate(direction)               # Move the Sprite by the direction vector

Le joystick

  • Intercepter un bouton du joystick
func _input(event):
  if event is InputEventJoypadButton:
    prints("Button:", str(event.button_index))
  • Déplacer un sprite avec un joystick
extends Sprite
const SPEED: = 300
var device_index: = 0
func joy_connect(index: int, connect) -> void:
  # When a joystick is detected, keep the device index in a variable
  if connect:
    device_index = index
func _ready() -> void:
  Input.connect("joy_connection_changed", self, "joy_connect")
func _process(delta: float) -> void:
  var direction: = Vector2(0, 0)
  # Query Input singleton with the device index
  if Input.is_joy_button_pressed(device_index, 12): # UP
    direction.y -= 1
  if Input.is_joy_button_pressed(device_index, 13): # DOWN
    direction.y += 1
  if Input.is_joy_button_pressed(device_index, 14): # LEFT
    direction.x -= 1
  if Input.is_joy_button_pressed(device_index, 15): # RIGHT
    direction.x += 1
  direction = direction.normalized()
  direction *= SPEED*delta
  translate(direction)

La souris

  • Afficher/Masquer le curseur de la souris

    # TODO
  • Connaître l'état des boutons de la souris

    func _input(event):
    if event is InputEventMouseButton && event.pressed:
      prints("Button", event.button_index, "is pressed at", str(event.position))
    if event is InputEventMouseMotion:
      prints("Mouse moved to", str(event.position))
    func _process(delta):
    if Input.is_mouse_button_pressed(BUTTON_LEFT):
      prints("Holding left mouse button at", get_tree().get_root().get_mouse_position())
  • Connaître la position de la souris

    x = get_global_mouse_position().x
    y = get_global_mouse_position().y
  • Déplacer un sprite avec la souris

    func _input(event):
    if event is InputEventMouseMotion:
      position = event.position

Tactile (Touchscreen)

  • Connaître l'état du Touchscreen

    func _input(event):
    if event is InputEventScreenTouch && event.pressed:
      prints("Screen touch at", str(event.position))
    if event is InputEventScreenDrag:
      prints("Screen drag at", str(event.position))
  • Déplacer un sprite avec le Touchscreen

    func _input(event):
    if event is InputEventScreenTouch:
      position = event.position

Physique

les différent types d'objets physiques

  • StaticBody2D
    • non déplacé par le moteur physique
    • participe à la détection des collisions, mais ne se déplace pas en réponse à la collision.
    • utilisé pour des objets qui font partie de l'environnement ou qui n'ont pas besoin d'avoir un comportement dynamique, comme les murs ou le sol.
  • RigidBody2D
    • simule la physique
    • vous ne contrôlez pas directement un RigidBody2D, mais on lui appliquez des force (gravité, impulsions, etc.) et le moteur physique intégré de Godot calcule le mouvement résultant, y compris les collisions, les rebonds, la rotation...
    • Vous ne devez pas modifier la position ou la linéarité de RigidBody2D à chaque frame ou même très souvent. Si vous devez affecter directement l'état de l'objet, utilisez _integrate_forces, qui vous permet d'accéder directement à l'état physique (et donc à changer ses propriétés, telles que sa position)
  • KinematicBody2D
    • pas de physique
    • participe à la détection des collisions, mais ne se déplace pas en réponse à la collision qui doivent être implémentés dans le code
    • utilisé pour les joueurs ou les acteurs qui nécessitent une physique de style arcade plutôt qu'une simulation réaliste.

Gravité & Inertie

  • Déplacer un objet ayant un angle de rotation (en degré)
    utiliser un rigibody2d pour appliquer le moteur physique à un objet
    les propriétés du moteur physique sont paramètrables dans le propriétés du projet (par ex le coefficient de gravité) et dans celle du rigidbody2D (par ex la friction avec Linear>Damps et Angular>Damp)

    func _process(delta):
    thrust = Vector2()
    if state in [DEAD, INIT]:
      return
    if Input.is_action_pressed("thrust"):
      thrust = Vector2(engine_power, 0)
    rotation_dir = 0
    if Input.is_action_pressed("rotate_right"):
      rotation_dir += 1
    if Input.is_action_pressed("rotate_left"):
      rotation_dir -= 1
    
    func _physics_process(delta):
    set_applied_force(thrust.rotated(rotation))
    set_applied_torque(spin_power*rotation_dir)
  • la function _integrate_forces permet de lire et de modifier en toute sécurité l'état physique d'un RigidBody2D. Utilisez-la à la place de _physics_process si vous devez changer directement la position ou d'autres propriétés physiques.

L'audio

Musique

  • Charger et jouer une musique ou un son
    • ajouter un ou plusieurs noeuds AudioStreamPlayer (par exemple CoinSound) à la scene
    • affecter un fichier audio à chacun d'eux
    • jouer l'audio, par exemple $CoinSound.play()

Système

Fichiers

  • godot conserve toutes les ressources dans le dossier du projet. Elles sont accessibles par code via le chemin res:://. Ce dossier est en lecture seule

  • Les données en lecture/écriture sont accessibles par code via le chemin user:://. Son emplacement réel dépend de la plateforme, il peut être déterminé par la fonction OS.get_user_data_dir()

  • Lire un fichier texte

    var highscore = 0
    var score_file = "user://highscore.txt"
    var f = File.new()
    if f.file_exists(score_file):
      f.open(score_file, File.READ)
      var content = f.get_as_text()
      highscore = int(content)
      f.close()
  • Ecrire dans un fichier texte

    var highscore = 0
    var score_file = "user://highscore.txt"
    var f = File.new()
    f.open(score_file, File.WRITE)
    f.store_string(str(highscore))
    f.close()

Divers

Gestion de l'application

  • Quitter l'application

    # TODO

Next Post Previous Post