Como lidar com o inventário de maneira orientada a objetos?

7

Estou tentando pensar na melhor maneira de lidar com o inventário de jogadores, seguindo uma abordagem orientada a objetos.

Por exemplo, espada e machado são duas classes diferentes, ambas herdadas da arma. Arma e poção também são classes diferentes, ambas herdadas do item.

Idealmente, o inventário seria implementado como um contêiner de itens, mas isso não pode ser feito por vários motivos. Portanto, ele deve ser um contêiner de ponteiros de itens (inteligentes). Como não quero escrever meu código com chamadas para new e delete, o agruparei em uma classe ItemWrapper que news / delete no ctor / dtor.

Portanto, o código será algo como isto

class Sword;
class ItemWrapper;

//Prepare the long sword.
const Sword longSword(...params...);

//Declare the inv
vector<ItemWrapper> inv;

 //Add it to inventory
inv.push_back(longSword);  //compiles if ItemWraper(const Sword&) is defined.

Essa abordagem foi a melhor que pude apresentar em termos de clareza de código e facilidade de redação. O problema é que o ItemWrapper precisará de um construtor para cada classe de folha que herda de Item (o que é muito).

Esta é minha primeira tentativa, então gostaria de saber: estou indo no caminho certo? ou se existe uma maneira melhor de fazer isso (talvez uma que evite tantos construtores)?

Malabarba
fonte
Estou realmente interessado em como isso é melhor implementado.
Tili

Respostas:

9

Há algo que você está fazendo agora que pode se arrepender mais tarde, mesmo que pareça lógico.

Você provavelmente descobrirá que é difícil gerenciar cada tipo de item de sua própria classe depois de ter mais de doze deles.

Meus próprios sistemas de gerenciamento de inventário não têm tipos diferentes para itens. Em vez disso, eu uso uma classe "Descritor", que possui um conjunto de propriedades (pares Nome-Valor, armazenados em um contêiner associativo [em C ++, você provavelmente usaria std :: map])

O getter de propriedade é uma função de modelo que lança a propriedade do mapa no tipo apropriado.

A maneira como eu diferencio os tipos de itens é ter uma propriedade ItemType, geralmente algum tipo de enumeração com entradas como WEAPON, ARMOR, etc. Outras propriedades contêm intervalos de danos, referências a imagens a serem usadas para renderizá-lo, descrição do nome etc.

Agora, esses descritores não são para instâncias de itens. Eles descrevem o tipo de item como um todo. As instâncias de item contêm algum tipo de referência ao descritor, bem como um mapa semelhante de propriedades para os valores necessários para uma instância de item (como durabilidade individual, por exemplo). Dessa maneira, evita-se repetir a mesma informação repetidamente. Afinal, uma espada é quase a mesma que outra, apenas pode estar mais perto de se desgastar.

Quanto a evitar chamadas de construtores, recomendo um método de fábrica.

PlayDeezGames
fonte
4
Eu acho que você pode estar um pouco mais longe do que eu. Penso que não há razão para criar uma classe VorpalBladeOfPower e uma classe PunyIronHandaxe; mas ter tipos separados para armas, armaduras etc. pode poupar sua dor. Deixe o compilador impedir que um jogador use duas poções de vida e um elmo.
dhasenan
Entendo o seu ponto de consolidar itens em uma classe. Eu realmente não entendo por que armazenar as propriedades dentro de um contêiner associativo. Se eu entendi direito (e digo se entendi errado), em vez de uma float weightvariável dentro da Itemclasse, você mantém o número em um contêiner (talvez associado ao rótulo "peso"). Parece que você recebe um pequeno impacto no desempenho (leitura do contêiner) e escreve mais código, em troca de um pouco de memória.
Malabarba 6/03/12
Estou assumindo que texturas e sons já são gerenciados por memória, é claro (como sempre deveriam ser). Nesse caso, eu acredito que a vantagem de memória de gestão de algumas floats, enums e ints por exemplo Item (mesmo que há centenas de casos item) não seria muito significativo.
Malabarba 6/03/12
Seria um impacto no desempenho se você tivesse milhares de propriedades em cada descritor. Geralmente, existem menos de 20 propriedades na maioria delas.
PlayDeezGames
Desculpe, o "desempenho atingido" não era o foco, eu sei que é pequeno ou até inexistente. Meu argumento é que parece que você está escrevendo mais código e economizando 1 ou 2 MB de memória (assumindo centenas de itens).
Malabarba
7

Por exemplo, espada e machado são duas classes diferentes, ambas herdadas da arma. Arma e poção também são classes diferentes, ambas herdadas do item.

Eu não faria as coisas dessa maneira; uma espada e um machado diferem principalmente em seus dados, não em seus comportamentos fundamentais como armas. Da mesma forma com os itens. Eu evitaria esse uso excessivo de herança, começando restringindo-se a justos Weapone Item(você pode até dobrá-los em uma única classe, mas essa é uma discussão diferente). Se nada mais, isso aliviará a explosão de construtores em que você está vendo ItemWrapper.

Portanto, ele deve ser um contêiner de ponteiros de itens (inteligentes). Como não quero escrever meu código com chamadas para new e delete, o agruparei em uma classe ItemWrapper que news / delete no ctor / dtor.

Considere os indicadores inteligentes Boost, em vez de usar os seus. Eles são bastante isolados do restante do Boost (para que você não tenha muitas dependências para usar se, por algum motivo, não quiser tirar proveito do restante das coisas legais do Boost), e muitos eles foram adotados para fazer parte da nova biblioteca padrão no C ++ 11, portanto, você não deve ter problemas para atualizar.

Esta é minha primeira tentativa, então gostaria de saber: estou indo no caminho certo? ou se existe uma maneira melhor de fazer isso (talvez uma que evite tantos construtores)?

Como acima, eu me esforçaria para evitar a herança para isso e consideraria o uso de soluções estabelecidas de terceiros em vez de criar as suas próprias. Somente essas etapas devem remover grande parte da complexidade da manutenção que você está descobrindo (ou descobrirá em breve).

Dito isso, outra abordagem é encará-la desta maneira: ItemWrapperé um band-aid para um problema de detalhes de implementação. Ele próprio não representa um "objeto" real em nenhum sentido típico. Então pense no que poderia ser uma analogia melhor que também resolve seu problema de ter um lugar para colocar o automático newe as deletechamadas.

A maioria dos inventários permite que (alguns) itens sejam empilhados, não é?

A "capacidade de empilhamento" de um item é uma propriedade desse item, mas o item em si não deve ser responsável por manter a pilha. Se você tem uma classe representando ItemStackmétodos with para consultar o tamanho da pilha, dividir a pilha, etc., pode colocar seu gerenciamento da vida útil do ponteiro do item (que, devo observar, pode se tornar um pouco mais complicado com as pilhas , fornecendo assim outro argumento para usar algo como Boost) na classe stack junto com a implementação do restante de seu comportamento. Os itens que não podem ser empilhados são representados por uma pilha que sempre contém um único item, e seu próprio inventário ainda lida com instâncias homogêneas de ItemStack.


fonte
+1 Combinando Weapone Itemseria o último prego no caixão paraItemWrapper . Weaponpoderia estender Itemou ambos poderiam estender uma classe base comum. Então você poderia apenas ter algo parecidovector<shared_ptr<Item> > inv;
Gyan aka Gary Buyn
Usar um ItemStack parece uma boa ideia.
Malabarba 6/03/12