Sistema de pontuação e persistência durante a partida (GameMode, GameState e PlayerState)

Capítulo 11

Tempo estimado de leitura: 7 minutos

+ Exercício

Responsabilidades na prática: quem guarda o quê?

Para implementar pontuação com regras claras e persistência durante a partida, é essencial separar responsabilidades entre GameMode, GameState e PlayerState. Essa separação evita “gambiarras” como guardar pontuação no personagem (que pode ser destruído ao morrer) ou no HUD (que é apenas visual).

ClasseOnde existeResponsabilidade prática para pontuaçãoO que NÃO fazer
GameModeSomente no servidor (autoridade)Regras: como pontua, quando pontua, validação, reset de rodada, condições de vitóriaGuardar estado que o HUD precisa ler diretamente (cliente não acessa)
GameStateServidor e clientesEstado global da partida: pontuação do time, tempo, objetivo global, fase do jogoGuardar pontuação individual de cada jogador (use PlayerState)
PlayerStateServidor e clientesEstado individual persistente durante a partida: pontuação do jogador, nome, mortes, pingFazer lógica de regra (isso é do GameMode)

Regra de ouro para pontuação

  • Regras e validação ficam no GameMode.
  • Valor da pontuação do jogador fica no PlayerState.
  • HUD lê do PlayerState (ou de um binding/evento que venha dele) e apenas exibe.
  • Estado global (ex.: pontuação total do time) fica no GameState.

Estrutura sugerida (Blueprints)

Você pode implementar com Blueprints derivados das classes padrão:

  • BP_MyGameMode (deriva de GameModeBase ou GameMode)
  • BP_MyGameState (deriva de GameStateBase ou GameState)
  • BP_MyPlayerState (deriva de PlayerState)

Em Project Settings > Maps & Modes, defina essas classes como padrão do seu mapa/modo.

Passo a passo: pontuação ao coletar itens

1) Criar a variável de pontuação no PlayerState

No BP_MyPlayerState:

  • Crie a variável ScorePoints (tipo Integer).
  • Crie uma função AddScore com entrada Delta (Integer).

Implementação da função AddScore:

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

ScorePoints = ScorePoints + Delta

Opcional (recomendado para rastreabilidade): dispare um evento/dispatcher quando a pontuação mudar.

  • Crie um Event Dispatcher chamado OnScoreChanged com parâmetro NewScore (Integer).
  • No final de AddScore, chame OnScoreChanged passando ScorePoints.

2) Centralizar a regra no GameMode (validação e aplicação)

No BP_MyGameMode, crie uma função AwardScoreForPickup com entradas:

  • PlayerController (tipo PlayerController)
  • Points (Integer)

Fluxo recomendado:

  1. Validar se PlayerController é válido.
  2. Obter o PlayerState do controller.
  3. Cast para BP_MyPlayerState.
  4. Chamar AddScore(Points).
Function AwardScoreForPickup(PC, Points):  If PC is not valid: return  PS = PC->PlayerState  Cast PS to BP_MyPlayerState as MyPS  If cast failed: return  MyPS->AddScore(Points)

Por que aqui? Porque o GameMode é o lugar natural para aplicar regras: “quanto vale cada item”, “pode pontuar agora?”, “há multiplicador?”, “limite por tempo?”, etc.

3) Item coletável chama o GameMode (sem referência frágil)

No Blueprint do item coletável (ex.: BP_Pickup), você normalmente tem um evento de overlap/uso. Ao confirmar a coleta, em vez de tentar acessar o HUD ou o personagem diretamente, faça:

  • Descobrir quem coletou (geralmente o Other Actor do overlap).
  • Obter o Controller do jogador (se for um Character/Pawn).
  • Chamar o GameMode para conceder pontos.

Exemplo de fluxo (conceitual):

OnComponentBeginOverlap(OtherActor):  Pawn = OtherActor (cast para seu Pawn/Character)  PC = Pawn->GetController (cast para PlayerController)  GM = GetGameMode (cast para BP_MyGameMode)  GM->AwardScoreForPickup(PC, PointsValue)  DestroyActor (o pickup)

Observação importante: o item coletável não deve “guardar” referência fixa para o jogador, HUD ou PlayerState. Ele resolve tudo no momento do evento (overlap/uso), reduzindo acoplamento e evitando referências quebradas.

Atualização do HUD: ler do PlayerState (e não do GameMode)

Como o HUD é local do jogador, a fonte mais estável para pontuação individual é o PlayerState do jogador local. Duas abordagens comuns:

Abordagem A: Atualização por evento (recomendado para rastreabilidade)

No Widget do HUD (ex.: WBP_HUD):

  • No Event Construct, obtenha o Owning Player (PlayerController).
  • Do controller, obtenha PlayerState e cast para BP_MyPlayerState.
  • Faça Bind no dispatcher OnScoreChanged.
  • Quando o evento disparar, atualize o texto de pontuação.
