Tutoriel pour commencer en VR partie 2¶
Introduction¶

Dans cette partie de la série de tutoriels pour débuter en VR, nous allons ajouter un certain nombre de nœuds spéciaux basés sur RigidBody qui peuvent être utilisés dans la VR.
Ceci continue là où nous nous sommes arrêtés dans la dernière partie du tutoriel, où nous venons de terminer de faire fonctionner les contrôleurs VR et de définir une classe personnalisée appelée VR_Interactable_Rigidbody
.
Astuce
Vous pouvez trouver le projet fini dans le dépôt OpenVR GitHub.
Ajout de cibles destructibles¶
Avant de créer des nœuds spéciaux basés sur RigidBody, nous avons besoin de leur faire faire quelque chose. Faisons une simple sphère cible qui se brisera en mille morceaux lorsqu'elle sera détruite.
Ouvrez le fichier Sphere_Target.tscn
, qui se trouve dans le dossier Scenes
. La scène est assez simple, avec juste un nœud StaticBody avec une sphère en forme de CollisionShape, un nœud MeshInstance affichant un maillage de sphère, et un nœud AudioStreamPlayer3D.
Les nœuds spéciaux RigidBody vont gérer les dommages causés à la sphère, c'est pourquoi nous utilisons un nœud StaticBody au lieu de quelque chose comme un nœud Area ou RigidBody. En dehors de cela, il n'y a pas vraiment grand-chose à dire, alors passons directement à l'écriture du code.
Sélectionnez le nœud Sphere_Target_Root
et créez un nouveau script appelé Sphere_Target.gd`
. Ajouter le code suivant :
extends Spatial
var destroyed = false
var destroyed_timer = 0
const DESTROY_WAIT_TIME = 80
var health = 80
const RIGID_BODY_TARGET = preload("res://Assets/RigidBody_Sphere.scn")
func _ready():
set_physics_process(false)
func _physics_process(delta):
destroyed_timer += delta
if destroyed_timer >= DESTROY_WAIT_TIME:
queue_free()
func damage(damage):
if destroyed == true:
return
health -= damage
if health <= 0:
get_node("CollisionShape").disabled = true
get_node("Shpere_Target").visible = false
var clone = RIGID_BODY_TARGET.instance()
add_child(clone)
clone.global_transform = global_transform
destroyed = true
set_physics_process(true)
get_node("AudioStreamPlayer").play()
get_tree().root.get_node("Game").remove_sphere()
Voyons comment fonctionne le script.
Explication du code de Sphere Target¶
Tout d'abord, passons en revue toutes les variables de classe dans le script :
destroyed
: Variable permettant de savoir si la cible de la sphère a été détruite.destroyed_timer
: Variable permettant de savoir depuis combien de temps la sphère cible a été détruite.DESTROY_WAIT_TIME
: une constante pour définir la durée pendant laquelle la cible peut être détruite avant de se libérer/se supprimer.health
: Une variable pour stocker la quantité de santé de la sphère cible.RIGID_BODY_TARGET
: Une constante pour contenir la scène de la cible sphère détruite.
Note
N'hésitez pas à jeter un coup d’œil à la scène RIGID_BODY_TARGET
. Il s'agit juste d'un tas de nœuds RigidBody et d'un modèle de sphère brisée.
Nous allons instancier cette scène pour que, lorsque la cible sera détruite, on ait l'impression qu'elle s'est brisée en mille morceaux.
Explication étape par étape de la fonction _ready
¶
Tout ce que fait la fonction _ready
, c'est qu'elle empêche le _physics_process
d'être appelé en appelant set_physics_process
avec false
comme argument. La raison pour laquelle nous faisons cela est que tout le code dans physics_process
est destiné à détruire ce nœud lorsque suffisamment de temps s'est écoulé, ce que nous ne voulons faire que lorsque la cible a été détruite.
Explication étape par étape de la fonction _physics_process
¶
D'abord, cette fonction ajoute le temps, delta
, à la variable destroyed_timer
. Il vérifie ensuite si destroyed_timer
est supérieur ou égal à DESTROY_WAIT_TIME
. Si destroyed_timer
est supérieur ou égal à DESTROY_WAIT_TIME
, alors la cible sphère se libère/se supprime en appelant la fonction queue_free
.
Explication étape par étape de la fonction damage
¶
La fonction damage
sera appelée par les nœuds spéciaux RigidBody, qui transmettront la quantité de dommages faits à la cible, qui est une variable d'argument de fonction appelée damage
. La variable damage
contiendra la quantité de dommages que le nœud spécial RigidBody a fait à la sphère cible.
Cette fonction vérifie d'abord que la cible n'est pas déjà détruite en vérifiant si la variable destroyed
est égale à true
. Si destroyed
est égal à true
, alors la fonction appelle return
, donc aucun autre code n'est appelé. Il s'agit simplement d'un contrôle de sécurité, de sorte que si deux choses endommagent la cible exactement au même moment, la cible ne peut pas être détruite deux fois.
Ensuite, la fonction enlève la quantité de dégâts subis, damage
, de la santé de la cible, health
. Il vérifie ensuite si la health
est égale à zéro ou moins, ce qui signifie que la cible vient d'être détruite.
Si la cible vient d'être détruite, alors nous désactivons la propriété CollisionShape en mettant sa propriété disabled
à true
. Nous rendons alors la Sphere_Target
MeshInstance invisible en réglant la propriété visible
à false
. Nous faisons cela pour que la cible ne puisse plus affecter le monde physique et pour que le maillage de la cible non brisé ne soit pas visible.
Ensuite, la fonction instance la scène RIGID_BODY_TARGET
et l'ajoute comme enfant de la cible. Il fixe ensuite la global_transform
de la nouvelle scène instanciée, appelée clone
, à la global_transform
de la cible non brisée. La cible brisée commence donc à la même position que la cible non brisée, avec la même rotation et la même échelle.
Ensuite, la fonction met la variable destroyed
à true
pour que la cible sache qu'elle a été détruite et appelle la fonction set_physics_process
avec l'argument true
. Cela lancera l'exécution du code dans _physics_process
de sorte qu'après que DESTROY_WAIT_TIME
secondes se soient écoulées, la cible de la sphère se libèrera/se détruira elle-même.
La fonction obtient alors le nœud AudioStreamPlayer3D et appelle la fonction play
pour qu'il joue son son.
Enfin, la fonction remove_sphere
est appelée dans Game.gd
. Pour obtenir Game.gd
, le code utilise l'arbre des scènes et se fraye un chemin de la racine de l'arbre des scènes à la racine de la scène Game.tscn
.
Ajout de la fonction remove_sphere
à Game.gd
¶
Vous avez peut-être remarqué que nous appelons une fonction dans Game.gd
, appelée remove_sphere
, que nous n'avons pas encore définie. Ouvrez Game.gd
et ajoutez les variables de classe supplémentaires suivantes :
var spheres_left = 10
var sphere_ui = null
spheres_left
: La quantité de sphères cibles restantes dans le monde. Dans la scèneGame
fournie, il y a10
sphères, ce qui correspond à la valeur initiale.sphere_ui
: Une référence à la sphère UI. Nous l'utiliserons plus tard dans le tutoriel pour afficher la quantité de sphères restantes dans le monde.
Avec ces variables définies, nous pouvons maintenant ajouter la fonction remove_sphere
. Ajoutez le code suivant à Game.gd
:
func remove_sphere():
spheres_left -= 1
if sphere_ui != null:
sphere_ui.update_ui(spheres_left)
Passons rapidement en revue ce que fait cette fonction :
Tout d'abord, il retire un de la variable spheres_left
. Il vérifie ensuite si la variable sphere_ui
n'est pas égale à null
, et si elle n'est pas égale à null
, il appelle la fonction update_ui
sur sphere_ui
, en passant le nombre de sphères en argument de la fonction.
Note
Nous ajouterons le code pour sphere_ui
plus tard dans ce tutoriel !
Maintenant, Sphere_Target
est prête à être utilisée, mais nous n'avons aucun moyen de la détruire. Corrigeons cela en ajoutant des nœuds spéciaux basés sur RigidBody qui peuvent endommager les cibles.
Ajouter un pistolet¶
Ajoutons un pistolet comme premier nœud interactif RigidBody. Ouvrez le fichier Pistol.tscn
, que vous trouverez dans le dossier Scenes
.
Passons rapidement en revue quelques points importants de Pistol.tscn
avant d'ajouter le code.
Tous les nœuds du fichier Pistol.tscn
s'attendent à ce que le nœud racine soit tourné. Cela permet au pistolet de tourner correctement par rapport au contrôleur VR lorsqu'il est ramassé. Le nœud racine est un nœud RigidBody, dont nous avons besoin parce que nous allons utiliser la classe VR_Interactable_Rigidbody
que nous avons créée dans la dernière partie de cette série de tutoriels.
Il y a un nœud MeshInstance appelé Pistol_Flash
, qui est un simple maillage que nous utiliserons pour simuler l'éclair de bouche au bout du canon du pistolet. Un nœud MeshInstance appelé LaserSight
est utilisé comme guide pour viser avec le pistolet, et il suit la direction du nœud Raycast, appelé Raycast
, que le pistolet utilise pour détecter si sa 'balle' a touché quelque chose. Enfin, il y a un nœud AudioStreamPlayer3D à l'extrémité du pistolet que nous utiliserons pour jouer le son du tir du pistolet.
N'hésitez pas à regarder les autres parties de la scène si vous le souhaitez. La plupart des scènes sont assez simples, avec les changements majeurs mentionnés ci-dessus. Sélectionnez le nœud RigidBody appelé Pistol
et faites un nouveau script appelé Pistol.gd
. Ajouter le code suivant :
extends VR_Interactable_Rigidbody
var flash_mesh
const FLASH_TIME = 0.25
var flash_timer = 0
var laser_sight_mesh
var pistol_fire_sound
var raycast
const BULLET_DAMAGE = 20
const COLLISION_FORCE = 1.5
func _ready():
flash_mesh = get_node("Pistol_Flash")
flash_mesh.visible = false
laser_sight_mesh = get_node("LaserSight")
laser_sight_mesh.visible = false
raycast = get_node("RayCast")
pistol_fire_sound = get_node("AudioStreamPlayer3D")
func _physics_process(delta):
if flash_timer > 0:
flash_timer -= delta
if flash_timer <= 0:
flash_mesh.visible = false
func interact():
if flash_timer <= 0:
flash_timer = FLASH_TIME
flash_mesh.visible = true
raycast.force_raycast_update()
if raycast.is_colliding():
var body = raycast.get_collider()
var direction_vector = raycast.global_transform.basis.z.normalized()
var raycast_distance = raycast.global_transform.origin.distance_to(raycast.get_collision_point())
if body.has_method("damage"):
body.damage(BULLET_DAMAGE)
elif body is RigidBody:
var collision_force = (COLLISION_FORCE / raycast_distance) * body.mass
body.apply_impulse((raycast.global_transform.origin - body.global_transform.origin).normalized(), direction_vector * collision_force)
pistol_fire_sound.play()
if controller != null:
controller.rumble = 0.25
func picked_up():
laser_sight_mesh.visible = true
func dropped():
laser_sight_mesh.visible = false
Voyons comment fonctionne le script.
Explication du code du pistolet¶
Tout d'abord, remarquez comment, au lieu de extends RigidBody
, nous avons extends VR_Interactable_Rigidbody
. Cela fait que le script du pistolet étend la classe VR_Interactable_Rigidbody
de sorte que les contrôleurs VR savent qu'il peuvent interagir avec cet objet et que les fonctions définies dans VR_Interactable_Rigidbody
peuvent être appelées lorsque cet objet est tenu par un contrôleur VR.
Ensuite, regardons les variables de classe :
flash_mesh
: Une variable pour tenir le nœud MeshInstance qui est utilisé pour simuler le flash de la bouche du canon du pistolet.FLASH_TIME
: Une constante pour définir la durée de visibilité de l'éclair de bouche du canon. Cela permettra également de définir la vitesse à laquelle le pistolet peut tirer.flash_timer
: Variable permettant de contenir la durée pendant laquelle le flash de la bouche du canon a été visible.laser_sight_mesh
: Une variable pour contenir le nœud MeshInstance qui agit comme la 'visée laser' du pistolet.pistol_fire_sound
: Une variable pour contenir le nœud AudioStreamPlayer3D utilisé pour le son de tir du pistolet.raycast
: Une variable pour contenir le nœud Raycast qui est utilisé pour calculer la position de la balle et normale lorsque le pistolet est tiré.BULLET_DAMAGE
: Une constante pour définir la quantité de dommages causés par une seule balle du pistolet.COLLISION_FORCE
: Une constante qui définit la quantité de force qui est appliquée aux nœuds RigidBody lorsque la balle du pistolet entre en collision.
Explication étape par étape de la fonction _ready
¶
Cette fonction récupère les nœuds et les assigne à leurs propres variables. Pour les nœuds flash_mesh
et laser_sight_mesh
, les deux ont leur propriété visible
fixée à false
, donc ils ne sont pas visibles au départ.
Explication étape par étape de la fonction _physics_process
¶
La fonction _physics_process
vérifie d'abord si le flash de la bouche du canon du pistolet est visible en vérifiant si flash_timer
est supérieur à zéro. Si flash_timer
est supérieur à zéro, alors nous en retirons le temps, delta
. Ensuite, nous vérifions si la variable flash_timer
est zéro ou inférieure maintenant que nous en avons retiré delta
. Si c'est le cas, le timer du flash de la bouche du canon du pistolet vient de se terminer et nous devons donc rendre flash_mesh
invisible en définissant sa propriété visible
à false
.
Explication étape par étape de la fonction interact
¶
La fonction interact vérifie d'abord si le flash de la bouche du canon du pistolet est invisible en vérifiant si le flash_timer
est inférieur ou égal à zéro. Nous faisons cela pour pouvoir limiter la cadence de tir du pistolet à la durée pendant laquelle l'éclair de la bouche est visible, ce qui est une solution simple pour limiter la vitesse à laquelle le joueur peut tirer.
Si flash_timer
est égal ou inférieur à zéro, nous réglons alors flash_timer
sur FLASH_TIME
afin qu'il y ait un délai avant que le pistolet puisse tirer à nouveau. Après cela, nous avons réglé flash_mesh.visible
sur true
pour que le flash de bouche du canon du pistolet soit visible tandis que flash_timer
est supérieur à zéro.
Ensuite, nous appelons la fonction force_raycast_update
sur le nœud Raycast dans raycast
afin qu'il obtienne les dernières informations de collision du monde physique. Nous vérifions ensuite si le raycast
a frappé quelque chose en vérifiant si la fonction is_colliding
est égale à true
.
Si le raycast
frappe quelque chose, alors nous obtenons le PhysicsBody avec lequel il est entré en collision via la fonction get_collider
. Nous affectons le PhysicsBody touché à une variable appelée body
.
Nous obtenons alors la direction du Raycast en obtenant son axe directionnel positif Z
à partir de la Basis sur la global_transform
du nœud raycast
. Cela nous donnera la direction dans laquelle le raycast pointe sur l'axe Z, qui est la même direction que la flèche bleue sur le gizmo Spatial lorsque le Local space mode
est activé dans l'éditeur Godot. Nous stockons cette direction dans une variable appelée direction_vector
.
Ensuite, nous obtenons la distance entre l'origine du Raycast et le point de collision du Raycast en obtenant la distance de la position globale, global_transform.origin
du nœud raycast
au point de collision de la Raycast, raycast.get_collision_point
, en utilisant la fonction distance_to
. Cela nous donnera la distance que le Raycast a parcourue avant sa collision, que nous stockons dans une variable appelée raycast_distance
.
Ensuite, le code vérifie si le PhysicsBody, body
, a une fonction/méthode appelée damage
en utilisant la fonction has_method
. Si le PhysicsBody a une fonction/méthode appelée dommage
, alors nous appelons la fonction dommage
et nous passons BULLET_DAMAGE
pour qu'elle subisse les dommages de la balle qui entre en collision avec elle.
Que le nœud PhysicsBody ait une fonction dommage
ou non, nous vérifions ensuite si body
est un nœud basé sur RigidBody. Si body
est un nœud basé sur RigidBody, alors nous voulons le pousser lorsque la balle entre en collision avec lui.
Pour calculer la quantité de force appliquée, nous prenons simplement COLLISION_FORCE
et la divisons par raycast_distance
, puis nous multiplions le tout par body.mass
. Nous stockons ce calcul dans une variable appelée collision_force
. Cela fera que les collisions sur une distance plus courte appliquent plus de force que celles sur de plus longues distances, donnant une réponse de collision légèrement plus réaliste.
Nous poussons ensuite le RigidBody en utilisant la fonction apply_impulse
, où la position est un Vector3 zéro de sorte que la force est appliquée à partir du centre, et la force de collision est la variable collision_force
que nous avons calculée.
Que la variable raycast
ait touché quelque chose ou non, nous jouons alors le son du coup de pistolet en appelant la fonction play
sur la variable pistol_fire_sound
.
Enfin, nous vérifions si le pistolet est tenu par un contrôleur VR en vérifiant si la variable controller
n'est pas égale à null
. S'il n'est pas égal à null
, nous réglons alors la propriété de rumble
du contrôleur VR sur 0.25
, de sorte qu'il y ait un léger grondement lorsque le pistolet fait feu.
Explication étape par étape de la fonction picked_up
¶
Cette fonction rend simplement visible le laser_sight_mesh
MeshInstance en mettant la propriété visible
à true
.
Explication étape par étape de la fonction dropped
¶
Cette fonction rend simplement le laser_sight_mesh
MeshInstance invisible en réglant la propriété visible
sur false
.
Pistolet fini¶

C'est tout ce qu'il nous faut pour avoir des pistolets en état de marche dans le projet ! Allez-y et lancez le projet. Si vous montez les escaliers et attrapez les pistolets, vous pouvez tirer sur les sphères cibles dans la scène en utilisant le bouton de déclenchement du contrôleur VR ! Si vous tirez sur les cibles assez longtemps, elles se briseront en morceaux.
Ajouter un fusil à pompe¶
Ensuite, ajoutons un fusil de chasse au projet VR.
Ajout d'un fusil de chasse spécial RigidBody devrait être assez simple, car presque tout avec le fusil de chasse est pareil qu'avec le pistolet.
Ouvrez le fichier Shotgun.tscn
, que vous trouverez dans le dossier Scenes
, et jetez un coup d’œil à la scène. Presque tout est comme dans Pistol.tscn
. La seule chose qui est différente, au-delà des changements de noms, est qu'au lieu d'un seul Raycast, il y a cinq nœuds Raycast. C'est parce qu'un fusil de chasse tire généralement en forme de cône, nous allons donc émuler cet effet en ayant plusieurs nœuds Raycast qui tourneront aléatoirement en forme de cône lorsque le fusil de chasse tire.
En dehors de cela, tout est plus ou moins comme Pistol.tscn
.
Écrivons le code du fusil de chasse. Sélectionnez le nœud RigidBody appelé Shotgun
et faites un nouveau script appelé Shotgun.gd
. Ajouter le code suivant :
extends VR_Interactable_Rigidbody
var flash_mesh
const FLASH_TIME = 0.25
var flash_timer = 0
var laser_sight_mesh
var shotgun_fire_sound
var raycasts
const BULLET_DAMAGE = 30
const COLLISION_FORCE = 4
func _ready():
flash_mesh = get_node("Shotgun_Flash")
flash_mesh.visible = false
laser_sight_mesh = get_node("LaserSight")
laser_sight_mesh.visible = false
raycasts = get_node("Raycasts")
shotgun_fire_sound = get_node("AudioStreamPlayer3D")
func _physics_process(delta):
if flash_timer > 0:
flash_timer -= delta
if flash_timer <= 0:
flash_mesh.visible = false
func interact():
if flash_timer <= 0:
flash_timer = FLASH_TIME
flash_mesh.visible = true
for raycast in raycasts.get_children():
if not raycast is RayCast:
continue
raycast.rotation_degrees = Vector3(90 + rand_range(10, -10), 0, rand_range(10, -10))
raycast.force_raycast_update()
if raycast.is_colliding():
var body = raycast.get_collider()
var direction_vector = raycasts.global_transform.basis.z.normalized()
var raycast_distance = raycasts.global_transform.origin.distance_to(raycast.get_collision_point())
if body.has_method("damage"):
body.damage(BULLET_DAMAGE)
if body is RigidBody:
var collision_force = (COLLISION_FORCE / raycast_distance) * body.mass
body.apply_impulse((raycast.global_transform.origin - body.global_transform.origin).normalized(), direction_vector * collision_force)
shotgun_fire_sound.play()
if controller != null:
controller.rumble = 0.25
func picked_up():
laser_sight_mesh.visible = true
func dropped():
laser_sight_mesh.visible = false
La majorité de ce code est exactement le même que celui du pistolet, avec seulement quelques changements minuscules qui sont principalement juste des noms différents. Étant donné la similitude de ces scripts, concentrons-nous sur les changements.
Explication du code du fusil de chasse¶
Comme pour le pistolet, le fusil de chasse étend VR_Interactable_Rigidbody
, de sorte que les contrôleurs de la VR savent qu'ils peuvent interagir avec cet objet et quelles sont les fonctions disponibles.
Il n'y a qu'une seule nouvelle variable de classe :
raycasts
: Une variable pour contenir le nœud qui a tous les nœuds Raycast comme ses enfants.
La nouvelle variable de classe remplace la variable raycast
de Pistol.gd
, car avec le fusil de chasse nous devons traiter plusieurs nœuds Raycast au lieu d'un seul. Toutes les autres variables de classe sont identiques à Pistol.gd
et fonctionnent de la même manière, certaines sont juste renommées pour être non spécifiques au pistolet.
Explication étape par étape de la fonction interact
¶
La fonction interact vérifie d'abord si le flash de la bouche du canon du fusil est invisible en vérifiant si le flash_timer
est inférieur ou égal à zéro. Nous le faisons pour pouvoir limiter la cadence de tir du fusil à la durée pendant laquelle l'éclair de la bouche du canon est visible, ce qui est une solution simple pour limiter la vitesse à laquelle le joueur peut tirer.
Si flash_timer
est égal ou inférieur à zéro, nous mettons alors flash_timer
à FLASH_TIME
afin qu'il y ait un délai avant que le fusil ne puisse tirer à nouveau. Après cela, nous avons réglé flash_mesh.visible
à true
, de sorte que le flash de la bouche à l'extrémité du fusil soit visible tandis que flash_timer
est supérieur à zéro.
Ensuite, nous appelons la fonction force_raycast_update
sur le nœud Raycast dans raycast
afin qu'il obtienne les dernières informations de collision du monde physique. Nous vérifions ensuite si le raycast
a frappé quelque chose en vérifiant si la fonction is_colliding
est égale à true
.
Ensuite, nous passons par chacun des nœuds enfants de la variable raycasts
en utilisant une boucle for. De cette façon, le code passera par chacun des nœuds Raycast qui sont les enfants de la variable raycasts
.
Pour chaque nœud, nous vérifions si raycast
n'est pas un nœud Raycast. Si le nœud n'est pas un nœud Raycast, nous utilisons simplement continue
pour le sauter.
Ensuite, nous faisons tourner le nœud raycast
de façon aléatoire autour d'un petit cône de 10
degrés en réglant la variable rotation_degrees
du raycast
sur un Vector3 où les axes X et Z sont un nombre aléatoire compris entre -10
à 10
. Ce nombre aléatoire est sélectionné à l'aide de la fonction rand_range
.
Ensuite, nous appelons la fonction force_raycast_update
sur le nœud Raycast dans raycast
afin qu'elle obtienne les dernières informations de collision du monde physique. Nous vérifions ensuite si le raycast
a touché quelque chose en vérifiant si la fonction is_colliding
est égale à true
.
Le reste du code est exactement le même, mais ce processus est répété pour chaque nœud Raycast qui est un enfant de la variable raycasts
.
Si le raycast
frappe quelque chose, alors nous obtenons le PhysicsBody avec lequel il est entré en collision via la fonction get_collider
. Nous affectons le PhysicsBody touché à une variable appelée body
.
Nous obtenons alors la direction du raycast en obtenant son axe directionnel Z
positif à partir de la Basis sur la global_transform
du nœud raycast
. Cela nous donnera la direction dans laquelle le raycast pointe sur l'axe Z, qui est la même direction que la flèche bleue sur le gizmo Spatial lorsque le mode Local space mode
est activé dans l'éditeur Godot. Nous stockons cette direction dans une variable appelée direction_vector
.
Ensuite, nous obtenons la distance entre l'origine du raycast et le point de collision du raycast en obtenant la distancede la position globale, global_transform.origin
du nœud raycast
et le point de collision du raycast, raycast.get_collision_point
, en utilisant la fonction distance_to
. Cela nous donnera la distance que le Raycast a parcourue avant sa collision, que nous stockons dans une variable appelée raycast_distance
.
Ensuite, le code vérifie si le PhysicsBody, body
, a une fonction/méthode appelée damage
en utilisant la fonction has_method
. Si le PhysicsBody a une fonction/méthode appelée dommage
, alors nous appelons la fonction dommage
et nous passons BULLET_DAMAGE
pour qu'elle subisse les dommages de la balle qui entre en collision avec elle.
Que le nœud PhysicsBody ait une fonction dommage
ou non, nous vérifions ensuite si body
est un nœud basé sur RigidBody. Si body
est un nœud basé sur RigidBody, alors nous voulons le pousser lorsque la balle entre en collision avec lui.
Pour calculer la quantité de force appliquée, nous prenons simplement COLLISION_FORCE
et la divisons par raycast_distance
, puis nous multiplions le tout par body.mass
. Nous stockons ce calcul dans une variable appelée collision_force
. Cela fera que les collisions sur une distance plus courte appliquent plus de force que celles sur de plus longues distances, donnant une réponse de collision légèrement plus réaliste.
Nous poussons ensuite le RigidBody en utilisant la fonction apply_impulse
, où la position est un Vector3 zéro de sorte que la force est appliquée à partir du centre, et la force de collision est la variable collision_force
que nous avons calculée.
Une fois que toutes les variables Raycasts de la variable raycast
ont été itérées, nous jouons alors le son du coup de fusil en appelant la fonction play
avec la variable shotgun_fire_sound
.
Enfin, nous vérifions si le fusil est tenu par un contrôleur VR en vérifiant si la variable controller
n'est pas égale à null
. Si elle n'est pas égal à null
, nous réglons alors la propriété de rumble
du contrôleur VR sur 0.25
, de sorte qu'il y ait un léger grondement lorsque le fusil tire.
Fusil de chasse terminé¶
Tout le reste est exactement le même que le pistolet, avec tout au plus quelques simples changements de nom.
Maintenant, le fusil de chasse est terminé ! Vous pouvez trouver le fusil de chasse dans la scène exemple en regardant derrière l'un des murs (mais pas dans le bâtiment !).
Ajouter une bombe¶
Ok, ajoutons un autre RigidBody spécial. Au lieu d'ajouter quelque chose qui tire, ajoutons quelque chose que nous pouvons lancer - une bombe !
Ouvrez Bomb.tscn
, qui se trouve dans le dossier Scenes
.
Le nœud racine est un nœud RigidBody que nous allons étendre pour utiliser VR_Interactable_Rigidbody
, qui a un nœud CollisionShape comme les autres nœuds RigidBody spéciaux que nous avons créés jusqu'à présent. De même, il y a un MeshInstance appelé Bomb
qui est utilisé pour afficher le maillage de la bombe.
Ensuite, nous avons un nœud Area simplement appelé Area
qui a un grand CollisionShape comme enfant. Nous utiliserons ce nœud Area pour affecter tout ce qui s'y trouve lorsque la bombe explosera. Essentiellement, ce nœud Area sera le rayon d'explosion de la bombe.
Il y a aussi quelques nœuds Particules. L'un des nœuds Particules est destiné à la fumée qui sort du détonateur de la bombe, tandis qu'un autre est destiné à l'explosion. Vous pouvez jeter un œil aux ressources ParticlesMaterial, qui définissent le fonctionnement des particules, si vous le souhaitez. Nous ne couvrirons pas le fonctionnement des particules dans ce tutoriel car il ne fait pas partie du sujet.
Il y a une chose avec les nœuds Particles que nous devons noter. Si vous sélectionnez le nœud Explosion_Particles
, vous verrez que sa propriété lifetime
est fixée à 0.75
et que la case one shot
est activée. Cela signifie que les particules ne seront jouer qu'une fois, et que les particules dureront 0,75 seconde. Nous aurons besoin de savoir cela pour pouvoir chronométrer le retrait de la bombe à la fin de l'explosion Particules.
Écrivons le code de la bombe. Sélectionnez le nœud Bomb`
RigidBody et créez un nouveau script appelé Bomb.gd
. Ajouter le code suivant :
extends VR_Interactable_Rigidbody
var bomb_mesh
const FUSE_TIME = 4
var fuse_timer = 0
var explosion_area
const EXPLOSION_DAMAGE = 100
const EXPLOSION_TIME = 0.75
var explosion_timer = 0
var exploded = false
const COLLISION_FORCE = 8
var fuse_particles
var explosion_particles
var explosion_sound
func _ready():
bomb_mesh = get_node("Bomb")
explosion_area = get_node("Area")
fuse_particles = get_node("Fuse_Particles")
explosion_particles = get_node("Explosion_Particles")
explosion_sound = get_node("AudioStreamPlayer3D")
set_physics_process(false)
func _physics_process(delta):
if fuse_timer < FUSE_TIME:
fuse_timer += delta
if fuse_timer >= FUSE_TIME:
fuse_particles.emitting = false
explosion_particles.one_shot = true
explosion_particles.emitting = true
bomb_mesh.visible = false
collision_layer = 0
collision_mask = 0
mode = RigidBody.MODE_STATIC
for body in explosion_area.get_overlapping_bodies():
if body == self:
pass
else:
if body.has_method("damage"):
body.damage(EXPLOSION_DAMAGE)
if body is RigidBody:
var direction_vector = body.global_transform.origin - global_transform.origin
var bomb_distance = direction_vector.length()
var collision_force = (COLLISION_FORCE / bomb_distance) * body.mass
body.apply_impulse(Vector3.ZERO, direction_vector.normalized() * collision_force)
exploded = true
explosion_sound.play()
if exploded:
explosion_timer += delta
if explosion_timer >= EXPLOSION_TIME:
explosion_area.monitoring = false
if controller != null:
controller.held_object = null
controller.hand_mesh.visible = true
if controller.grab_mode == "RAYCAST":
controller.grab_raycast.visible = true
queue_free()
func interact():
set_physics_process(true)
fuse_particles.emitting = true
Voyons comment fonctionne le script.
Explication du code de la bombe¶
Comme avec les autres nœuds spéciaux RigidBody, la bombe étend VR_Interactable_Rigidbody
de sorte que les contrôleurs VR savent qu'ils peuvent interagir avec cet objet et que les fonctions définies dans VR_Interactable_Rigidbody
peuvent être appelées lorsque cet objet est tenu par un contrôleur VR.
Ensuite, regardons les variables de classe :
bomb_mesh
: Une variable pour tenir le nœud MeshInstance qui est utilisé pour la bombe non explosée.FUSE_TIME
: Une constante pour définir combien de temps le fusible va 'brûler' avant que la bombe n'explosefuse_timer
: Une variable pour contenir le temps qui s'est écoulé depuis que l'amorce de la bombe a commencé à brûler.explosion_area
: Une variable pour contenir le nœud Area utilisé pour détecter les objets dans l'explosion de la bombe.EXPLOSION_DAMAGE
: Une constante permettant de définir la quantité de dégâts causés par l'explosion de la bombe.EXPLOSION_TIME
: Une constante pour définir la durée de vie de la bombe après son explosion. Cette valeur doit être la même que la propriétélifetime
du nœud Particles de l'explosion.explosion_timer
une variable qui indique le temps écoulé depuis l'explosion de la bombe.exploded
: Une variable pour savoir si la bombe a explosé ou non.COLLISION_FORCE
: Une constante qui définit la quantité de force qui est appliquée aux nœuds RigidBody lorsque la bombe explose.fuse_particles
: Une variable pour contenir une référence au nœud Particles utilisé pour l'amorce de la bombe.explosion_particles
: Une variable pour contenir une référence au nœud Particles utilisé pour l'explosion de la bombe.explosion_sound
: Une variable pour contenir une référence au nœud AudioStreamPlayer3D utilisé pour le son de l'explosion.
Explication étape par étape de la fonction _ready
¶
La fonction _ready
récupère d'abord tous les nœuds de la scène de la bombe et les affecte à leurs variables de classe respectives pour une utilisation ultérieure.
Ensuite, nous appelons set_physics_process
et nous passons false
pour que _physics_process
ne soit pas exécuté. Nous faisons cela parce que le code dans _physics_process
va commencer à brûler le fusible et à faire exploser la bombe, ce que nous ne voulons faire que lorsque l'utilisateur interagit avec la bombe. Si nous ne désactivons pas le _physics_process
, le fusible de la bombe se déclenchera avant que l'utilisateur n'ait la possibilité d'obtenir la bombe.
Explication étape par étape de la fonction _physics_process
¶
La fonction _physics_process
vérifie d'abord si fuse_timer
est inférieur à FUSE_TIME
. Si c'est le cas, alors le détonateur de la bombe brûle toujours.
Si le fusible de la bombe brûle toujours, nous ajoutons alors le temps, delta
, à la variable fuse_timer
. Nous vérifions ensuite si fuse_timer
est supérieur ou égal à FUSE_TIME
, maintenant que nous y avons ajouté delta
. Si fuse_timer
est supérieur ou égal à FUSE_TIME
, alors le fusible vient de se terminer et nous devons faire exploser la bombe.
Pour faire exploser la bombe, nous arrêtons d'abord d'émettre des particules pour le fusible en réglant emitting
à false
sur fuse_particles
. On dit alors au nœud explosion Particules, explosion_particules
, d'émettre toutes ses particule en un seul coup en mettant one_shot
à true
. Après cela, nous réglons emitting
à true
sur explosion_particles
pour que la bombe ait l'air d'avoir explosé. Pour faire croire que la bombe a explosé, nous cachons le nœud MeshInstance en réglant bomb_mesh.visible
sur false
.
Pour éviter que la bombe n'entre en collision avec d'autres objets du monde de la physique, nous avons réglé les propriétés collision_layer
et collision_mask
de la bombe sur 0
. Nous changeons également le mode RigidBody en MODE_STATIC
pour que le RigidBody de la bombe ne bouge pas.
Ensuite, nous devons récupérer tous les nœuds PhysicsBody dans le nœud explosion_area
. Pour ce faire, nous utilisons les get_overlapping_bodies
dans une boucle for. La fonction get_overlapping_bodies
retournera un tableau des nœuds PhysicsBody dans le nœud Area, ce qui est exactement ce que nous recherchons.
Pour chaque nœud PhysicsBody, que nous stockons dans une variable appelée body
, nous vérifions s'il est égal à self
. Nous faisons cela pour que la bombe ne s'explose elle-même pas accidentellement, car l'explosion_area
pourrait potentiellement détecter la Bomb
RigidBody comme un corps physique dans la zone d'explosion.
Si le nœud PhysicsBody, body
, n'est pas la bombe, alors nous vérifions d'abord si le nœud PhysicsBody a une fonction appelée damage
. Si le nœud PhysicsBody a une fonction appelée damage
, nous l'appelons et lui passons EXPLOSION_DAMAGE
pour qu'il prenne les dommages de l'explosion.
Ensuite, nous vérifions si le nœud PhysicsBody est un nœud RigidBody. Si body
est un RigidBody, nous voulons le déplacer quand la bombe explose.
Pour déplacer le nœud RigidBody lorsque la bombe explose, nous devons d'abord calculer la direction de la bombe vers le nœud RigidBody. Pour ce faire, nous soustrayons la position globale de la bombe, global_transform.origin
de la position globale du RigidBody. Cela nous donnera un Vecteur3 qui pointe de la bombe vers le nœud RigidBody. Nous stockons ceci Vecteur3 dans une variable appelée direction_vector
.
Nous calculons ensuite à quelle distance le RigidBody est de la bombe en utilisant la fonction length
sur le direction_vector
. Nous stockons la distance dans une variable appelée bomb_distance
.
Nous calculons ensuite la force qui sera appliquée au nœud RigidBody lorsque la bombe explosera en divisant COLLISION_FORCE
par bomb_distance
, et en multipliant le résultat par collision_force
. Ainsi, si le nœud RigidBody est plus proche de la bombe, il sera poussé plus loin.
Enfin, nous poussons le nœud RigidBody en utilisant la fonction apply_impulse
, avec une position Vector3 de zéro et la force de collision multipliée par le vecteur de direction normalisé comme force. Cela enverra le nœud RigidBody voler lorsque la bombe explosera.
Après avoir parcouru tous les nœuds PhysicsBody dans la explosion_area
, nous mettons la variable exploded
à true
pour que le code sache que la bombe a explosé et nous appelons play
avec l'argument explosion_sound
pour que le son d'explosion soit joué.
D'accord, la section suivante du code commence par vérifier si exploded
est égal à true
.
Si exploded
est égal à true
, alors cela signifie que la bombe attend que les particules de l'explosion se terminent avant de se libérer/se détruire. Nous ajoutons le temps, delta
, à explosion_timer
afin de pouvoir suivre le temps écoulé depuis l'explosion de la bombe.
Si explosion_timer
est supérieur ou égal à EXPLOSION_TIME
après avoir ajouté delta
, alors le minuteur d'explosion vient de se terminer.
Si le timer d'explosion vient de se terminer, nous mettons explosion_area.monitoring
à false
. La raison pour laquelle nous faisons cela est qu'il y avait un bug qui affichait une erreur lorsque vous libériez/supprimiez un nœud Area lorsque la propriété monitoring
était vraie. Pour s'assurer que cela ne se produise pas, nous avons simplement mis monitoring
à faux sur explosion_area
.
Ensuite, nous vérifions si la bombe est tenue par un contrôleur VR en vérifiant si la variable controller
n'est pas égale à null
. Si la bombe est détenue par un contrôleur VR, nous mettons la propriété held_object
du contrôleur VR, controller
, à null
. Comme le contrôleur VR ne tient plus rien, nous rendons visible le maillage de la main du contrôleur VR en réglant controller.hand_mesh.visible
à true
. Ensuite, nous vérifions si le mode de capture du contrôleur VR est RAYCAST
, et si c'est le cas, nous mettons controller.grab_raycast.visible
à true
pour que le 'laser sight' du raycast soit visible.
Enfin, que la bombe soit tenue par un contrôleur VR ou non, nous appelons queue_free
pour que la scène de la bombe soit libérée/supprimée de la scène.
Explication étape par étape de la fonction interact
¶
D'abord, la fonction interact
appelle set_physics_process
avec l'argument true
pour que le code dans _physics_process
commence à s'exécuter. Cela allumera l'amorce de la bombe et conduira finalement à l'explosion de la bombe.
Enfin, nous commençons les particules de l'amorce en réglant fuse_particles.visible
à true
.
Bombe terminée¶
La bombe est prête ! Vous pouvez trouver les bombes dans le bâtiment orange.
En raison de la façon dont nous calculons la vitesse du contrôleur VR, il est plus facile de lancer les bombes en utilisant un mouvement de poussée plutôt qu'un mouvement de lancer plus naturel. La courbe lisse d'un mouvement de type lancer est plus difficile à suivre avec le code que nous utilisons pour calculer la vitesse des contrôleurs VR, donc ça ne fonctionne pas toujours correctement et peut conduire à des vitesses calculées de manière imprécise.
Ajouter une épée¶
Ajoutons un dernier nœud spécial basé sur RigidBody de qui peut détruire des cibles. Ajoutons une épée pour pouvoir couper les cibles !
Ouvrez le fichier Sword.tscn
, que vous trouverez dans le dossier Scenes
.
Il n'y a pas grand-chose qui se passe ici. Tous les nœuds enfants du nœud racine Sword
RigidBody sont tournés pour qu'ils soient positionnés correctement quand le contrôleur VR les prend, il y a un MeshInstance nœud pour afficher l'épée, et il y a un nœud AudioStreamPlayer3D qui contient un son pour l'épée entrant en collision avec quelque chose.
Il y a cependant une chose qui est légèrement différente. Il y a un nœud KinematicBody appelé Damage_Body
. Si vous le regardez, vous verrez qu'il ne se trouve sur aucune couche de collision, et qu'il ne se trouve au contraire que sur un seul masque de collision. Ainsi, le KinematicBody n'affectera pas les autres nœuds PhysicsBody de la scène, mais il sera toujours affecté par les nœuds PhysicsBody.
Nous allons utiliser le nœud Damage_Body
KinematicBody pour détecter le point de collision et la normale lorsque l'épée entre en collision avec quelque chose dans la scène.
Astuce
Bien que ce ne soit peut-être pas la meilleure façon d'obtenir les informations sur les collisions du point de vue des performances, cela nous donne beaucoup d'informations que nous pouvons utiliser pour le post-traitement ! En utilisant un KinematicBody de cette façon, nous pouvons détecter exactement où cette épée est entrée en collision avec d'autres nœuds PhysicsBody.
C'est vraiment la seule chose digne d'intérêt sur la scène de l'épée. Sélectionnez le nœud Sword
RigidBody et créez un nouveau script appelé Sword.gd
. Ajouter le code suivant :
extends VR_Interactable_Rigidbody
const SWORD_DAMAGE = 2
const COLLISION_FORCE = 0.15
var damage_body = null
func _ready():
damage_body = get_node("Damage_Body")
damage_body.add_collision_exception_with(self)
sword_noise = get_node("AudioStreamPlayer3D")
func _physics_process(_delta):
var collision_results = damage_body.move_and_collide(Vector3.ZERO, true, true, true);
if (collision_results != null):
if collision_results.collider.has_method("damage"):
collision_results.collider.damage(SWORD_DAMAGE)
if collision_results.collider is RigidBody:
if controller == null:
collision_results.collider.apply_impulse(
collision_results.position,
collision_results.normal * linear_velocity * COLLISION_FORCE)
else:
collision_results.collider.apply_impulse(
collision_results.position,
collision_results.normal * controller.controller_velocity * COLLISION_FORCE)
sword_noise.play()
Voyons comment fonctionne ce script !
Explication du code de l'épée¶
Comme pour les autres nœuds spéciaux RigidBody, l'épée étend VR_Interactable_Rigidbody
de sorte que les contrôleurs VR savent qu'ils peuvent interagir avec cet objet et que les fonctions définies dans VR_Interactable_Rigidbody
peuvent être appelées lorsque cet objet est tenu par un contrôleur VR.
Ensuite, regardons les variables de classe :
SWORD_DAMAGE
: Une constante pour définir la quantité des dégâts causés par l'épée. Ce dommage est appliqué à tous les objets de l'épée à chaque appel de_physics_process
COLLISION_FORCE
: Une constante qui définit la quantité de force appliquée aux nœuds RigidBody lorsque l'épée entre en collision avec un PhysicsBody.damage_body
: Une variable pour contenir le nœud KinematicBody utilisé pour détecter si l'épée blesse ou non un nœud PhysicsBody.sword_noise
: Une variable pour contenir le nœud AudioStreamPlayer3D utilisé pour jouer un son lorsque l'épée entre en collision avec quelque chose.
Explication étape par étape de la fonction _ready
¶
Tout ce que nous faisons dans la fonction _ready
est d'obtenir le nœud Damage_Body
KinematicBody et de l'assigner à damage_body
. Parce que nous ne voulons pas que l'épée détecte une collision avec la racine RigidBody du nœud de l'épée, nous appelons add_collision_exception_with
sur damage_body
et passons self
pour que l'épée ne soit pas détectée.
Enfin, on obtient le nœud AudioStreamPlayer3D pour le son de collision d'épée et on l'applique à la variable sword_noise
.
Explication étape par étape de la fonction _physics_process
¶
Nous devons d'abord déterminer si l'épée entre en collision avec quelque chose ou non. Pour ce faire, nous utilisons la fonction move_and_collide
du nœud damage_body
. Contrairement à la façon dont on utilise normalement move_and_collide
, nous ne passons pas une vitesse et nous passons plutôt un Vecteur3 vide. Parce que nous ne voulons pas que le nœud damage_body
se déplace, nous avons défini l'argument test_only
(le quatrième argument) comme true
donc le KinematicBody génère des informations sur les collisions sans provoquer réellement de collisions dans le monde des collisions.
La fonction move_and_collide
renverra un KinematicCollision qui contient toutes les informations dont nous avons besoin pour détecter les collisions sur l'épée. Nous assignons la valeur de retour de move_and_collide
à une variable appelée collision_results
.
Ensuite, nous vérifions si collision_results
n'est pas égal à null
. Si collision_results
n'est pas égal à null
, alors nous savons que l'épée a heurté quelque chose.
Nous vérifions ensuite si le PhysicsBody avec lequel l'épée est entrée en collision a une fonction/méthode appelée dommage
en utilisant la fonction has_method
. Si le PhysicsBody a une fonction appelée damage_body
, nous l'appelons et lui transmettons la quantité de dégâts que l'épée fait, SWORD_DAMAGE
.
Ensuite, nous vérifions si le PhysicsBody avec lequel l'épée est entrée en collision est un RigidBody. Si l'épée est entrée en collision avec un nœud RigidBody, nous vérifions alors si l'épée est tenue par un contrôleur VR ou non en vérifiant si contrôleur
est égal à null
.
Si l'épée n'est pas tenue par un contrôleur VR, controller
est égal à null
, alors nous déplaçons le nœud RigidBody avec lequel l'épée est entrée en collision en utilisant la fonction apply_impulse
. Pour la position
de la fonction apply_impulse
, nous utilisons la variable collision_position
stockée dans la classe KinematicCollision dans collision_results
. Pour la velocity
de la fonction apply_impulse
, nous utilisons la collision_normal
multipliée par la linear_velocity
du nœud de l'épée RigidBody multipliée par COLLISION_FORCE
.
Si l'épée est tenue par un contrôleur VR, controller
n'est pas égal à null
, alors nous déplaçons le nœud RigidBody avec lequel l'épée est entrée en collision en utilisant la fonction apply_impulse
. Pour la position
de la fonction apply_impulse
, nous utilisons la variable collision_position
stockée dans la classe KinematicCollision dans collision_results
. Pour la velocity
de la fonction apply_impulse
, nous utilisons la collision_normal
multipliée par la vitesse du contrôleur VR multipliée par la COLLISION_FORCE
.
Enfin, que le PhysicsBody soit un RigidBody ou non, nous jouons le son de l'épée qui entre en collision avec quelque chose en appelant play
sur sword_noise
.
Épée terminée¶

