Outils pour utilisateurs

Outils du site


dindomoteur_creation_du_joueur

DindoMoteur : Création du Joueur

Contexte

Cette page présente les différentes étapes de la création d'un nœud Joueur pour le DindoMoteur.

Création visuelle d'un joueur et d'une plate-forme

La première étape est d'avoir un simple joueur contrôlable ainsi qu'une plate-forme sur lequel celui-ci puisse se mouvoir.

Normalement, nous pourrions supposer que cette plate-forme est l'objet d'un autre article du wiki, mais il ne fait pas sens d'avoir un joueur sans environnement pour l'intégrer, nous mettons donc en place un environnement rudimentaire pour tester celui-ci.

Dans un premier temps, je créé deux répertoire dans src/ : Joueur/ et Niveau/ .

Je remarque que dans l'exemple Platformer, il y a un répertoire Platform/ dédié aux plate-formes. Nous verrons en temps voulu si c'est pertinent, commençons simplement pour l'instant.

Avant toute logique de déplacement et de collision, je souhaite simplement que ces deux objets soient affichés à l'écran. J'utilise donc Inkscape pour créer deux sprites qui leurs correspondent.

En raison de l'unité de référence que nous avons choisi, la plate-forme de test fera 512 x 64 pixels.

Pour l'instant, je créerai un dossier assets/Dev/ qui contiendra les ressources temporaires liées au développement.

Voici les deux sprites utilisés pour l'instant :

Une fois les sprites réalisés, je crée donc les scènes associées :

  • pour le Joueur, un Kinematic Body 2D (ce choix est discuté à la section suivante).
  • pour la plate-forme, un Static Body 2D .

De manière intuitive, chacun d'entre eux aura un nœud Sprite et une Collision Shape 2D de la bonne taille.

Les calques et masques de collisions seront également réglés correctement (le Joueur est sur le calque 1 et détecte le masque 4 (entre autres), et la plate-forme est sur le calque 4 et ne détecte rien).

Ensuite, je les ajoute au nœud Main en les instanciant. Ils ne bougent pas, mais ils apparaissent à l'écran ! À noter : dans le futur, ceux-ci seront plutôt instanciés à l'intérieur d'un nœud “niveau”.

Notes à propos de la méthode de collision

Après quelques recherches, nous sommes arrivés à plusieurs conclusions concernant le système de collisions liées au Joueur.

C'est un nœud KinematicBody2D qui sera le plus pertinent pour celui-ci. Il existe de nombreux articles sur la différence entre les KinematicBody et les RigidBody qui rentrent en détail sur les différences de ceux-ci, et insistent sur le fait qu'un personnage jouable est plus facilement géré via le KinematicBody en raison du contrôle fin qu'il est possible de lui donner sans avoir à contrebalancer l'intégration automatique des forces physiques sur le nœud.

Si vous n'êtes pas familier avec les corps physiques de Godot, voici l'article dédié.

Voici la documentation du système de collision que nous utilisons.

Nous allons utiliser la méthode move_and_slide_with_snap() afin de déplacer le joueur, ce qui est très pratique et facile à faire. Comparée à la méthode move_and_slide(), celle-ci permet d'accrocher automatiquement la hitbox du joueur à la hitbox de l’environnement avec laquelle il est en contact, même si son déplacement l'amène à traverser celle-ci. Il n'y a donc pas à corriger sa position 'à la main'.

La méthode move_and_slide() est une méthode très pratique dans le cas d'un clone de Super Mario Bros 3, ou d'un jeu en vue de dessus comme The Binding Of Isaac. Elle permet d'indiquer le déplacement du Joueur, à l'aide d'un Vecteur 2D, et s'occupe automatiquement de faire glisser le Joueur contre la hitbox de l'environnement. Si un vecteur définissant l'orientation du sol lui est attribué, elle permet de différencier les murs, contre lesquels le joueur va glisser, et le sol, contre lequel la vélocité de déplacement est remise systématiquement à zéro, et empêche donc de glisser.

