Database
 sql >> Base de Dados >  >> RDS >> Database

Jogos MMO e Design de Banco de Dados


Sejamos honestos:todos nós gostamos de jogar, especialmente em nossos computadores. Até a Internet se espalhar, a maioria de nós jogava sozinho, geralmente contra oponentes de IA. Foi divertido, mas assim que você percebeu como funcionava a mecânica de jogo, o jogo perdeu a maior parte de sua magia.

O desenvolvimento da Internet moveu os jogos online. Agora, podemos jogar contra oponentes humanos e testar nossas habilidades contra as deles. Não há mais jogabilidade mecânica!

Então, os jogos multiplayer online (MMO) surgiram e mudaram tudo. Milhares de jogadores se encontraram nos mesmos universos de jogo, competindo por recursos, negociando, negociando e lutando. Para tornar esses jogos possíveis, era necessária uma estrutura de banco de dados que pudesse armazenar todas as informações relevantes.

Neste artigo, projetaremos um modelo que incorpora os elementos mais comuns encontrados em jogos MMO. Discutiremos como usá-lo, suas limitações e suas possíveis melhorias.

Uma introdução aos modelos de dados para jogos MMO


Existem muitos jogos MMO muito populares hoje em dia, e eles envolvem todos os tipos de cenários. Vou focar aqui em jogos de estratégia como Ogame , Travian , Esparta :Guerra dos Impérios e Imperia Online . Esses jogos são mais sobre planejamento, construção e estratégia, e menos sobre ação direta.

Os jogos MMO são ambientados em universos diferentes, são visualmente diferentes e usam opções de jogabilidade mais ou menos diferentes. Ainda assim, algumas ideias são as mesmas. Os jogadores competem por locais, lutam por eles e formam alianças com (e contra) outros jogadores. Eles constroem estruturas, coletam recursos e pesquisam tecnologias. Eles constroem unidades (como guerreiros, tanques, comerciantes, etc.) e as usam para negociar com aliados ou para lutar com oponentes. Tudo isso precisa ser suportado em nosso banco de dados.

Podemos pensar nesses jogos como jogos de tabuleiro online com muitos quadrados indexados. Cada quadrado pode ter muitas ações diferentes associadas a ele; algumas ações incluirão vários quadrados – por exemplo, quando movemos unidades ou recursos de um local para outro.




A base de dados está dividida em cinco áreas principais:
  • Players / Users
  • Alliances
  • Locations and Structures
  • Research and Resources
  • Units

As sete tabelas desagrupadas restantes estão relacionadas às unidades e descrevem a posição das unidades e os movimentos no jogo. Analisaremos cada uma dessas áreas com muito mais detalhes, começando com Jogadores e Alianças .

Jogadores e Alianças


Sem dúvida, os jogadores são a parte mais importante de qualquer jogo.



O player table contém uma lista de todos os jogadores registrados que participam de uma instância de jogo. Armazenaremos os nomes de usuário, senhas e nomes de tela dos jogadores. Eles serão armazenados no user_name , password e nickname atributos respectivamente.

Novos usuários precisarão fornecer um endereço de e-mail durante o registro. Um código de confirmação será gerado e enviado a eles, ao qual eles responderão. Atualizaremos a confirmation_date atributo quando o usuário verifica seu endereço de e-mail. Portanto, esta tabela tem três chaves exclusivas:user_name , nickname e email .

Cada vez que um usuário faz login, um novo registro é adicionado ao login_history tabela. Todos os atributos nesta tabela são autoexplicativos. O logout_time é específico. Pode ser NULL quando a sessão atual do usuário está ativa ou quando o usuário sai do jogo (sem sair) devido a problemas técnicos. No login_data atributo, armazenaremos detalhes de login como a localização geográfica de um jogador, endereço IP e o dispositivo e navegador que ele usa.



A maioria dos jogos MMO nos permite cooperar com outros jogadores. Uma das formas padrão de cooperação do jogador é a aliança. Os jogadores compartilham seus “dados privados” no jogo (status online, planos, localização de suas cidades e colônias, etc.) com outros para se beneficiarem de ações aliadas e por pura diversão.

A alliance table armazena informações básicas sobre alianças de jogo. Cada um tem um alliance_name exclusivo que vamos armazenar. Também teremos um campo, date_founded , que armazena quando a aliança foi fundada. Se uma aliança for dissolvida, armazenaremos essas informações no date_disbanded atributo.