WBP_HUD::Event Construct:  PC = GetOwningPlayer  PS = PC->PlayerState  MyPS = Cast to BP_MyPlayerState  Bind Event to MyPS.OnScoreChanged  (chamar também UpdateScoreText(MyPS.ScorePoints) para inicializar)  OnScoreChanged(NewScore):  ScoreTextBlock->SetText(NewScore as text)

Vantagens: o fluxo é explícito (quem mudou, quando mudou) e fácil de depurar.

Abordagem B: Binding direto (simples, mas menos rastreável)

Você pode fazer binding do TextBlock para uma função que lê ScorePoints do PlayerState. Funciona, mas pode atualizar com frequência e torna mais difícil rastrear “quem disparou a mudança”.

Reset de pontuação ao reiniciar o nível

Ao reiniciar o nível (ex.: Open Level no mesmo mapa), uma nova instância de GameMode e GameState será criada, e os PlayerState também tendem a ser recriados (dependendo do fluxo). Para garantir reset consistente:

Opção 1: Reset automático pela recriação (com validação)

  • Defina ScorePoints com valor padrão 0 no BP_MyPlayerState.
  • Garanta que o HUD sempre leia do PlayerState atual (re-bind no Construct).

Essa opção funciona bem quando reiniciar significa recarregar o mapa.

Opção 2: Reset explícito no GameMode (útil para “Restart” sem trocar de mapa)

No BP_MyGameMode, crie uma função ResetMatchScore:

  • Obter todos os PlayerStates (via GameState).
  • Para cada PlayerState, setar ScorePoints = 0 (ou chamar uma função ResetScore no PlayerState).
  • Disparar OnScoreChanged para atualizar HUD imediatamente.
ResetMatchScore:  GS = GetGameState (cast para BP_MyGameState ou GameStateBase)  ForEach PlayerState in GS.PlayerArray:    MyPS = Cast to BP_MyPlayerState    If valid:      MyPS.ScorePoints = 0      MyPS.OnScoreChanged.Broadcast(0)

Se você tiver também pontuação global (time/partida), resete no GameState aqui.

Usando GameState para pontuação global (opcional)

Se o jogo tiver uma pontuação compartilhada (ex.: “pontuação da equipe” ou “itens coletados no total”), crie no BP_MyGameState:

  • Variável TeamScore (Integer).
  • Função AddTeamScore(Delta) que soma e (opcionalmente) dispara dispatcher OnTeamScoreChanged.

O GameMode pode, ao conceder pontos individuais, também atualizar o total global no GameState quando fizer sentido.

Etapa de validação: evitar referências frágeis e tornar o fluxo rastreável

Checklist de robustez (anti-acoplamento)

  • O pickup não guarda referência fixa para HUD/PlayerState/Character. Ele resolve o coletor no evento e chama o GameMode.
  • O HUD não busca o GameMode para ler pontuação (GameMode não existe no cliente). O HUD lê do PlayerState.
  • A pontuação não fica no Character. Se o Pawn morrer/trocar, a pontuação permanece no PlayerState.
  • Regras ficam no GameMode: o pickup não decide “se vale ponto” além do seu valor; quem valida é o GameMode.

Checklist de rastreabilidade (debug do fluxo)

Adicione logs com Print String (ou um sistema de log próprio) em pontos-chave, com mensagens padronizadas:

  • No pickup, ao coletar: [Pickup] Collected by PC=... Points=...
  • No GameMode, ao conceder: [GameMode] AwardScore PC=... Points=... PS=...
  • No PlayerState, ao somar: [PlayerState] AddScore Delta=... NewScore=...
  • No HUD, ao atualizar: [HUD] OnScoreChanged NewScore=...

Validação prática:

  • Teste reiniciar o nível e confirme que a pontuação volta a 0 (ou ao valor esperado).
  • Teste coletar múltiplos itens rapidamente e verifique se a ordem dos logs faz sentido (Pickup → GameMode → PlayerState → HUD).
  • Force um cenário onde o Pawn é destruído/trocado (ex.: respawn) e confirme que a pontuação permanece, pois está no PlayerState.

Agora responda o exercício sobre o conteúdo:

Ao implementar um sistema de pontuação que precisa persistir durante a partida e ser exibido no HUD, qual organização de responsabilidades é a mais adequada?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

As regras ficam no GameMode, mas o valor persistente de pontuação do jogador deve estar no PlayerState, que existe para servidor e clientes. O HUD apenas exibe lendo do PlayerState (de preferência via evento/dispatcher).

Próximo capitúlo

Checkpoints e respawn: lógica de salvamento em runtime com Blueprints

Arrow Right Icon
Capa do Ebook gratuito Unreal Engine para Iniciantes: Fundamentos de Blueprints e Lógica de Gameplay
65%

Unreal Engine para Iniciantes: Fundamentos de Blueprints e Lógica de Gameplay

Novo curso

17 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.