Outils pour utilisateurs

Outils du site


dindomoteur_creation_du_joueur

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
dindomoteur_creation_du_joueur [2022/03/20 19:13] Simon Deplatdindomoteur_creation_du_joueur [2022/03/27 19:24] (Version actuelle) Simon Deplat
Ligne 64: Ligne 64:
  
 ====Implémentation de la gravité==== ====Implémentation de la gravité====
 +
 +**[ NOTE ] : Comme discuté [[https://regakakobigman.netlify.app/godot/2019/10/09/how-to-use-delta.html|ici]], la gestion actuelle des accélérations est mal implémentée car elle n'est pas multipliée par delta.**
  
 Le joueur aura donc une masse et sera affecté par la gravité : Le joueur aura donc une masse et sera affecté par la gravité :
Ligne 70: Ligne 72:
 extends KinematicBody2D extends KinematicBody2D
  
-var _gravite = Global.gravite+var _gravite = 50
  
 var _masse = 1 var _masse = 1
  
 var _velocite = Vector2.ZERO var _velocite = Vector2.ZERO
 +
 +func _ready():
 + if get_tree().root.has_node("Global"):
 + _gravite = Global.gravite
  
 func _physics_process( _delta ): func _physics_process( _delta ):
Ligne 100: Ligne 106:
 extends KinematicBody2D extends KinematicBody2D
  
-var _gravite = Global.gravite+var _gravite = 50
  
 var _masse = 1 var _masse = 1
Ligne 106: Ligne 112:
  
 var _velocite = Vector2.ZERO var _velocite = Vector2.ZERO
 +
 +func _ready():
 + if get_tree().root.has_node("Global"):
 + _gravite = Global.gravite
  
 func _physics_process( _delta ): func _physics_process( _delta ):
Ligne 140: Ligne 150:
 extends KinematicBody2D extends KinematicBody2D
  
-var _gravite = Global.gravite+var _gravite = 50
  
 var _masse = 1 var _masse = 1
Ligne 149: Ligne 159:
  
 var _velocite = Vector2.ZERO var _velocite = Vector2.ZERO
 +
 +func _ready():
 + if get_tree().root.has_node("Global"):
 + _gravite = Global.gravite
  
 func _physics_process( _delta ): func _physics_process( _delta ):
Ligne 204: Ligne 218:
 extends KinematicBody2D extends KinematicBody2D
  
-var _gravite = Global.gravite+var _gravite = 50
  
 var _masse = 1 var _masse = 1
Ligne 216: Ligne 230:
  
 var _velocite = Vector2.ZERO var _velocite = Vector2.ZERO
 +
 +func _ready():
 + if get_tree().root.has_node("Global"):
 + _gravite = Global.gravite
  
 func _physics_process( _delta ): func _physics_process( _delta ):
Ligne 302: Ligne 320:
 extends KinematicBody2D extends KinematicBody2D
  
-var _gravite = Global.gravite+var _gravite = 50
  
 var _masse = 2 var _masse = 2
Ligne 318: Ligne 336:
 var _velocite = Vector2.ZERO var _velocite = Vector2.ZERO
 var _saute = false var _saute = false
 +
 +func _ready():
 + if get_tree().root.has_node("Global"):
 + _gravite = Global.gravite
  
 func _physics_process( _delta ): func _physics_process( _delta ):
Ligne 415: Ligne 437:
 C'est suffisant pour le prototypage. C'est suffisant pour le prototypage.
  
 +====Changement de direction du personnage====
 +
 +L'algorithme est tout bête, il suffit d'**inverser l'échelle du //Sprite// sur l'axe X lors de l'appel à la fonction //bouge()//** . Attention cependant, comme j'ai opté pour un déplacement spécifique lors de l'utilisation du clavier, à savoir de pouvoir reculer sans changer de sens en appuyant sur //Gauche// et //Droite// en même temps, le retournement ne se fait pas dans ce cas-ci.
 +
 +<code>
 +# Appui sur les boutons clavier de déplacement
 +func bouge():
 + if Input.is_action_just_pressed("gauche"):
 + _direction = -1
 + if not Input.is_action_pressed("droite"):
 + $Sprite.set_scale( Vector2( -1, 1 ) )
 + elif Input.is_action_just_pressed("droite"):
 + _direction = 1
 + if not Input.is_action_pressed("gauche"):
 + $Sprite.set_scale( Vector2( 1, 1 ) )
 + if Input.is_action_just_released("gauche"):
 + if not Input.is_action_pressed("droite"):
 + _direction = 0
 + else:
 + _direction = 1
 + $Sprite.set_scale( Vector2( 1, 1 ) )
 +
 + if Input.is_action_just_released("droite"):
 + if not Input.is_action_pressed("gauche"):
 + _direction = 0
 + else:
 + _direction = -1
 + $Sprite.set_scale( Vector2( -1, 1 ) )
 +</code>
 +
 +
 +====Animation du personnage====
 +
 +Il ne manque à ce stade plus que d'**animer le personnage**. J'ai pour ce faire créé quatre //sprites// très simple. Le résultat n'est pas fabuleux mais permet de comprendre comment faire. Voici les texture utilisées :
 +
 +{{ ::dm_quatrelutins.png?357 |}}
 +
 +J'ai commencé par **ajouter un nœud //AnimationPlayer//** à la scène, auquel j'ai rajouté **3 pistes, nommées //repos//, // saut//, et //marche//**. Pour chacune d'elles, j'ai simplement **modifié la texture** du //Sprite//. //repos// et //saut// ne consistent qu'en une seule clef, //marche// boucle sur 4 textures.
 +
 +J'ai activé **la //lecture automatique au chargement// de l'animation //repos//**, et activé **la //lecture en boucle// de l'animation //marche//**.
 +
 +**J'ai dû rajouter un nœud //RayCast2D// pour que l'animation du saut se fasse correctement.**
 +
 +Ma première idée était d'utiliser //is_on_floor()// pour détecter si le personnage était en train de sauter. Cependant, **lorsque le //KinematicBody2D// glisse le long d'une pente, il lui arrive de se décoller légèrement lorsque que la pente ascendante passe sur un plan horizontal**. Dans ce cas, //is_on_floor()// passe à //false// et déclenche l'animation, ce qui n'est pas joli à voir.
 +
 +À la place, j'ai mis un //RayCast2D// sous les pieds du //Joueur//, dont la propriété //Cast To// a été réglée à Vector2( 0, 20 ). J'ai coché le paramètre //enabled// pour l'activer, et changer le //masque de collision//.
 +
 +Cependant, cela pose le problème inverse lorsque que le personnage passe une plate-forme à sens unique en sautant. Alors qu'il est sensé être en saut, le //RayCast2D// détecte la plate-forme et lance l'animation. Il faut donc, pendant le saut, désactiver le //RayCast2D//.
 +
 +Le tout est ensuite appelé au sein d'une fonction dédiée :
 +
 +<code>
 +func _physics_process( _delta ):
 + [...]
 + animer()
 +
 +[...]
 +
 +# Appui sur le bouton de saut
 +func entreeSaut():
 + # Mécanisme de saut
 + if is_on_floor():
 + if not $RayCast2D.enabled:
 + $RayCast2D.set_enabled( true )
 + if Input.is_action_just_pressed("espace"):
 + _velocite.y -= _forceSaut
 + _saute = true
 + $RayCast2D.set_enabled( false )
 +
 +
 +[...]
 +# Animation du personnage :
 +func animer():
 + # Vérifier si le personnage est au sol :
 + if $RayCast2D.enabled:
 + if $RayCast2D.is_colliding():
 + # S'il bouge
 + if _velocite.x != 0:
 + $AnimationPlayer.play("marche")
 + # Sinon
 + else:
 + $AnimationPlayer.play("repos")
 + else:
 + $AnimationPlayer.play("saut")
 +</code>
 +
 +
 +====Contrôle du personnage à la manette====
 +
 +Avant de terminer, je souhaite simplement que l'on puisse également **contrôler le personnage à l'aide d'une manette**.
 +
 +Dans cette optique, je décide de changer la fonction //bouge()// affin qu'elle se comporte différemment en fonction d'un contrôle au clavier ou à la manette. Trois types de contrôles sont possibles, à la manette, au clavier, et au clavier et à la souris. Cependant, le déplacement du personnage étant restreint à la main gauche au clavier, il sera le même pour les deux derniers modes.
 +
 +Je rajoute donc **un paramètre d'entrée et de zone morte pour le **joystick**, et change l'algorithme de déplacement** :
 +
 +<code>
 +extends KinematicBody2D
 +
 +var _typeEntree = "clavier"
 +var _zoneMorte = 0.3
 +
 +var _gravite = 50
 +
 +var _masse = 2
 +var _forceSaut = 2000
 +var _vitesse = 400
 +var _acceleration = 20
 +var _deceleration = 10
 +var _ratioDInterruptionDuSaut = 0.5
 +var _ratioDGraviteSaut = 0.75
 +
 +var _direction = 0
 +var _vitesseActuelle = 0
 +var _forceSautActuelle = 0
 +
 +var _velocite = Vector2.ZERO
 +var _saute = false
 +
 +func _ready():
 + if get_tree().root.has_node("Global"):
 + _typeEntree = Global.typeEntree
 + _zoneMorte = Global.zoneMorte
 + _gravite = Global.gravite
 +
 +func _physics_process( _delta ):
 +
 + bouge()
 + entreeSaut()
 + if _saute:
 + if _velocite.y < 0:
 + interruptionDuSaut()
 + traitementDuMouvement()
 +
 + # Intégration du déplacement latéral
 + _velocite.x = _vitesseActuelle
 + # Intégration de la gravité
 + if _velocite.y < 0:
 + _velocite.y += _gravite * _masse * _ratioDGraviteSaut
 + else:
 + _velocite.y += _gravite * _masse
 +
 + _velocite = move_and_slide_with_snap(
 + _velocite,
 + Vector2.ZERO, # Accroche au sol
 + Vector2.UP, # Direction du sol ( Vector2.UP )
 + true, # Ne glisse pas sur le sol en cas d'inactivité ?
 + 4, # Nombre de collisions traitées par cycle
 + 0.9, # Angle maximum du sol ( en radians )
 + false # Inertie infinie ?
 + )
 +
 + animer()
 +
 +# Appui sur le bouton de saut
 +func entreeSaut():
 + # Mécanisme de saut
 + if is_on_floor():
 + if not $RayCast2D.enabled:
 + $RayCast2D.set_enabled( true )
 + if Input.is_action_just_pressed("espace"):
 + _velocite.y -= _forceSaut
 + _saute = true
 + $RayCast2D.set_enabled( false )
 +
 +# Gestion de l'interruption du saut
 +func interruptionDuSaut():
 + if Input.is_action_just_released("espace"):
 + _velocite.y *= _ratioDInterruptionDuSaut
 + _saute = false
 +
 +# Appui sur les boutons clavier de déplacement
 +func bouge():
 + if _typeEntree == "manette":
 + var entree = Input.get_axis("gauche", "droite")
 + if entree < -_zoneMorte:
 + _direction = -1
 + $Sprite.set_scale( Vector2( -1, 1 ) )
 + elif entree > _zoneMorte:
 + _direction = 1
 + $Sprite.set_scale( Vector2( 1, 1 ) )
 + else:
 + _direction = 0
 + else:
 + if Input.is_action_just_pressed("gauche"):
 + _direction = -1
 + if not Input.is_action_pressed("droite"):
 + $Sprite.set_scale( Vector2( -1, 1 ) )
 + elif Input.is_action_just_pressed("droite"):
 + _direction = 1
 + if not Input.is_action_pressed("gauche"):
 + $Sprite.set_scale( Vector2( 1, 1 ) )
 + if Input.is_action_just_released("gauche"):
 + if not Input.is_action_pressed("droite"):
 + _direction = 0
 + else:
 + _direction = 1
 + $Sprite.set_scale( Vector2( 1, 1 ) )
 +
 + if Input.is_action_just_released("droite"):
 + if not Input.is_action_pressed("gauche"):
 + _direction = 0
 + else:
 + _direction = -1
 + $Sprite.set_scale( Vector2( -1, 1 ) )
 +
 +# Gestion des accélérations/décélérations
 +func traitementDuMouvement():
 +# Vélocité horizontale
 + if _direction != 0:
 + _vitesseActuelle += _acceleration * _direction
 +
 + # Restriction aux vitesses max
 + if _vitesseActuelle < 0:
 + if _vitesseActuelle < -_vitesse:
 + _vitesseActuelle = -_vitesse
 + else:
 + if _vitesseActuelle > _vitesse:
 + _vitesseActuelle = _vitesse
 + else:
 + # Décélération au sol
 + if is_on_floor():
 + if _vitesseActuelle < 0:
 + if _vitesseActuelle < -_deceleration:
 + _vitesseActuelle += _deceleration
 + else:
 + _vitesseActuelle = 0
 + elif _vitesseActuelle > 0:
 + if _vitesseActuelle > _deceleration:
 + _vitesseActuelle -= _deceleration
 + else:
 + _vitesseActuelle = 0
 +
 +# Animation du personnage :
 +func animer():
 + # Vérifier si le personnage est au sol :
 + if $RayCast2D.enabled:
 + if $RayCast2D.is_colliding():
 + # S'il bouge
 + if _velocite.x != 0:
 + $AnimationPlayer.play("marche")
 + # Sinon
 + else:
 + $AnimationPlayer.play("repos")
 + else:
 + $AnimationPlayer.play("saut")
 +</code>
 +
 +
 +
 +====Notes de fin====
 +
 +Et voilà pour cette première étape de personnage. Je décide de m'arrêter ici car **nous avons un personnage jouable pour un jeu simple**. En fait nous avons quasiment le personnage de //Super Mario Bros 3// : celui-ci se déplace latéralement, saute, est affecté par l'inertie, et rentre en collision avec l'environnement.
 +
 +Il pourra servir de base pour des jeux n'ayant pas besoin de contrôles supplémentaires.
 +
 +Au cas où, voici à quoi ressemble ma scène dans l'éditeur :
 +
 +{{ ::dm_joueursimpleediteur.jpg?600 |}}
 +
 +Notez que je n'ai pas renommé les noeuds (sauf le parent) pour que vous les identifiiez plus facilement ici, mais qu'il est tout-de-même conseillé de les renommer lors de ses projets propres.
 +
 +Je créé par ailleurs **une copie autonome de ce //Joueur// dans le dossier //src/JoueurSimple/// **, en stockant l'ensemble des ressources à l'intérieur même du dossier afin de faciliter l'importation de la scène dans d'autres projets. La dépendance à //Global// n'étant pas forte, le seul paramétrage à faire est d'associer les bonnes entrées utilisateurs dans les //Paramètres du projet//.
 {{tag>godot dindomoteur jeu vidéo}} {{tag>godot dindomoteur jeu vidéo}}
dindomoteur_creation_du_joueur.1647803634.txt.gz · Dernière modification : 2022/03/20 19:13 de Simon Deplat