O alliance_member tabela relaciona jogadores com alianças. Os jogadores podem entrar e sair da mesma aliança mais de uma vez. Por isso, o player_idalliance_id par não é uma chave única. Manteremos informações sobre quando um jogador se junta à aliança e quando (se) ele sai no date_from e date_to Campos. O membership_type_id atributo é uma referência ao membership_type dicionário; ele armazena o nível atual dos direitos dos jogadores na aliança.

Os direitos dos jogadores em uma aliança podem mudar ao longo do tempo. As membership_actions , membership_type e actions_allowed as tabelas juntas definem todos os direitos possíveis para os membros da aliança. Esse modelo não permite que os jogadores definam seus próprios níveis de direitos em uma aliança, mas isso pode ser feito com bastante facilidade adicionando novos registros no membership_type dicionário e armazenar informações sobre quais alianças estão relacionadas.

Resumindo:os valores armazenados nestas tabelas são definidos por nós durante a configuração inicial; eles mudarão apenas se introduzirmos novas opções.

O membership_history table armazena todos os dados sobre funções ou direitos dos jogadores dentro de uma aliança, incluindo o intervalo em que esses direitos eram válidos. (Por exemplo, ele pode ter permissões de "novato" por um mês e, depois, "associação completa" a partir desse ponto.) O date_to atributo é NULLable porque os direitos atualmente ativos ainda não terminaram.

As membership_actions dicionário contém uma lista de todas as ações que os jogadores podem fazer em uma aliança. Cada ação tem seu próprio action_name e a lógica do jogo é construída em torno desses nomes. Podemos esperar valores como “ver lista de membros” , “ver status dos membros” e "enviar mensagem" aqui.

O membership_type dicionário contém os nomes exclusivos dos grupos de ação usados ​​no jogo. As actions_allowed tabela atribui ações a tipos de associação. Cada ação pode ser atribuída a um tipo apenas uma vez. Portanto, a membership_action - membership_type par forma a chave exclusiva para esta tabela.

Locais e Estruturas



Locais de jogo são áreas onde os jogadores coletam recursos e constroem estruturas e unidades. Alguns jogos têm um intervalo predefinido de locais possíveis, enquanto outros podem permitir que os usuários definam seus próprios locais.

Em um espaço 3D, as localizações podem ser definidas com coordenadas [x:y:z]. Se um jogo tiver um intervalo predefinido, ele pode não permitir que os jogadores usem qualquer local fora do intervalo [0:1000] para todos os três eixos, portanto, estamos limitados a um espaço de 1000 * 1000 * 1000.

Por outro lado, talvez queiramos permitir que os jogadores insiram as coordenadas exatas de sua nova localização – por exemplo, [1001:2073:4] – e queremos que o jogo processe isso para eles.

Manteremos uma lista de todos os locais usados ​​em uma instância do nosso jogo no location tabela. Cada local tem seu próprio nome, mas os nomes não são exclusivos. Por outro lado, as coordinates atributo deve conter apenas valores exclusivos. As coordenadas de localização são armazenadas como valores de texto, para que possamos armazenar coordenadas para jogos 3D como [112:72:235]. As coordenadas para jogos 2D podem ser armazenadas como <1102:98>.

Em alguns jogos, os locais terão vários quadrados que são usados ​​para abrigar estruturas ou unidades. Manteremos essas informações na dimension atributo, que é um campo de texto. Uma dimensão pode ser simplesmente o número de quadrados em uma grade 2D ou 3D. O player_id O atributo armazena informações sobre o proprietário atual desse local. Pode ser NULL quando os locais são predefinidos e os jogadores competem para ocupá-los.

A structure table contém uma lista de todas as estruturas que podemos construir em vários locais do jogo. As estruturas representam melhorias que nos permitem produzir unidades melhores, realizar novos tipos de pesquisa, produzir mais recursos, etc. Cada estrutura usada no jogo tem seu próprio structure_name exclusivo . Alguns possíveis structure_name os valores são “fazenda”, “mina de minério”, “usina solar” e “centro de pesquisa”.

Podemos esperar que cada estrutura seja atualizada várias vezes, então também armazenaremos informações sobre seu nível atual. Cada atualização melhora a produção das estruturas, então produz mais recursos ou nos permite usar novos recursos no jogo. Não podemos saber o nível máximo de atualização com antecedência, então definiremos todas as coisas relacionadas ao nível (custos, tempo de atualização e produção) com fórmulas. Todas as fórmulas armazenadas no banco de dados são o núcleo da mecânica do jogo, e seu ajuste é crucial para o equilíbrio do jogo e a jogabilidade em geral.

