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)?
Respostas:
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.
fonte
float weight
variável dentro daItem
classe, 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.float
s,enum
s eint
s por exemplo Item (mesmo que há centenas de casos item) não seria muito significativo.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
Weapon
eItem
(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á vendoItemWrapper
.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.
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áticonew
e asdelete
chamadas.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
ItemStack
mé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 deItemStack
.fonte
Weapon
eItem
seria o último prego no caixão paraItemWrapper
.Weapon
poderia estenderItem
ou ambos poderiam estender uma classe base comum. Então você poderia apenas ter algo parecidovector<shared_ptr<Item> > inv;