Je pensais en premier lieu que cela ne permettait pas d'implémenter certaines fonctionnalités traditionnelles des metroidvania récents, comme le saut mural. En effet, move_and_slide() est pensée pour ne proposer que la détection du sol, et n'est donc pas sensée faire la différence entre le plafond et le mur. Heureusement, les développeurs ont ajouté deux fonctions spécifiques pour ces cas particuliers : is_on_wall() et is_on_ceiling().

Le cas échéant, c'est la méthode move_and_collide() que nous aurions utilisée. Lorsque cette méthode est appelée avec la vélocité du joueur en argument, celle-ci renvoie un objet KinematicCollision2D, qui contient différentes informations à propos de la collision, notamment le vecteur de déplacement absorbé par l'objet en collision. En analysant cet objet, il devient possible de déduire l'orientation de la hitbox que le personnage vient de toucher, et donc de différencier le sol, les murs, le plafond ou encore certaines pentes. Il est ainsi possible d'implémenter soi-même la réponse à la collision de manière fine.

Note à propos de la classe Joueur

Dans les versions précédentes de Godot, l'exemple Platformer n'utilisait pas de système d'héritage pour mutualiser l'attraction terrestre entre le joueur et les ennemis. C'est maintenant le cas, et le script Player hérite d'Actor, qui lui-même de Kinematic Body 2D.

Les scripts en deviennent cependant moins intuitifs et cela peut conduire à des erreurs.

Notamment, dans le script Actor.gd de l'exemple, la vélocité verticale est modifiée par le delta de la fonction. Or, le paramètre delta n'est pas sensé être utilisé lors de l'appel à move_and_slide(). Ce paramètre est donc redéfini dans Player.gd, ce qui est contre-intuitif.

De fait nous n'utiliserons pas d'héritage pour la classe Joueur.gd.

Implémentation de la gravité

[ NOTE ] : Comme discuté 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é :

extends KinematicBody2D

var _gravite = 50

var _masse = 1

var _velocite = Vector2.ZERO

func _ready():
	if get_tree().root.has_node("Global"):
		_gravite = Global.gravite

func _physics_process( _delta ):
	
	# Intégration de la gravité
	_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 ?
	)

Implémentation du saut

On ajoute au Joueur une capacité de saut. Plus les valeurs de masse et de forceSaut sont élevées, plus le saut est rapide et dynamique.

Personnage.gd

extends KinematicBody2D

var _gravite = 50

var _masse = 1
var _forceSaut = 1000

var _velocite = Vector2.ZERO

func _ready():
	if get_tree().root.has_node("Global"):
		_gravite = Global.gravite

func _physics_process( _delta ):
	
	entreeSaut()
	
	# Intégration de la gravité
	_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 ?
	)

# Appui sur le bouton de saut
func entreeSaut():
	# Mécanisme de saut
	if is_on_floor():
		if Input.is_action_just_pressed("espace"):
			_velocite.y -= _forceSaut

Ajout du déplacement latéral

Un simple algorithme qui permet de déplacer le Joueur sur l'axe horizontal. Ici, le plus intéressant est la méthode bouge(), qui ne peut fonctionner qu'au clavier : le nesting permet de mettre en place plusieurs comportement différents en fonction de l'ordre dans lequel les touches sont appuyées/relâchées.

Pas de phénomène d'accélération et d'inertie horizontale pour l'instant. Le déplacement pendant le saut est autorisé, et le restera probablement vu l'agréabilité de ce type de gameplay.

extends KinematicBody2D

var _gravite = 50

var _masse = 1
var _forceSaut = 1000
var _vitesse = 300

var _direction = 0

var _velocite = Vector2.ZERO

func _ready():
	if get_tree().root.has_node("Global"):
		_gravite = Global.gravite

func _physics_process( _delta ):
	
	bouge()
	entreeSaut()
	
	# Intégration du déplacement latéral
	_velocite.x = _direction * _vitesse
	# Intégration de la gravité
	_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 ?
	)

# Appui sur le bouton de saut
func entreeSaut():
	# Mécanisme de saut
	if is_on_floor():
		if Input.is_action_just_pressed("espace"):
			_velocite.y -= _forceSaut