Esse também é o caso da upgrade_time_formula atributo. Um exemplo de valor para este campo é * 30 min” , onde representa o nível para o qual queremos atualizar.

Na maioria dos casos, existem requisitos que devem ser atendidos antes que os jogadores tomem certas ações. Talvez precisemos concluir uma quantidade definida de pesquisas antes de podermos construir novas estruturas ou vice-versa. Armazenaremos o nível de pesquisa necessário para construir estruturas na prerequisite_research tabela. Os relacionamentos e o nível de estrutura necessário para iniciar várias pesquisas são mantidos no prerequisite_structure tabela. Em ambas as tabelas, as chaves estrangeiras research_id e structure_id são emparelhados para formar uma chave única. O level_required atributo é o único valor.

Estas duas tabelas, prerequisite_research e prerequisite_structure , também formam o núcleo do jogo.

Para cada estrutura, definiremos uma lista de pré-requisitos:outras estruturas e seus níveis mínimos que os jogadores devem ter para começar a construir. Armazenaremos esses dados no structure_required tabela. Aqui, structure_id representa a estrutura que queremos construir; structure_required_id é uma referência à(s) estrutura(s) de pré-requisitos e level é o nível necessário.

A structure_built A tabela armazena informações sobre os níveis de estrutura atuais em um determinado local. O upgrade_ongoing O atributo será definido apenas se uma atualização estiver em andamento, enquanto o upgrade_end_time O atributo conterá um carimbo de data/hora assim que a atualização for concluída.

A structure_formula tabela relaciona estruturas e recursos. O par de chaves estrangeiras para esta tabela forma sua chave exclusiva. Esta tabela também possui dois atributos de texto contendo fórmulas com como parâmetro. Definiremos essas fórmulas, uma para custos e outra para geração de recursos, no banco de dados. Eles serão semelhantes ao upgrade_time_formula . Precisamos deles porque devemos definir os recursos gastos na construção de cada estrutura. Também precisamos definir a produção de recursos após a atualização, se a estrutura gerar quaisquer recursos (ou seja, a mina de minério produzirá * 20 minério por dia).

Pesquisa e Recursos


Pesquisas (ou tecnologias) em jogos costumam ser requisito para a criação de outras funcionalidades. Sem certos níveis de pesquisa, novas estruturas ou tipos de unidades não podem ser construídos. A pesquisa também pode ter seus próprios requisitos. Um dos mais comuns é o nível de uma determinada estrutura, geralmente chamada de “laboratório de pesquisa”. Ou talvez os jogadores precisem completar um certo nível de pesquisa antes de iniciar uma nova pesquisa. Todos esses requisitos serão tratados nesta seção. Abaixo, podemos encontrar o modelo de dados para Pesquisa e Recursos:



A research tabela contém uma lista de todas as ações de pesquisa possíveis em nosso jogo. Ele usa a mesma lógica da structure tabela. O research_name atributo é a chave exclusiva da tabela, enquanto o upgrade_time_formula O campo contém uma representação de texto da fórmula de requisitos de tempo de pesquisa, com como seu parâmetro. Quaisquer recursos necessários para atualizações são definidos na upgrade_formula armazenado na research_formula tabela.

Assim como nas estruturas, definiremos a lista de todas as outras pesquisas e seus níveis que devem ser concluídos antes de iniciarmos outro tipo de pesquisa. Armazenaremos esses dados no research_required tabela, onde research_id representa a pesquisa desejada; research_required_id é uma referência à pesquisa de pré-requisitos e level é o nível necessário.

A pesquisa está relacionada a jogadores individuais e para cada jogador – pesquisa ch pair, devemos armazenar o nível de pesquisa atual de um jogador e qualquer status de atualização em andamento. Armazenaremos essas informações usando o research_level tabela da mesma maneira que usamos o structure_built tabela.

Recursos como madeira, minério, gemas e energia são extraídos ou coletados e usados ​​posteriormente para construir estruturas e outras melhorias. Armazenaremos uma lista de todos os recursos do jogo no resource dicionário. O único atributo aqui é o resource_name campo, e também é a chave exclusiva da tabela.

Para acompanhar a quantidade atual de recursos em cada local, usaremos o resources_on_location tabela. Novamente, um par de chaves estrangeiras (resource_id e location_id ) forma a chave única da tabela, enquanto o number O atributo armazena os valores atuais do recurso.

Unidades e Movimentos



Os recursos são usados ​​para produzir unidades. As unidades podem ser usadas para transportar recursos, atacar outros jogadores ou, em geral, pilhar e queimar.

