Dans cette dernière étape du tutoriel Créer un jeu de casse-tête avec séquences de lumières balisées, vous trouverez le script complet du casse-tête et des conseils pour modifier davantage l'exemple fourni.
Script complet
Le code suivant est le script complet d'un casse-tête réutilisable qui demande au joueur de trouver la bonne combinaison de lumières en modifiant leur état avec des boutons.
using { /Fortnite.com/Devices }
using { /Verse.org/Native }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation/Tags }
using { /Verse.org/Simulation } }
# Dérivez la classe `tag` du module Verse.org/Simulation/Tags pour créer une nouvelle étiquette de jeu.
puzzle_light := class(tag){}
log_tagged_lights_puzzle := class(log_channel){}
<#
Vous pouvez utiliser une instance de cette classe pour vous abonner à l'événement
InteractedWithEvent d'un button_device et accéder aux propriétés par événement (dans ce cas, Indices et PuzzleDevice) dans votre gestionnaire d'événements.
C'est un peu comme si chaque bouton disposait de son propre état et de son propre gestionnaire d'événements.
Étant donné que classe n'a pas le spécificateur <concrete>, il n'est pas nécessaire de spécifier des valeurs par défaut pour les champs.
#>
button_event_handler := class():
# Positions utilisées pour accéder aux lumières que ce bouton contrôle.
Indices : []int
# tagged_lights_puzzle ayant créé ce button_event_handler afin de pouvoir appeler des fonctions dessus.
PuzzleDevice : tagged_lights_puzzle
OnButtonPressed(InPlayer : agent) : void =
# Demandez à PuzzleDevice d'activer/de désactiver les lumières aux positions que ce bouton contrôle.
PuzzleDevice.ToggleLights(Indices)
tagged_lights_puzzle := class<concrete>(creative_device):
Logger : log = log{Channel := log_tagged_lights_puzzle}
# Boutons que le joueur utilise pour interagir avec le casse-tête.
@editable
Buttons : []button_device = array{}
<#
Le nombre d'éléments dans ButtonsToLights doit correspondre au nombre d'éléments dans Buttons.
Chaque matrice d'index avec un index de base zéro décrit les lumières que le bouton à l'index Buttons[x] active/désactive (où 'x' représente l'index du bouton).
Par exemple, Buttons[2] active/désactive la première et la deuxième lumière, comme spécifié par array{0,1}.
#>
ButtonsToLights : [][]int = array{array{0, 3}, array{0, 1, 2}, array{0, 1}, array{1}}
<#
La matrice logique LightsState permet d'effectuer un suivi de l'état d'activation et de désactivation de toutes les lumières.
Elle permet en outre de définir l'état initial des lumières ; par conséquent, le nombre de ses éléments
doit correspondre au nombre de lumières avec la balise `puzzle_light`.
#>
@editable
var LightsState : []logic = array{false, false, false, false}
<#
Le casse-tête est résolu lorsque LightsState correspond à SolvedLightsState.
En d'autres termes, SolvedLightsState doit avoir autant d'éléments que LightsState.
#>
@editable
SolvedLightsState : []logic = array{true, true, true, true}
@editable
# Ce générateur d'objets est activé lorsque le casse-tête est résolu.
ItemSpawner : item_spawner_device = item_spawner_device{}
# Contient les lumières détectées via les balises de jeu.
var Lights : []customizable_light_device = array{}
# Contient les gestionnaires d'événements de l'événement InteractedWithEvent de chaque bouton pour se désabonner de l'événement une fois le casse-tête résolu.
var ButtonSubscriptions : []cancelable = array{}
OnBegin<override>()<suspends> : void =
SetupPuzzleLights()
<#
Pour chaque bouton et son index, si le bouton possède des valeurs LightsIndices valides,
créez un nouveau button_event_handler avec les index qu'il utilise et une référence à ce tagged_light_puzzle (Self).
Utilisez la fonction OnButtonPressed du gestionnaire pour vous abonner à l'événement InteractedWithEvent du bouton.
Enregistrez ces abonnements dans la matrice ButtonSubscriptions pour annuler l'abonnement plus tard, afin que le joueur ne puisse plus interagir avec le jeu de casse-tête après l'avoir résolu.
#>
set ButtonSubscriptions = for:
ButtonIndex -> Button : Buttons
LightIndices := ButtonsToLights[ButtonIndex]
do:
Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed)
SetupPuzzleLights() : void =
# Gardez à l'esprit que l'ordre des appareils n'est pas garanti lorsque ceux-ci sont récupérés via les balises de jeu.
TaggedActors := GetCreativeObjectsWithTag(puzzle_light{})
<#
Pour chaque acteur avec la balise puzzle_light, vérifiez s'il s'agit d'un appareil customizable_light_device en essayant de le convertir en ce type.
S'il est converti, obtenez son état LightState initial pour allumer (TurnOn()) ou éteindre (TurnOff()) l'appareil LightDevice.
Enregistrez tous les appareils customizable_light_device balisés dans la matrice Lumières.
#>
set Lights = for:
ActorIndex -> TaggedActor : TaggedActors
LightDevice := customizable_light_device[TaggedActor]
ShouldLightBeOn := LightsState[ActorIndex]
do:
Logger.Print("Adding Light at index {ActorIndex} with State:{if (ShouldLightBeOn?) then "On" else "Off"}")
if (ShouldLightBeOn?) then LightDevice.TurnOn() else LightDevice.TurnOff()
LightDevice # La dernière instruction d'une expression correspond à la valeur de son résultat. Les expressions `for` renvoient toutes les valeurs dans une matrice.
ToggleLights(LightIndices : []int) : void =
<#
Pour chaque index, obtenez l'état LightState correspondant et la référence à l'appareil Lumière.
S'ils sont tous deux valides, déterminez IsLightOn à partir de l'état actuel de la lumière (on->off ; off->on) et
définissez la valeur IsLightOn sur l'état LighsState[Index] correspondant.
#>
for:
LightIndex : LightIndices
IsLightOn := LightsState[LightIndex]
Light := Lights[LightIndex]
do:
<#
Pour annuler une valeur booléenne dans Verse : if (MyLogic ?) {false} else {true}
Ici, nous éteignons (TurnOff()) ou allumons (TurnOn()) la lumière avant de renvoyer la valeur négative.
#>
Logger.Print("Turning light at index {LightIndex} {if (IsLightOn?) then "Off" else "On"}")
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
if (set LightsState[LightIndex] = NewLightState): # L'indexation de la matrice risque d'échouer, elle doit donc être enveloppée dans un contexte d'échec.
Logger.Print("Updated the state for light at {LightIndex}")
# Dans la mesure où l'état LightsState peut avoir changé, vérifiez si le casse-tête est résolu.
if (IsPuzzleSolved[]):
Logger.Print("Puzzle solved!")
ItemSpawner.Enable()
# Désabonnez/annulez tous les gestionnaires d'événements pour que le joueur ne puisse plus activer/désactiver les lumières.
for (ButtonSubscription : ButtonSubscriptions):
ButtonSubscription.Cancel()
<#
Une fonction dotée de l'effet <decides> ne peut être utilisée que dans un contexte d'échec comme `if (IsPuzzleSolved[])`.
Notez l'appel de fonction avec [] au lieu de () pour les appels de fonction faillibles.
#>
IsPuzzleSolved()<decides><transacts> : void =
<#
Itérez chaque état LightsState et vérifiez s'il correspond à son état SolvedLightsState.
La première fois que le bloc interne `for` échoue, la fonction échoue et
l'échec se répercute sur l'appelant.
Sinon, la fonction réussit.
#>
for:
LightIndex -> IsLightOn : LightsState
IsLightOnInSolution := SolvedLightsState[LightIndex]
do:
IsLightOn = IsLightOnInSolution
À vous de jouer
Grâce à ce tutoriel, vous avez appris à créer un jeu de casse-tête réutilisable à l'aide de Verse, qui demande au joueur de trouver la bonne combinaison de lumières en modifiant leur état à l'aide de boutons.
Utilisez vos connaissances pour vous essayer aux défis suivants :
- Créez des balises supplémentaires et utilisez-les pour contrôler les lumières dans un ordre visuel déterminé.
- Utilisez différentes conditions et solutions initiales et ajoutez des boutons et des lumières pour créer d'autres casse-têtes avec la même configuration.
- Contrôlez plusieurs types d'appareils avec divers types d'appareils interagissant avec l'utilisateur.