Cela étant fait, vous pouvez maintenant découper les cibles ! Vous pouvez trouver l'épée dans le coin entre le fusil et le pistolet.
Mise à jour de l'UI cible¶
Mettons à jour l'interface utilisateur au fur et à mesure que les sphères cibles sont détruites.
Ouvrez Main_VR_GUI.tscn
, que vous trouverez dans le dossier Scenes
. N'hésitez pas à regarder comment la scène est configurée si vous le souhaitez, mais pour éviter que ce tutoriel ne devienne trop long, nous ne couvrirons pas la configuration de la scène dans ce tutoriel.
Développez le nœud GUI
Viewport et sélectionnez ensuite le nœud Base_Control
. Ajoutez un nouveau script appelé Base_Control.gd
, et ajoutez ce qui suit :
extends Control
var sphere_count_label
func _ready():
sphere_count_label = get_node("Label_Sphere_Count")
get_tree().root.get_node("Game").sphere_ui = self
func update_ui(sphere_count):
if sphere_count > 0:
sphere_count_label.text = str(sphere_count) + " Spheres remaining"
else:
sphere_count_label.text = "No spheres remaining! Good job!"
Voyons comment ce script fonctionne très rapidement.
D'abord, dans _ready
, nous obtenons le Label qui indique combien de sphères il reste et nous l'attribuons à la variable de classe sphere_count_label
. Ensuite, nous obtenons Game.gd
en utilisant get_tree().root
et nous assignons sphere_ui
à ce script.
Dans update_ui
, nous changeons le texte du Label sphère. S'il reste au moins une sphère, nous modifions le texte pour montrer combien de sphères il reste dans le monde. S'il ne reste plus de sphères, nous changeons le texte et félicitons le joueur.
Ajout du dernier RigidBody spécial¶
Enfin, avant de terminer ce tutoriel, ajoutons un moyen de réinitialiser le jeu à l'intérieur de la VR.
Ouvrez Reset_Box.tscn
, que vous trouverez dans Scenes
. Sélectionnez le nœud Reset_Box
RigidBody et créez un nouveau script appelé Reset_Box.gd
. Ajouter le code suivant :
extends VR_Interactable_Rigidbody
var start_transform
var reset_timer = 0
const RESET_TIME = 10
const RESET_MIN_DISTANCE = 1
func _ready():
start_transform = global_transform
func _physics_process(delta):
if start_transform.origin.distance_to(global_transform.origin) >= RESET_MIN_DISTANCE:
reset_timer += delta
if reset_timer >= RESET_TIME:
global_transform = start_transform
reset_timer = 0
func interact():
# (Ignore the unused variable warning)
# warning-ignore:return_value_discarded
get_tree().change_scene("res://Game.tscn")
func dropped():
global_transform = start_transform
reset_timer = 0
Passons rapidement en revue le fonctionnement de ce script.
Explication du code de la boîte de réinitialisation¶
Comme pour les autres objets spéciaux RigidBody que nous avons créés, la boîte de réinitialisation étend VR_Interactable_Rigidbody
.
La variable de classe start_transform
stockera la transformation globale de la boîte de réinitialisation au début du jeu, la variable de classe reset_timer
contiendra la durée qui s'est écoulée depuis que la position de la boîte de réinitialisation s'est déplacée, la constante RESET_TIME
définit la durée pendant laquelle la boîte de réinitialisation doit attendre avant d'être réinitialisée, et la constante RESET_MIN_DISTANCE
définit à quelle distance la boîte de réinitialisation doit se trouver de sa position initiale avant que le minuteur de réinitialisation ne commence.
Dans la fonction _ready
, tout ce que nous faisons est de stocker la global_transform
de la position de réinitialisation lorsque la scène commence. Nous pouvons ainsi réinitialiser la position, la rotation et l'échelle de l'objet de la boîte de réinitialisation à cette transformation initiale lorsque suffisamment de temps s'est écoulé.
Dans la fonction _physics_process
, le code vérifie si la position initiale de la boîte de réinitialisation par rapport à la position actuelle de la boîte de réinitialisation est plus éloignée que la RESET_MIN_DISTANCE
. Si elle est plus loin, il commence à ajouter du temps, delta
, à reset_timer
. Une fois que le reset_timer
est supérieur ou égal à RESET_TIME
, nous réinitialisons la global_transform
à la start_transform
pour que la boîte de réinitialisation soit à nouveau dans sa position initiale. Nous mettons ensuite reset_timer
à 0
.
La fonction interact
recharge simplement la scène Game.tscn
en utilisant get_tree().change_scene
. Cela va recharger la scène de jeu, en remettant tout à zéro.
Enfin, la fonction dropped
réinitialise la global_transform
à la transformation initiale start_transform
de sorte que la boîte de réinitialisation est à sa position/rotation initiale. Ensuite, reset_timer
est réglé sur 0
, ce qui réinitialise le minuteur.
Boîte de réinitialisation terminée¶
Une fois cela fait, lorsque vous saisissez et interagissez avec la boîte de réinitialisation, la scène entière se réinitialise/redémarre et vous pouvez à nouveau détruire toutes les cibles !
Note
Rétablir la scène brusquement sans aucune sorte de transition peut entraîner une gêne en VR.
Notes finales¶

Ouf ! C'était beaucoup de travail.
Vous avez maintenant un projet VR entièrement fonctionnel avec plusieurs types de nœuds spéciaux RigidBody qui peuvent être utilisés et étendus. Nous espérons que cela servira d'introduction à la création de jeux de VR complets dans Godot ! Le code et les concepts détaillés dans ce tutoriel peuvent être développés pour en faire des jeux de puzzle, des jeux d'action, des jeux basés sur l'histoire, et plus encore !
Avertissement
Vous pouvez télécharger le projet terminé de cette série de tutoriels depuis le dépôt GitHub de OpenVR, sous l'onglet releases !