Discordo levemente de todos e digo que a abordagem relacional é razoável aqui. O interessante aqui é que os itens podem ter várias funções. A questão principal será que o mapeamento entre esse layout relacional e um layout OO no código não parecerá "natural", mas acho que no lado do banco de dados várias funções podem ser expressas de maneira limpa (sem codificações ou redundância estranhas, apenas se juntam) .
A primeira coisa a decidir é quanto dos dados é específico do item e quanto é compartilhado por todos os itens de um determinado tipo.
Aqui está o que eu faria se todos os dados fossem específicos do item:
// ITEMS table: attributes common to all items
item_id | name | owner | location | sprite_id | ...
1 | Light Saber | 14 (Tchalvek) | 381 (Tchalvek house) | 5663 | ...
// WEAPONS table: attributes for items that are weapons
item_id | damage | damage_type | durability | ...
1 | 5 | sharp | 13 | ...
// LIGHTING table: attributes for items that serve as lights
item_id | radius | brightness | duration | ...
1 | 3 meters | 50 | 8 hours | ...
Nesse design, cada item está na tabela Itens, juntamente com os atributos que todos (ou a maioria) dos itens possuem. Cada função adicional que um item pode desempenhar é uma tabela separada.
Se você quiser usá-lo como arma, procure na tabela de Armas. Se estiver lá, é utilizável como arma. Se não estiver lá, não poderá ser usado como arma. A existência do registro informa se é uma arma. E se estiver lá, todos os seus atributos específicos de arma serão armazenados lá. Como esses atributos são armazenados diretamente em vez de em algum formato codificado, você poderá realizar consultas / filtros com eles. (Por exemplo, para a página de métricas do seu jogo, você pode agregar jogadores por tipo de dano da arma e poderá fazer isso com algumas junções e um tipo de dano por grupo.)
Um item pode ter várias funções e existir em mais de uma tabela específica de função (neste exemplo, arma e iluminação).
Se for apenas um valor booleano como "isso é suportável", eu o colocaria na tabela Items. Pode valer a pena armazenar em cache "isto é uma arma" etc. etc. para que você não precise fazer uma pesquisa nas armas e em outras tabelas de funções. No entanto, ele adiciona redundância, portanto, você deve ter cuidado para mantê-lo sincronizado.
A recomendação de Ari de ter uma tabela adicional por tipo também pode ser usada com essa abordagem se alguns dados não variarem por item. Por exemplo, se o dano da arma não variar por item, mas os papéis ainda variarem por item, você pode levar em consideração os atributos da arma compartilhada em uma tabela:
// WEAPONS table: attributes for items that are weapons
item_id | durability | weapon_type
1 | 13 | light_saber
// WEAPONTYPES table: attributes for classes of weapons
weapon_type_id | damage | damage_type
light_saber | 5 | energy
Outra abordagem seria se os papéis desempenhados pelos itens não variarem por item, mas apenas por tipo de item. Nesse caso, você colocaria o item_type na tabela Items e poderá armazenar as propriedades como "é uma arma" e "é suportável" e "é uma luz" em uma tabela ItemTypes. Neste exemplo, também faço os nomes dos itens não variarem por item:
// ITEMS table: attributes per item
item_id | item_type | owner | location
1 | light_saber | 14 (Tchalvek) | 381 (Tchalvek house)
// ITEMTYPES table: attributes shared by all items of a type
item_type | name | sprite_id | is_holdable | is_weapon | is_light
light_saber | Light Saber | 5663 | true | true | true
// WEAPONTYPES table: attributes for item types that are also weapons
item_type | damage | damage_type
light_saber | 5 | energy
É provável que os tipos de item e tipo de arma não sejam alterados durante o jogo; portanto, você pode carregar essas tabelas na memória apenas uma vez e procurar esses atributos em uma tabela de hash em vez de em uma associação ao banco de dados.
Infelizmente, situações como esta são onde os bancos de dados relacionais (como SQL) ficam aquém e os bancos de dados não relacionais (como o MongoDB ) se destacam. Dito isto, não é impossível modelar os dados em um banco de dados relacional e, como parece que sua base de código já é dependente do SQL, eis o modelo com o qual eu iria:
Em vez de criar um item e usar tabela, crie uma tabela e uma tabela de referência "[item] _type" para cada tipo de item.
Aqui estão alguns exemplos:
Essa solução oferece muita flexibilidade e escalabilidade a longo prazo, reduz (se não elimina) o espaço desperdiçado e é auto-documentada (ao contrário de "item_use_data"). Isso envolve um pouco mais de configuração no final do desenvolvedor, mas, na minha opinião, é a solução mais elegante disponível para situações em que você precisa usar um banco de dados relacional para armazenar seus dados. De um modo geral, os bancos de dados não relacionais são muito melhores para o desenvolvimento de jogos, pois são mais flexíveis na maneira como modelam dados, além de terem melhor desempenho do que os bancos de dados baseados em SQL - tornando-os uma opção "ganha-ganha".
Edição 1: corrigido um erro com o campo potion_type_id
Edit 2: Adicionado mais detalhes sobre bancos de dados não relacionais x relacionais para fornecer uma perspectiva adicional
fonte
Primeiro, despeje a abordagem de herança orientada a objetos e use um sistema baseado em componentes .
Depois de fazer isso, o layout do SQL fica muito mais fácil. Você tem uma tabela para cada tipo de componente com um número de identificação compartilhado. Se você quiser o item 17, procure "item 17" em todas as tabelas. Qualquer tabela que tenha uma chave recebe seu componente adicionado.
Sua tabela de itens contém todos os dados necessários para itens em geral (nome, preço de venda, peso, tamanho, qualquer outra coisa que seja compartilhada entre todos os itens.) Sua tabela de armas contém todos os dados apropriados para armas, sua tabela de poções contém todos os dados apropriados para poções, sua tabela de armadura contém todos os dados apropriados para a armadura. Quer um peitoral, é uma entrada no Item e uma entrada na Armadura. Quer um espada que você possa beber, basta colocar uma entrada em cada mesa, e bam, está feito.
Lembre-se de que esse mesmo padrão não é particularmente específico de um item - você pode usá-lo para criaturas, zonas ou qualquer outra coisa que desejar. É surpreendentemente versátil.
fonte
Usar o SQL foi seu grande erro. Não é absolutamente adequado para armazenar dados estáticos de design de jogos.
Se você não pode se afastar do SQL, eu consideraria armazenar itens em um serializado. Ou seja,
Claro, isso é feio e lança todas as "sutilezas" do SQL pela janela, mas você realmente precisa delas? Provavelmente, você lê todos os dados de seus itens no início do jogo de qualquer maneira, e nunca
SELECT
por nada além deitem_id
.fonte
Dependendo de quantas características você provavelmente precisará, você pode usar uma máscara de bits simples para objetos com os diferentes bits correspondentes a diferentes características:
Em seguida, você pode fazer testes de bits simples para verificar se um objeto pode ser usado para uma determinada função.
Portanto, "garrafa de vidro" pode ter um valor de 101111, o que significa que pode ser segurado, pode ser usado como uma arma, ele se quebra facilmente, você pode jogá-lo e conter líquidos.
Qualquer editor que você criar para itens poderá ter um conjunto simples de caixas de seleção para ativar / desativar traços em um objeto.
fonte
Em nosso projeto, temos item_attributes para os diferentes "dados extras" que um item pode ter. É apresentado algo como isto:
Em seguida, temos uma tabela de atributos com a seguinte aparência:
Ari Patrick está certo, no entanto, os bancos de dados relacionais não são feitos para isso. A desvantagem é que temos alguns procedimentos bastante complexos para gerar novos itens (feitos por meio de uma ferramenta externa - que eu recomendo, não tente adicioná-los manualmente, você só se confundirá)
A outra opção que você tem é o uso de uma linguagem de script para criar os modelos de itens. Em seguida, você pode facilmente analisá-los e usá-los para criar novos itens. É claro que você ainda precisa salvar os dados do item no banco de dados, mas nesse momento não precisa se preocupar com detalhes da criação de novos itens, basta copiar e copiar o arquivo de script antigo, alterar algumas informações e é bom ir.
Honestamente, se refizéssemos como criamos novos itens estáticos, provavelmente adotaríamos uma abordagem muito mais simples usando modelos de itens de script.
fonte
Esse é seu relacionamento típico de muitos para muitos, nada esotérico demais para qualquer banco de dados relacional capaz. Você tem muitas características para qualquer objeto, e qualquer característica pode ser usada por um ou mais tipos diferentes de objetos. Modele três relações (tabelas), sendo uma delas uma relação de associação, e pronto. A indexação adequada garantirá leituras rápidas de dados.
Coloque em camadas um ORM no seu código e você deverá ter muito poucos problemas entre o DB e o middleware. Muitos ORMs também são capazes de gerar automaticamente as próprias classes, tornando-se ainda mais "invisíveis".
Quanto aos bancos de dados NoSQL, não há razão para que você não possa fazê-lo. Atualmente, está na moda torcer por essa tecnologia em trapos e blogs comerciais, mas eles vêm com uma série de problemas próprios: plataformas relativamente imaturas, ótimas para leituras simples e com pouca frequência alteradas (como um twits um-para-muitos em um perfil), mas ruim para leituras ou atualizações dinâmicas complexas, cadeia de ferramentas de suporte ruim, dados redundantes e os problemas de integridade que o acompanham, etc. No entanto, eles oferecem o apelo a uma melhor escalabilidade porque evitam recursos transacionais e sua sobrecarga de graus variados e modelos mais fáceis de distribuição / replicação .
O hobgoblin usual de bancos de dados relacionais versus bancos de dados NoSQL é o desempenho. RDMBSes diferentes têm diferentes graus de sobrecarga envolvidos, o que os torna menos preferidos para escalar para os níveis do Facebook ou Twitter. No entanto, é muito improvável que você enfrente esses problemas. Mesmo assim, sistemas simples de servidores baseados em SSD podem tornar o debate sobre desempenho inútil.
Sejamos claros: a maioria dos bancos de dados NoSQL são tabelas de hash fundamentalmente distribuídas e limitarão você da mesma forma que uma tabela de hash no seu código, ie. nem todos os dados se encaixam bem nesse modelo. O Modelo Relacional é muito mais poderoso para modelar relações entre dados. (O fator de confusão é que a maioria dos RDBMSs são sistemas legados que estão mal ajustados para as demandas dos populares 0,0001% da web, como Facebook et al.)
fonte
É aqui que acho que os mapeadores OR mais modernos são realmente úteis, como o Entity Framework 4 e seu recurso de primeiro código (CTP) . Você acabou de escrever as classes e seus relacionamentos em código regular e, mesmo sem precisar decorá-las ou executar manualmente quaisquer ferramentas, a EF gerará o backing store no formato SQL para você, com tabelas de links necessárias e tudo ... isso realmente desencadeia a criatividade. ^^
fonte
Aqui está o que eu estou considerando agora:
Como toda "característica" requer essencialmente alterações no código, decidi manter as características (e quaisquer dados padrão que elas requeiram) no próprio código (pelo menos por enquanto).
Por exemplo
$traits = array('holdable'=>1, 'weapon'=>1, 'sword'=>array('min_dam'=>1, 'max_dam'=>500));
Os itens obtêm um campo "trait_data" no banco de dados que usará a
json_encode()
função para ser armazenado no formato JSON.O plano é que os itens herdem todas as estatísticas padrão de seus traços pai e tenham apenas traços de substituição que especifiquem diferenças do que os traços definem como padrão.
A desvantagem do campo traits é que, embora eu possa editar a parte do json manualmente ... ... não será realmente fácil ou seguro fazê-lo com os dados que estão no banco de dados.
fonte
Isso é um pouco hacky e não garanto que seja um bom design de banco de dados; Minhas aulas de DB ocorreram há um tempo atrás e elas não vêm à mente rapidamente. Mas se for garantido que os itens tenham, digamos, menos de 10 itens, você pode atribuir a cada item um campo attribute1, e attribute2, e assim por diante, até attribute10. Isso eliminaria sua necessidade da tabela de atributos de itens muitos para muitos, com o custo de limitar o número de atributos.
Olhando para trás, tenho certeza de que é um design terrível, mas significa que você pode usar um banco de dados relacional confortável e não precisar entrar em território desconhecido. Cabe a você decidir se a troca vale a pena.
fonte
Nevermind deu uma boa resposta, mas eu me pergunto, você precisa de um banco de dados para isso? Armazenar todos os dados em um arquivo simples e carregá-los no programa no lançamento parece ser a maneira mais inteligente de fazer isso.
Edit:
Certo, em um ambiente controlado por solicitação sem estado, é melhor manter os dados em um banco de dados. Escreva seus dados em um arquivo e escreva um pedaço de código para transformá-lo em um banco de dados do tipo sugerido por Nevermind.
Como alternativa, se o número de objetos não for muito grande, declarar literalmente o lote em um arquivo de código pode ser o método mais rápido.
fonte