A lista de tipos de unidades usadas em nosso jogo é armazenada na unit dicionário com apenas um valor, unit_name; esse atributo é a chave exclusiva desta tabela. Algumas unidades de jogo comuns são “swordsman”, “battlecruiser”, “griffin”, “jet fighter”, “tank”, etc.

Precisamos descrever cada unidade com características específicas. Uma lista de todas as características possíveis é armazenada na characteristic dicionário. O characteristic_name campo contém um valor exclusivo. Os valores neste campo podem incluir:“ataque”, “defesa” e “pontos de vida”. Atribuiremos características às unidades usando o unit_characteristic relação. O par de chaves estrangeiras de unit_id e characteristic_id formam a chave única da tabela. Usaremos apenas um atributo, value , para armazenar o valor desejado.

A research_unit A tabela contém uma lista de todas as atividades de pesquisa que devem ser concluídas antes que possamos iniciar a produção de um determinado tipo de unidade. O unit_cost tabela define os recursos necessários para produzir uma única unidade. Ambas as tabelas têm chaves únicas compostas pelo par de chaves estrangeiras (research_id ou resources_id combinado com unit_id ) e um campo de valor (cost e level_required ).



E agora, a parte divertida. A produção é divertida, mas mover as unidades e agir é ainda melhor. Já apresentamos a unit table, mas vamos mantê-lo aqui por causa de como ele se relaciona com outras tabelas.

Ou as unidades estão estacionadas em um local ou estão se movendo entre os locais. Adicionando o player_id O campo determina quem é o proprietário do local ou do grupo que está se movendo entre os locais.

Se as unidades estiverem apenas estacionadas no local determinado, armazenaremos esse local e o número de unidades estacionadas lá. Para fazer isso, usaremos o units_on_location tabela.

Quando as unidades não estão estacionadas, elas estão se movendo. Precisaremos armazenar o ponto de partida e o destino. Além disso, precisamos definir possíveis ações durante os movimentos. Todas essas ações são armazenadas no movement_type dicionário. O type_name atributo é único enquanto o allows_wait O atributo determina se uma ação permite esperar no ponto de destino.

Podemos mover um único tipo de unidade, mas em quase todos os casos moveremos muitas unidades de vários tipos de unidades diferentes. Esse grupo compartilhará dados comuns e nós os armazenaremos no group_movement tabela. Nesta tabela, definiremos os seguintes itens:

  • o jogador que iniciou essa ação
  • o tipo de ação
  • o ponto de partida
  • o ponto de destino
  • a arrival_time no destino
  • o return_time para o ponto de partida
  • o wait_time no destino

O return_time atributo pode ser NULL se for uma viagem de ida e wait_time é definido pelo jogador. As unidades pertencentes a um grupo são definidas por valores armazenados no units_in_group tabela. O par de chaves estrangeiras de units_id e group_moving_id forma a chave única da tabela. O número das unidades do mesmo tipo dentro de um grupo é definido no number atributo.

Cada movimento pode transportar recursos de um local para outro. Portanto, definiremos uma relação muitos-para-muitos entre o group_movement e os resources mesas. Além das chaves primárias e estrangeiras, o resources_in_group tabela contém apenas o number atributo. Este campo armazena a quantidade de recursos que os jogadores movem do ponto de partida até o destino.

Na maioria dos casos, os jogadores podem chamar outros para participar de sua aventura. Para suportar isso, usaremos duas tabelas:allied_movement e allied_groups . Um jogador iniciará uma ação conjunta, e isso criará um novo recorde no allied_movement tabela. Todos os grupos de unidades que participam de uma ação aliada são definidos por valores armazenados em allied_groups tabela. Cada grupo pode ser atribuído a uma ação aliada apenas uma vez, de modo que as chaves estrangeiras formam a chave exclusiva desta tabela.

Este modelo nos dá a estrutura básica necessária para construir um jogo de estratégia MMO. Ele contém os recursos mais importantes do jogo:locais, estruturas, recursos, pesquisas e unidades. Ele também os relaciona, nos permite definir pré-requisitos no banco de dados e também armazena a maior parte da lógica do jogo no banco de dados.

Depois que essas tabelas são preenchidas, a maior parte da lógica do jogo é definida e não esperamos que novos valores sejam adicionados. Quase todas as tabelas têm um valor de chave exclusivo, seja um nome de recurso ou um par de chaves estrangeiras. Alterar as características das unidades e as fórmulas de produção/custo nos permitirá alterar o equilíbrio do jogo na camada de banco de dados.

Como você mudaria esse modelo? O que você gosta e o que você faria diferente? Diga-nos na seção de comentários!