L'étape finale consiste à rassembler tous les éléments via un gestionnaire de jeu. Le gestionnaire de jeu contrôle l'affectation des objets de jeu aux joueurs et le déroulement de la boucle de jeu. Plus précisément, le gestionnaire de jeu :
Attribue des objets de jeu, notamment un plateau ou un mini-plateau aux joueurs ;
Contrôle la logique de la boucle de jeu, y compris ce qui se passe lorsqu'un déplacement se produit ;
Détermine quand un joueur a gagné et quand le jeu se termine.
Définir les types de déplacements
Lors du tour d'un joueur, celui-ci choisit des coordonnées sur le plateau de jeu. Après avoir choisi des coordonnées, deux types de déplacements différents sont possibles :
Attaque : tentative de détruire le pion à l'emplacement indiqué.
Révélation : révéler tous les pions dans un certain rayon d'un emplacement donné.
Dans le module DataTypes, ajoutez l'énumération suivante en définissant les types de déplacements :
using{/Verse.org/Simulation}
using{/Verse.org/Random}
using{/UnrealEngine.com/Temporary/SpatialMath}
DataTypes<public> := module:
...
move_type<public> := enum<open>:
Attack
Étant donné que cette énumération est ouverte, vous pouvez toujours ajouter d'autres types de déplacements ultérieurement.
Créer le gestionnaire de jeu
Ensuite, créez un nouveau fichier Verse nommé game_manager.verse et ajoutez un nouvel appareil du mode Créatif nommé game_manager. Il s'agit d'un objet unique présent dans le monde du jeu pour contrôler le déroulement du jeu.
Définir les objets par joueur
Chaque joueur dispose de plusieurs objets qui lui sont associés, notamment un plateau de jeu et un mini-plateau, mais également les carrés qu'il a attaqués, un événement indiquant qu'un déplacement a été effectué et un événement indiquant que les coordonnées choisies ont changé. Définissez une nouvelle classe nommée per_player_objects :
using { /Verse.org/Simulation }
per_player_objects<public> := class:
@editable
Board<public>:board
@editable
Miniboard<public>:miniboard
var AttackedTiles<public>:[]tile_coordinate = array{}
MoveEvent<public>:event(tuple(tile_coordinate, move_type)) = event(tuple(tile_coordinate, move_type)){}
CoordinateChangeEvent<public>:event(tile_coordinate) = event(tile_coordinate){}Définir le gestionnaire de jeu
La classe de gestionnaire de jeu doit associer les joueurs à leurs objets de joueur. Pour associer un joueur à un objet dans Verse, il est recommandé d'utiliser un weak_map. Ajoutez les champs suivants à votre classe de gestionnaire de jeu :
using { /Verse.org/Simulation }
per_player_objects<public> := class:
@editable
Board<public>:board
@editable
Miniboard<public>:miniboard
var AttackedTiles<public>:[]tile_coordinate = array{}
MoveEvent<public>:event(tuple(tile_coordinate, move_type)) = event(tuple(tile_coordinate, move_type)){}
CoordinateChangeEvent<public>:event(tile_coordinate) = event(tile_coordinate){}
Assigner les objets de joueur
Lorsque les joueurs rejoignent la partie, attribuez des objets de joueur à chaque joueur à partir de l'objet PerPlayerObjects vers l'objet PerPlayerManagement. Obtenez d'abord tous les joueurs dans le jeu, puis attribuez des objets de joueur à chacun d'eux. S'il n'y a pas suffisamment d'objets de joueur, traitez l'erreur. Ajoutez la fonction AssignPlayerObjects suivante à votre gestionnaire de jeu :
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
...
game_manager := class(creative_device):
AssignPlayerObjects():void =
for (Index -> Player : GetPlayspace().GetPlayers()):
if:
Définir la condition de victoire
Vous devez ensuite déterminer quand un joueur remplit la condition de victoire. Un joueur gagne s'il n'y a plus de pions à trouver et à détruire sur le plateau. Pour cela, il est nécessaire d'interroger directement la durée de la matrice Pions dans le plateau. Ajoutez la fonction WinConditionMet suivante au gestionnaire de jeu :
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
...
game_manager := class(creative_device):
WinConditionMet(Player:player)<decides><transacts>:void =
# Player wins if no pawns remain
Print("Pawns remaining: {PerPlayerManagement[Player].Board.Pawns.Length}")
Cette fonction réussit si et seulement s'il n'y a plus de pions à trouver et à éliminer pour le joueur d'entrée.
Lors d'un déplacement de type Attaque
Maintenant que vous savez comment attribuer les objets du jeu à chaque joueur et déterminer si un joueur a gagné ou non, l'étape suivante consiste à définir ce qui se passe pendant un déplacement d'attaque. Lorsqu'un joueur attaque l'un des carrés de son adversaire, vous procédez comme suit :
Déterminez si un pion est présent sur le plateau aux coordonnées de l'attaque.
Oui :
Retirez le pion du plateau du joueur.
Placez un marqueur d'impact sur le mini-plateau de l'adversaire.
Non :
Placez un marqueur d'échec sur le mini-plateau de l'adversaire.
Ajoutez une fonction nommée OnAttack à votre classe game_manager avec la définition suivante :
OnAttack(Instigator:player, Recipient:player, TileCoordinate:tile_coordinate):void =
if:
InstigatorObjects := PerPlayerManagement[Instigator]
RecipientObjects := PerPlayerManagement[Recipient]
then:
# Determine if the attack is a hit
var MarkerType:marker_type = marker_type.Miss
Print("Attack coordinate: Left: {TileCoordinate.Left}, Forward: {TileCoordinate.Forward}")
Lors d'un déplacement de type Révélation
L'autre type de déplacement est le déplacement de révélation. Ce type de déplacement nécessite des réglages supplémentaires pour fonctionner avec le déplacement d'attaque. Tout d'abord, ajoutez trois nouvelles fonctions à votre module FloatFunctions :
operator'-': définissez l'opération binaire de soustraction pour deux objetstile_coordinate.Abs: obtenez la valeur absolue par composant d'un objettile_coordinate.ManhattanDistance: obtenez la distance de Manhattan (également appelée taxi-distance) entre deux objetstile_coordinate.
UtilityFunctions<public> := module:
using{DataTypes}
...
Abs(TileCoordinate:tile_coordinate)<transacts>:tile_coordinate =
tile_coordinate:
Left := Abs(TileCoordinate.Left)
Forward := Abs(TileCoordinate.Forward)
Le système Distance de Manhattan calcule la distance entre deux objets tile_coordinate en naviguant le long des points cardinaux de la grille de carrés. Pour en savoir plus, consultez la page https://fr.wikipedia.org/wiki/Distance_de_Manhattan.
Maintenant que les utilitaires sont définis, définissez le comportement de la fonction OnReveal. Lorsqu'un joueur choisit de révéler les pions dans un certain rayon d'une tile_coordinate pour son adversaire, vous procédez comme suit :
Recherchez tous les pions sur le plateau du joueur qui se trouvent dans la plage de
RevealDistancedéfinie à partir des coordonnées d'entrée selon le paramètreManhattanDistance.Pour chaque pion dans cette plage de distance, jouez un effet de révélation.
Ajoutez une fonction nommée OnReveal à votre classe game_manager avec la définition suivante :
OnReveal(Instigator:player, Recipient:player, TileCoordinate:tile_coordinate):void =
if:
InstigatorObjects := PerPlayerManagement[Instigator]
RecipientObjects := PerPlayerManagement[Recipient]
then:
for:
Pawn : InstigatorObjects.Board.Pawns
PawnTileCoordinate := InstigatorObjects.Board.GetTileCoordinate[Pawn]
ManhattanDistance(PawnTileCoordinate, TileCoordinate) < RevealDistance
do:
Tour du joueur
Ensuite, déterminez le déroulement du tour d'un joueur. Lorsque c'est au tour du joueur, attendez l'un des deux événements différents pouvant être signalés : MoveEvent ou CoordinateChangeEvent. Chaque fois que l'un de ces événements est signalé, annulez l'autre événement et placez ces événements côte à côte dans une condition race. Lorsqu'un changement de coordonnées est signalé, le même joueur doit continuer à jouer jusqu'à ce qu'il choisisse un type de déplacement. Par conséquent, ne passez au joueur suivant que si l'attaque ou la révélation est sélectionnée.
Ajoutez la fonction OnTurn à votre classe game_manager avec la définition suivante :
OnTurn(Player:player, Opponent:player)<suspends>:void =
if (PlayerObjects := PerPlayerManagement[Player]):
loop:
var Continue:logic = false
race:
block:
# Listens for a call to PerPlayerManager[Player].CoordinateChangeEvent.Signal(:tile_coordinate)
TileCoordinate := PlayerObjects.CoordinateChangeEvent.Await()
block:
# Listens for a call to PerPlayerManager[Player].MoveEvent.Signal(:tile_coordinate,:move_type)
Définir la boucle de jeu
Maintenant que le tour du joueur est défini, vous pouvez construire la boucle de jeu principale. Au sein de la boucle de jeu, vous procédez comme suit :
Obtenez tous les joueurs.
Assignez un joueur pour avoir son tour et l'autre pour attendre que le premier joueur se déplace.
Poursuivez la boucle jusqu'à ce que l'un des joueurs gagne, chacun jouant à tour de rôle.
Pour ce faire, ajoutez la fonction GameLoop suivante à votre classe game_manager :
GameLoop()<suspends>:void =
Players := GetPlayspace().GetPlayers()
if :
Players.Length = 2
var TurnPlayer:player = Players[0]
var OtherPlayer:player = Players[1]
then:
loop:
OnTurn(TurnPlayer, OtherPlayer)
if (WinConditionMet[TurnPlayer]):
Démarrer le jeu
La dernière chose à faire est d'attribuer des objets à chaque joueur et de lancer la boucle de jeu. Faites-le automatiquement en ajoutant des appels à AssignPlayerObjects et GameLoop à la fonction OnBegin :
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
AssignPlayerObjects()
GameLoop()Résumé
En résumé, cette page vous a guidé à travers les étapes suivantes :
Définir les déplacements dans le jeu.
Construire la boucle de jeu.
Déterminer quand une condition de victoire est remplie.
Vous pouvez apporter de nombreuses autres améliorations pour rendre cette expérience unique, notamment :
Concevoir et implémenter une interface utilisateur.
Coordonner le moment et la façon dont les mouvements du joueur sont signalés au gestionnaire de jeu.
Concevoir le monde et le paramètre du jeu.
Créer des effets pour les attaques et les révélations
Ajouter de la musique et habiller le décor.
N'hésitez pas à utiliser ces classes de gameplay, à les reconstruire, à en utiliser des éléments spécifiques et à vous les approprier.
.udatasmith
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { DataTypes }
using { UtilityFunctions }
per_player_objects<public> := class:
@editable
Board<public>:board
@editable