Melhor maneira de gerenciar eventos no jogo?


13

Estou trabalhando em um jogo em que alguns eventos do jogo precisam acontecer de vez em quando. Um bom exemplo seria um tutorial. Você inicia o jogo e, em vários pontos do jogo, ocorre um evento:

  • Você encontra seu primeiro inimigo, o jogo faz uma pausa e você recebe uma explicação sobre como matá-lo.
  • Você matou o primeiro inimigo, recebe uma mensagem de "bom trabalho".
  • Você ganha um novo item, um menu com o pop-up de estatísticas de itens.
  • etc etc.

O jogo no qual estou trabalhando é um jogo de quebra-cabeça em que as regras do jogo são praticamente as mesmas, então parece ineficiente codificar todos esses eventos em níveis separados.

De alguma forma, devo definir esses eventos em uma fonte externa, como XML? Em seguida, escreva um intérprete que leia o XML e defina os requisitos de eventos para o nível? Não sei como definir um evento que deve ocorrer quando você mata dois inimigos, por exemplo.

Só para esclarecer, não estou procurando a melhor linguagem de programação ou linguagem de script para fazer isso, mas sim o melhor método para lidar com isso.

Obrigado!


Edit: Um segundo exemplo, já que minha pergunta era bastante difícil de entender:

O problema que estou tendo é colocar algumas ações extras no jogo em um procedimento que é sempre praticamente o mesmo. Como uma batalha de RPG, todo mundo tem um turno, escolhe uma habilidade etc. - é sempre a mesma. Mas e se houvesse um caso em que eu gostaria de exibir uma cena em algum lugar no meio. Modificar toda a estrutura do jogo para passar em uma classe de batalha alterada com a cena incluída parece muito ineficiente. Eu estou querendo saber como isso é feito normalmente.


8
Não tente generalizar demais as coisas, os tutoriais, por exemplo, são muito específicos e vêm com vários eventos / gatilhos diferentes. Nada de errado com a codificação / script.
Maik Semder

1
@Aik Se você colocar isso em uma resposta, identifique-o com +1. Simples e resolvido é melhor do que qualquer dia.
James

Seu segundo exemplo deixa muito mais claro que um sistema abstrato de mensagens seria uma grande vitória. Para um tutorial, você pode apenas codificar as coisas, pois elas acontecem apenas uma vez no início, mas para eventos em andamento que podem acontecer a qualquer momento durante toda a duração do jogo, bem, isso é diferente.
Jhocking 03/07

Ainda é um pouco vago, liste pelo menos três gatilhos para três cenas diferentes. é muito difícil responder em geral. Basicamente, você deve encontrar um padrão comum para entender como implementá-lo melhor.
Maik Semder

O que você quer? Deseja pausar as ações e fazer o extra, e não pausar as ações?
user712092

Respostas:


7

Isso depende muito de como os eventos são realmente comunicados entre os objetos no seu jogo. Por exemplo, se você estiver usando um sistema central de mensagens, poderá ter um módulo de tutorial que escute determinadas mensagens e crie pop-ups de tutoriais sempre que ouvir determinadas mensagens. Em seguida, você pode definir qual mensagem escutar, juntamente com qual pop-up exibir, em um arquivo XML ou em algo analisado pelo módulo do tutorial. Por ter um objeto de tutorial separado que monitora o estado do jogo e exibe pop-ups de tutorial quando ele percebe coisas no jogo, você pode alterar o objeto do tutorial à vontade sem precisar alterar mais nada sobre o seu jogo. (Esse é o padrão Observador? Não estou familiarizado com todos os padrões de design.)

No geral, apesar de depender da complexidade do seu tutorial, se vale a pena se preocupar com isso. Codificar os eventos em seu código e / ou níveis não me parece muito importante para apenas alguns pop-ups de tutoriais. Estou curioso para saber exatamente o que você pensa que será ineficiente, pois tudo o que você deve fazer a cada acionador é apenas enviar uma mensagem para o módulo do tutorial, algo como TutorialModule.show ("1st_kill");