# Appui sur les boutons clavier de déplacement
func bouge():
	if Input.is_action_just_pressed("gauche"):
		_direction = -1
	elif Input.is_action_just_pressed("droite"):
		_direction = 1
	
	if Input.is_action_just_released("gauche"):
		if not Input.is_action_pressed("droite"):
			_direction = 0
		else:
			_direction = 1
	
	if Input.is_action_just_released("droite"):
		if not Input.is_action_pressed("gauche"):
			_direction = 0
		else:
			_direction = -1

Ajout d'une inertie latérale

Il y a maintenant nécessité pour le Joueur de se mettre en mouvement et d'augmenter sa vitesse avant d'atteindre le maximum. Celui-ci met également un peu de temps à s'arrêter sur le sol. Cette décélération est absente lorsqu'il est en l'air. Pour l'instant, être collé à un mur permet tout-de-même d'augmenter sa vitesse : cela sera réglé lors de l'appel à is_on_wall().

extends KinematicBody2D

var _gravite = 50

var _masse = 1
var _forceSaut = 350
var _vitesse = 300
var _acceleration = 20
var _deceleration = 10

var _direction = 0
var _vitesseActuelle = 0

var _velocite = Vector2.ZERO

func _ready():
	if get_tree().root.has_node("Global"):
		_gravite = Global.gravite

func _physics_process( _delta ):
	
	bouge()
	entreeSaut()
	traitementDuMouvement()
	
	# Intégration du déplacement latéral
	_velocite.x = _vitesseActuelle
	# Intégration de la gravité
	_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 ?
	)

# Appui sur le bouton de saut
func entreeSaut():
	# Mécanisme de saut
	if is_on_floor():
		if Input.is_action_just_pressed("espace"):
			_velocite.y = _forceSaut

# Appui sur les boutons clavier de déplacement
func bouge():
	if Input.is_action_just_pressed("gauche"):
		_direction = -1
	elif Input.is_action_just_pressed("droite"):
		_direction = 1
	
	if Input.is_action_just_released("gauche"):
		if not Input.is_action_pressed("droite"):
			_direction = 0
		else:
			_direction = 1
	
	if Input.is_action_just_released("droite"):
		if not Input.is_action_pressed("gauche"):
			_direction = 0
		else:
			_direction = -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

Affinage de l'algorithme de saut

On ajoute deux mécanismes pour rendre les sauts plus agréables.

Premièrement, l'interruption d'un saut en relâchant la barre espace multiplie la vélocité par un ratio inférieur à 1 (ici 0.6) si celle-ci est négative, une fois par saut. Le joueur peut donc sauter plus ou moins en haut en fonction du moment où il relâche le bouton. C'est notamment utilisé lors des passages de plate-formes où il y a des piques au-dessus du joueur.

Deuxièmement, la gravité contraire appliquée à la phase ascendante du saut est modifiée par un ratio qui peut nier tout ou partie de la gravité. Au lieu d'un saut en cloche, la phase ascendant peut donc être plus rapide que la phase descendante.

extends KinematicBody2D

var _gravite = 50

var _masse = 2
var _forceSaut = 2000
var _vitesse = 300
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"):
		_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 ?
	)

# Appui sur le bouton de saut
func entreeSaut():
	# Mécanisme de saut
	if is_on_floor():
		if Input.is_action_just_pressed("espace"):
			_velocite.y -= _forceSaut
			_saute = true

# 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 Input.is_action_just_pressed("gauche"):
		_direction = -1
	elif Input.is_action_just_pressed("droite"):
		_direction = 1
	
	if Input.is_action_just_released("gauche"):
		if not Input.is_action_pressed("droite"):
			_direction = 0
		else:
			_direction = 1
	
	if Input.is_action_just_released("droite"):
		if not Input.is_action_pressed("gauche"):
			_direction = 0
		else:
			_direction = -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

Ajout d'une Camera2D

L'ajout d'un nœud Camera2D permet de suivre facilement les déplacement du personnage.

À l'aide de l'inspecteur, j'ai simplement réglé son offset à [ 0, - 150 ], et coché la propriété Current.

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.

# 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 ) )

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 :

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 :

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

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 :

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

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 :

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.

dindomoteur_creation_du_joueur.txt · Dernière modification: 2022/03/27 19:24 de Simon Deplat