Ci-dessous, les différences entre deux révisions de la page.
| Prochaine révision | Révision précédente | ||
| ludique:godot:tutoriaux:pong [2017/11/26 09:00] – créée yoann | ludique:godot:tutoriaux:pong [2021/02/01 21:51] (Version actuelle) – modification externe 127.0.0.1 | ||
|---|---|---|---|
| Ligne 5: | Ligne 5: | ||
| Traduction du tutoriel. On se propose ici de coder le mythique Pong avec Godot. | Traduction du tutoriel. On se propose ici de coder le mythique Pong avec Godot. | ||
| - | Pour commencer exécuter Godot Engine et démarrer un nouveau projet. Afin de se concentrer sur l' | + | Pour commencer exécuter Godot Engine et démarrer un nouveau projet. Afin de se concentrer sur l' |
| + | En souvenir du bon vieux temps, la résolution du jeu sera fixée en 640*400. On peut définir la résolution du projet dans le menu: | ||
| + | * Scene -> Paramètres du Projet | ||
| + | |||
| + | Dans la section **Display** cocher les options et définir les valeurs pour: | ||
| + | * **width** = 640 | ||
| + | * **Height** = 400 | ||
| + | |||
| + | Dans la section **Render** activer **Default Clear Color** et définir sa valeur au noir. | ||
| + | |||
| + | Fermer la fenêtre Paramètre du projet. | ||
| + | |||
| + | Créer un Node2D comme racine du projet. Le type Node2D est le type de base pour les projets 2D. Renommer le Node game. | ||
| + | |||
| + | Insérer les nodes pour les sprites (nodes Sprite) nécessaires pour les deux raquettes, la balle et le séparateur. | ||
| + | |||
| + | < | ||
| + | * < | ||
| + | * < | ||
| + | </ | ||
| + | |||
| + | Renommer les sprites | ||
| + | ^ sprite | ||
| + | | raquette gauche | left | | ||
| + | | raquette droite | right | | ||
| + | | fillet | ||
| + | | ball | ball | | ||
| + | |||
| + | Via l' | ||
| + | |||
| + | ^ sprite | ||
| + | | left | (67, 183) | | ||
| + | | right | (577, 187) | | ||
| + | | separator | (320, 200) | | ||
| + | | ball | (320, 188) | | ||
| + | |||
| + | Lancer le projet pour faire une première sauvegarde de la scene, nommer la scene pong.tscn | ||
| + | |||
| + | ===== Configurer les entrées ===== | ||
| + | |||
| + | Dasn un jeu vidéo, les interactions du joueurs peuvent provenir d'un grand nombre de périphériques: | ||
| + | |||
| + | Dans le cas de pong les seules actions nécessaires permettront à la raquette de monter ou descendre. | ||
| + | |||
| + | Pour définir les actions, ouvrir les propriétés | ||
| + | * Menu **Scene** -> **Paramètres du projet** | ||
| + | * Sélectionner l' | ||
| + | |||
| + | Certaines actions existent par défaut pour le projet, nous allons en créer de nouvelles: | ||
| + | |||
| + | ^ Actions à créer ^ Touche assignée | | ||
| + | | left_move_up | ||
| + | | left_move_down | ||
| + | | right_move_up | ||
| + | | right_move_down | < | ||
| + | |||
| + | Fermer la fenêtre de Paramètres du projet | ||
| + | |||
| + | ===== Le script ===== | ||
| + | |||
| + | Créer un script sur la racine du projet (le node game). | ||
| + | En premier lieu, il nous faut définir quelques attributs qui nous seront utiles pour les traitements: | ||
| + | * screen_size: | ||
| + | * pad_size: taille des raquettes | ||
| + | * direction: position de la balle | ||
| + | |||
| + | La fonction _ready() est appelée lorsque tous les nodes ont été chargés. Ici nous allons l' | ||
| + | |||
| + | <code python> | ||
| + | extends Node2D | ||
| + | |||
| + | # attributs du script courant | ||
| + | # variables de travail | ||
| + | var screen_size | ||
| + | var pad_size | ||
| + | var direction = Vector2(1.0, | ||
| + | |||
| + | |||
| + | func _ready(): | ||
| + | # initialisation des attributs | ||
| + | self.pad_size = get_node(" | ||
| + | self.screen_size = get_viewport_rect().size | ||
| + | |||
| + | # Activer les traitements pour chaque frame sur temps libre | ||
| + | set_process(true) | ||
| + | </ | ||
| + | |||
| + | ==== Mouvement de la balle et collisions ==== | ||
| + | |||
| + | Il nous faut à présent ajouter d' | ||
| + | |||
| + | Maintenant nous allons redéfinir la fonction **_process()** l' | ||
| + | Les ajustement | ||
| + | |||
| + | <code python> | ||
| + | func _process(delta): | ||
| + | # Récupère la position courante de la balle | ||
| + | var ball_position = get_node(" | ||
| + | |||
| + | # Construit une aire occupée par la raquette gauche | ||
| + | # pour la détection de collision | ||
| + | var left_rect = Rect2(get_node(" | ||
| + | var right_rect = Rect2(get_node(" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | La création des objets de type Rect2 utilisés pour la détection de collision avec la balle se base sur la texture du sprite qui par défaut est centrée d'où l' | ||
| + | </ | ||
| + | |||
| + | A présent attaquons nous au mouvement de la balle. Comme la position est stockée dans la variable **ball_position** sa nouvelle position est donnée par le calcul: | ||
| + | |||
| + | <code python> | ||
| + | # Calcul de la nouvelle position | ||
| + | ball_position += self.direction * self.ball_speed * delta | ||
| + | # Mise a jour de la position de la balle | ||
| + | get_node(" | ||
| + | </ | ||
| + | |||
| + | Si on lance l’exécution de la scène à présent, la balle se déplace bien mais traverse la raquette droite, il va nous falloir gérer les collisions. | ||
| + | |||
| + | Avant de mettre à jour la position de la balle nous allons donc tester si elle n' | ||
| + | |||
| + | <code python> | ||
| + | ... | ||
| + | |||
| + | # La balle change de direction lorsqu' | ||
| + | if ((ball_position.y < 0 and self.direction.y < 0) or (ball_position.y > self.screen_size.y and self.direction.y > 0)): | ||
| + | self.direction.y = -self.direction.y | ||
| + | |||
| + | # Mise a jour de la position de la balle | ||
| + | get_node(" | ||
| + | </ | ||
| + | |||
| + | A présent les raquettes, si une des raquettes est touchée, la direction sur **X** est inversée et celle sur **Y** prend une valeur aléatoire. Par la même occasion, la vitesse est légèrement augmentée. | ||
| + | |||
| + | Pour en finir avec la balle et les collisions, on teste le cas du point gagné. Pour faire simple, si la balle sort, elle est replacée au centre | ||
| + | |||
| + | <code python> | ||
| + | ... | ||
| + | |||
| + | # Si la balle sort, elle est replacée au centre avec vitesse et position initiale | ||
| + | if (ball_position.x < 0 or ball_position.x > self.screen_size.x): | ||
| + | ball_position = self.screen_size*0.5 | ||
| + | self.ball_speed = self.INITIAL_BALL_SPEED | ||
| + | self.direction = Vector2(1.0, | ||
| + | |||
| + | # Mise a jour de la position de la balle | ||
| + | get_node(" | ||
| + | </ | ||
| + | |||
| + | ==== Mouvement des raquettes ==== | ||
| + | |||
| + | Il faut modifier la position des raquettes en fonction des entrées faites par les utilisateurs. Pour ce la nous allons utiliser les actions d' | ||
| + | |||
| + | <code python> | ||
| + | # Bouger la raquette gauche | ||
| + | var left_pos = get_node(" | ||
| + | |||
| + | if (left_pos.y > 0 and Input.is_action_pressed(" | ||
| + | left_pos.y += -self.PAD_SPEED * delta | ||
| + | if (left_pos.y < screen_size.y and Input.is_action_pressed(" | ||
| + | left_pos.y += self.PAD_SPEED * delta | ||
| + | |||
| + | get_node(" | ||
| + | |||
| + | # Bouger la raquette droite | ||
| + | var right_pos = get_node(" | ||
| + | |||
| + | if (right_pos.y > 0 and Input.is_action_pressed(" | ||
| + | right_pos.y += -self.PAD_SPEED * delta | ||
| + | if (right_pos.y < screen_size.y and Input.is_action_pressed(" | ||
| + | right_pos.y += self.PAD_SPEED * delta | ||
| + | |||
| + | get_node(" | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== script complet ==== | ||
| + | |||
| + | Le script complet a associer au node racine | ||
| + | |||
| + | <code python pong.gd> | ||
| + | extends Node2D | ||
| + | |||
| + | # attributs du script courant | ||
| + | # variables de travail | ||
| + | var screen_size | ||
| + | var pad_size | ||
| + | var direction = Vector2(1.0, | ||
| + | |||
| + | # Vitesse initiale de la balle(en pixels/ | ||
| + | const INITIAL_BALL_SPEED = 80 | ||
| + | |||
| + | # Vitesse de la balle (en pixels/ | ||
| + | var ball_speed = INITIAL_BALL_SPEED | ||
| + | |||
| + | # Vitesse des raquettes | ||
| + | const PAD_SPEED = 150 | ||
| + | |||
| + | func _ready(): | ||
| + | # initialisation des attributs | ||
| + | self.pad_size = get_node(" | ||
| + | self.screen_size = get_viewport_rect().size | ||
| + | |||
| + | # Activer les traitements pour chaque frame sur temps libre | ||
| + | set_process(true) | ||
| + | |||
| + | func _process(delta): | ||
| + | # position courante de la balle | ||
| + | var ball_position = get_node(" | ||
| + | |||
| + | # Construit une aire occupée par la raquette gauche | ||
| + | # pour la detection de collision | ||
| + | var left_rect = Rect2(get_node(" | ||
| + | var right_rect = Rect2(get_node(" | ||
| + | |||
| + | # Calcul de la nouvelle position | ||
| + | ball_position += self.direction * self.ball_speed * delta | ||
| + | |||
| + | # La balle change de direction lorsqu' | ||
| + | if ((ball_position.y < 0 and self.direction.y < 0) or (ball_position.y > self.screen_size.y and self.direction.y > 0)): | ||
| + | self.direction.y = -self.direction.y | ||
| + | |||
| + | # Change la direction et augmente la vitesse lorsque la balle touche les raquettes | ||
| + | if ((left_rect.has_point(ball_position) and self.direction.x < 0) or (right_rect.has_point(ball_position) and self.direction.x > 0)): | ||
| + | self.direction.x = -self.direction.x | ||
| + | self.direction.y = randf()*2.0 - 1 | ||
| + | self.direction = self.direction.normalized() | ||
| + | self.ball_speed *= 1.1 | ||
| + | |||
| + | # Si la balle sort, elle est replacée au centre avec vitesse et position initiale | ||
| + | if (ball_position.x < 0 or ball_position.x > self.screen_size.x): | ||
| + | ball_position = self.screen_size*0.5 | ||
| + | self.ball_speed = self.INITIAL_BALL_SPEED | ||
| + | self.direction = Vector2(1.0, | ||
| + | |||
| + | # Mise a jour de la position de la balle | ||
| + | get_node(" | ||
| + | |||
| + | # Bouger la raquette gauche | ||
| + | var left_pos = get_node(" | ||
| + | |||
| + | if (left_pos.y > 0 and Input.is_action_pressed(" | ||
| + | left_pos.y += -self.PAD_SPEED * delta | ||
| + | if (left_pos.y < screen_size.y and Input.is_action_pressed(" | ||
| + | left_pos.y += self.PAD_SPEED * delta | ||
| + | |||
| + | get_node(" | ||
| + | |||
| + | # Bouger la raquette droite | ||
| + | var right_pos = get_node(" | ||
| + | |||
| + | if (right_pos.y > 0 and Input.is_action_pressed(" | ||
| + | right_pos.y += -self.PAD_SPEED * delta | ||
| + | if (right_pos.y < screen_size.y and Input.is_action_pressed(" | ||
| + | right_pos.y += self.PAD_SPEED * delta | ||
| + | |||
| + | get_node(" | ||
| + | </ | ||