Eu acho que, como é um jogo de quebra-cabeça, sua lógica está em um local para vários níveis e tal, fazendo as verificações para fazermos um tutorial para que isso seja algo que dure por toda parte. Honestamente, se for um jogo de quebra-cabeça, acho que não será um grande sucesso, mesmo que não seja o código mais bonito, e no final do dia o código que funciona em um jogo que é enviado sempre é sempre. 100% melhor do que um código bonito que nunca vê a luz do dia;) #
James

Nunca pensei em algo como o padrão de observador, parece uma boa solução. Vou tentar, obrigada :) #
omgnoseat

7

Aqui estão as restrições de design como eu as entendo:

  1. O código principal da jogabilidade não se importa com os requisitos de nível e não deve ser associado ao código que lida com eles.

  2. Ao mesmo tempo, é o código principal da jogabilidade que sabe quando ocorrem os eventos específicos que atendem a esses requisitos (obter um item, matar um inimigo etc.)

  3. Diferentes níveis têm diferentes conjuntos de requisitos e esses precisam ser descritos em algum lugar.

Diante disso, eu provavelmente faria algo assim: Primeiro, faça uma classe que represente um nível de jogo. Ele encapsulará o conjunto de requisitos específicos que um nível possui. Possui métodos que podem ser chamados quando ocorrem eventos do jogo.

Dê ao código de jogo principal uma referência ao objeto de nível atual. Quando ocorrem eventos de jogo, ele vai dizer o nível chamando métodos nele: enemyKilled, itemPickedUp, etc.

Internamente, Levelprecisa de algumas coisas:

  • Estado para rastrear quais eventos já ocorreram. Dessa forma, ele pode distinguir o primeiro inimigo morto dos outros e sabe a primeira vez que você escolhe um determinado item.
  • Uma lista de LevelRequirementobjetos que descrevem o conjunto específico de metas necessárias para esse nível.

Quando você entra em um nível, você cria um Levelcom os LevelRequirements certos , configura o código de jogo e atribui esse nível.

Cada vez que um evento ocorre, a jogabilidade passa para Level. Por sua vez, calcula os dados agregados (número total de inimigos mortos, inimigos desse tipo mortos, etc.) Em seguida, percorre os objetos de requisitos, fornecendo a cada um os dados agregados. Um requisito testa para verificar se ele foi atendido e, se for o caso, gera qualquer comportamento resultante apropriado (mostrando o texto do tutorial etc.)

LevelRequirement basicamente precisa de duas coisas:

  1. Uma descrição de um teste para saber se o requisito foi atendido. Isso pode ser apenas uma função se o seu idioma facilitar, caso contrário, você pode modelá-lo em dados. (Ou seja, você tem um RequirementTypeenum com coisas do tipo FIRST_KILLe depois uma grande switchque sabe como verificar cada tipo.)
  2. Uma ação a ser executada quando o requisito for atendido.

Ainda há a questão de onde esses conjuntos de requisitos são descritos. Você pode fazer algo como XML ou outro formato de arquivo de texto. Isso é útil se:

  1. Os não programadores criarão níveis.
  2. Você deseja alterar os requisitos sem recompilar e / ou reiniciar.

Se nenhum desses for o caso, eu provavelmente os construiria diretamente no código. Mais simples é sempre melhor.


Os três primeiros pontos são uma descrição muito próxima do método que estou usando agora, impressionante! Sim, a coisa com a qual mais estou lutando é onde descrever o requisito e como traduzi-lo para o jogo (já que provavelmente será algo externo). Obrigado pela explicação detalhada :)
omgnoseat

5

Eu pensei que você precisava saber como fazer esses eventos e o restante da postagem é sobre isso. Se você deseja apenas armazenar esses eventos, use algum banco de dados relacional ou descreva-os por texto e use a linguagem de script (ele fará a análise e a avaliação de eventos). Vocês). :)

O que você deseja é reconhecer os eventos ocorridos (1) e, em seguida, executar algumas ações exigidas por esses eventos (imprimir mensagem, verificar pressionamento de tecla ...) (2). Você também deseja que esses eventos ocorram apenas uma vez (3).

Basicamente, você deseja verificar as condições e agendar algum comportamento.

