A etapa final é juntar todas as peças por meio de um gerenciador de jogo. O gerenciador de jogo controla a atribuição de objetos de jogabilidade aos jogadores e o fluxo do loop do jogo. Especificamente, o gerenciador de jogo:
Atribui objetos de jogabilidade, como tabuleiro e minitabuleiro, aos jogadores.
Controla a lógica do loop do jogo, incluindo o que acontece quando ocorre um movimento.
Determina quando um jogador ganhou e a jogabilidade termina.
Definir os tipos de movimento
Durante a vez do jogador, o jogador escolhe uma coordenada no tabuleiro. Quando uma coordenada é escolhida, existem dois tipos de movimentos diferentes:
Ataque: tentativa de destruir o pawn na posição especificada.
Revelação: revele todos os pawns em um determinado raio de uma determinada posição.
No módulo DataTypes, adicione a seguinte enum definindo os tipos de movimentos:
using{/Verse.org/Simulation}
using{/Verse.org/Random}
using{/UnrealEngine.com/Temporary/SpatialMath}
DataTypes<public> := module:
...
move_type<public> := enum<open>:
Attack
Como esta enum é aberta, você sempre pode adicionar mais tipos de movimentos no futuro.
Criar o gerenciador de jogo
Agora, crie um novo arquivo Verse denominado game_manager.verse e adicione um novo dispositivo do Modo Criativo denominado game_manager. Este será um único objeto residindo no mundo do jogo para controlar o fluxo do jogo.
Definir objetos por jogador
Cada jogador tem vários objetos associados a ele, incluindo um tabuleiro de jogo e um minitabuleiro, mas também quais ladrilhos atacaram, um evento para indicar que um movimento é feito e um evento para indicar que a coordenada escolhida foi alterada. Defina uma nova classe denominada 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){}Definir o gerenciador de jogo
A classe do gerenciador de jogo precisa associar jogadores aos seus objetos de jogador. Uma maneira ideal de associar um jogador a um objeto no Verse é por meio de um strong_map. Adicione os seguintes campos à sua classe de gerenciador de jogo:
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){}
Atribuir objetos do jogador
À medida que os jogadores entram, atribua objetos de jogador a cada jogador do PerPlayerObjects para o objeto PerPlayerManagement. Primeiro, obtenha todos os jogadores no jogo e, em seguida, atribua objetos de jogador a cada jogador. Se não houver objetos de jogador suficientes, lide com o erro. Adicione a seguinte função AssignPlayerObjects ao seu gerenciador de jogo:
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
...
game_manager := class(creative_device):
AssignPlayerObjects():void =
for (Index -> Player : GetPlayspace().GetPlayers()):
if:
Definir condição de vitória
A próxima ação é determinar quando um jogador atendeu à condição de vitória. Um jogador vence se não houver mais pawns para ele encontrar e destruir no tabuleiro. Isso é feito consultando diretamente o comprimento da matriz de pawns no tabuleiro. Adicione a seguinte função WinConditionMet ao gerenciador de jogo:
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}")
Essa função tem sucesso se, e somente se, não houver mais pawns restantes para encontrar e descartar para o jogador de entrada.
No movimento de ataque
Agora que você sabe como atribuir os objetos de jogo a cada jogador e determinar se um jogador ganhou ou não, o próximo passo é definir o que acontece durante um movimento de ataque. Quando um jogador ataca um dos ladrilhos do seu oponente, ocorrem as seguintes etapas:
Determine se há um pawn no tabuleiro na coordenada de ataque.
Se sim:
Remova o pawn do tabuleiro do jogador.
Defina um marcador de acerto no minitabuleiro do oponente.
Se não:
Defina um marcador de erro no minitabuleiro do oponente.
Adicione uma função denominada OnAttack à sua classe game_manager com a seguinte definição:
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}")
No movimento de revelação
O outro tipo de movimento é o movimento de revelação. Isso requer algumas configurações adicionais para funcionar sobre o movimento de ataque. Primeiro, adicione três novas funções ao seu módulo UtilityFunctions:
operador '-': defina a operação binária de subtração para dois objetostile_coordinate.Abs: obtenha o valor absoluto em termos de componentes de umtile_coordinate.FortniteDistance: obtenha a distância de malhação ou de malhação entre dois objetostile_coordinate.
UtilityFunctions<public> := module:
using{DataTypes}
...
Abs(TileCoordinate:tile_coordinate)<transacts>:tile_coordinate =
tile_coordinate:
Left := Abs(TileCoordinate.Left)
Forward := Abs(TileCoordinate.Forward)
A distância de Manhattan calcula a distância entre dois objetos tile_coordinate navegando ao longo das direções cardinais na grade de ladrilhos. Para obter mais informações, consulte https://en.wikipedia.org/wiki/Taxicab_geometry.
Agora que os utilitários estão definidos, defina o comportamento da função OnReveal. Quando um jogador escolhe revelar pawns em um determinado raio de um tile_coordinate para seu inimigo, as seguintes etapas ocorrem:
Encontre todos os pawns no tabuleiro do jogador que estão dentro de um
RevealDistancedefinido a partir da coordenada de entrada de acordo com aManhattanDistance.Para cada pawn dentro dessa distância, execute um efeito de revelação.
Adicione uma função denominada OnReveal à sua classe game_manager com a seguinte definição:
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:
Vez do jogador
Em seguida, junte as etapas para ver como é a vez de um jogador. Quando for a vez de um jogador, aguarde um dos dois eventos diferentes que podem ser sinalizados: MoveEvent ou CoordinateChangeEvent. Sempre que um desses eventos for sinalizado, abandone o outro evento e coloque-os lado a lado dentro de uma condição de race. Quando uma alteração de coordenada é sinalizada, o mesmo jogador deve continuar jogando até escolher um tipo de movimento. Portanto, apenas passe para o próximo jogador quando o ataque ou a revelação for selecionado.
Adicione a função OnTurn à sua classe game_manager com a seguinte definição:
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)
Definir o loop do jogo
Você pode finalmente construir o loop principal do jogo agora que a vez do jogador foi definida. Dentro do loop de jogo, ocorrem as seguintes etapas:
Obter todos os jogadores.
Atribuir um jogador para ter sua vez e o outro para esperar o primeiro jogador se mover.
Executar o loop até que um dos jogadores vença, com cada um jogando em turnos alternados.
Para fazer isso, adicione a seguinte função GameLoop à sua 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]):
Como começar a jogabilidade
A última coisa a fazer é atribuir objetos jogador a cada jogador e iniciar o loop de jogo. Faça isso automaticamente adicionando chamadas para AssignPlayerObjects e GameLoop à função OnBegin :
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
AssignPlayerObjects()
GameLoop()Resumo
Em suma, esta página guia você pelas seguintes etapas:
Definir os movimentos do jogo.
Construir o loop do jogo.
Determinar quando uma condição de vitória é atendida.
Ainda há muitas coisas que você pode fazer para tornar essa experiência exclusivamente sua, como:
Projetar e implementar uma interface de usuário.
Conectar quando e como o jogador se move são sinalizados ao gerenciador de jogo.
Definir o design do mundo e da configuração do jogo.
Criar efeitos para ataque e revelações.
Adicionar design de música e decoração do cenário.
Sinta-se à vontade para criar essas classes de jogabilidade principais, reconstruí-las, usar partes delas e criar tudo para você.
Arquivos
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