Como reconhecer eventos (1)

  • Você deseja reconhecer eventos como estes "primeiro inimigo encontrado", "novo item ganho"
  • se parte genérica acontecer, " inimigo encontrado ", " item ganho " Você verifica a parte específica " primeiro ...", " novo item ganho"

De que são feitos os eventos

Em uma visão mais geral, cada evento é composto de:

  • condições prévias , você as verifica
  • ações que serão executadas quando as condições prévias forem cumpridas (diga "" Você matou o primeiro inimigo! ", diga" "faça combos pressionando os botões A e B", diga "pressione 'enter' para continuar", pressione a tecla "enter")

Como armazenar esses eventos

Em alguma estrutura de dados:

  • tenha uma lista de pré-condições (strings ou código se você estiver escrevendo em algum idioma de alto nível)
  • tenha uma lista de ações (elas podem ser cadeias, o mecanismo do Quake usa cadeias para eventos)

Você também pode armazená-lo no banco de dados relacional, embora pareça que não é necessário. Se você deseja tornar esse jogo grande, talvez seja necessário criar um.

Você então precisa analisar essas strings / coisas. Ou você pode usar alguma linguagem de script como Python ou LUA ou linguagem como LISP, todas elas podem analisar e executar para você. :)

Como usar esses eventos no loop do jogo (2)

Você precisará destas duas estruturas de dados:

  • fila de eventos (os eventos que estão programados para serem executados são colocados aqui)
  • fila de ações (ações agendadas, eventos implicam quais ações são executadas)

Algoritmo:

  • Se você reconhecer alguns dos eventos 's pré-condições são satisfeitas Você colocá-lo na fila de eventos
  • (3) Então você deve garantir que esse evento ocorra apenas uma vez se desejar :) (por exemplo, com o array booleano has_this_event_happened ["first inimigo found"])
  • (se a fila de ações estiver vazia, então) Se houver evento na fila de eventos Você coloca suas ações na fila de ações e remove-o da fila de eventos
  • Se houver ação na fila de ações Você começará a fazer o que é exigido por ela
  • Se essa ação for realizada, você a remove da fila de ações

Como fazer essas ações em si (2)

Você cria uma lista de objetos que possuem a função "update". Às vezes, são chamadas de entidades (no mecanismo do Quake) ou de atores (no mecanismo do Unreal).

  1. Você inicia esses objetos quando eles são solicitados a iniciar na fila de ações.
  2. esses objetos podem ser usados ​​para outras coisas, como alguns outros temporizadores. No Quake, essas entidades são usadas para toda a lógica do jogo, eu recomendo que você leia algum material sobre isso .

Ação "diga alguma coisa"

  1. Você imprime algo na tela
  2. Você deseja que esta mensagem apareça por alguns segundos
  3. em "atualização":
    • faça a variável remove_me_after e diminua-a pelo tempo que passou
    • quando a variável é 0 Você remove esta ação da fila de ações
    • Você também remove este objeto (ou agende para que seja removido ...)

Ação "exigir chave"

  1. Depende de como você quer fazer isso, mas acho que você faz uma mensagem
  2. em "atualização" ":
    • Você acabou de verificar o evento de pressionamento de tecla desejado
    • Você provavelmente precisará de alguma matriz / fila para armazenar eventos de pressionamento de tecla
    • Você pode removê-lo da fila de ações e remover o objeto

Quais métodos aprender


-1 direito, ou ele simplesmente chama uma função, a sério, OP só quer uma caixa de mensagem quando uma determinada condição é satisfeita
Maik Semder

É uma redação muito boa, mas não é exatamente o que eu estava procurando. Eu tive dificuldade em explicar o que eu quero. Eu adicionei uma segunda explicação: O problema que estou tendo é colocar algumas ações extras no jogo em um procedimento que é sempre praticamente o mesmo. Como uma batalha de RPG, todo mundo tem um turno, escolhe uma habilidade etc. - é sempre a mesma. Mas e se houvesse um caso em que eu exibisse uma cena em algum lugar no meio. Modificar toda a estrutura do jogo para passar em uma classe de batalha alterada com o cutsene incluído parece muito ineficiente. Eu estou querendo saber como isso geralmente é feito?
Omgoseat
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.