Estou trabalhando em um sistema de componentes de entidade em C ++ que espero seguir o estilo de Artemis (http://piemaster.net/2011/07/entity-component-artemis/) em que os componentes são principalmente sacos de dados e é o Sistemas que contêm a lógica. Espero tirar proveito da centralização de dados dessa abordagem e criar algumas boas ferramentas de conteúdo.
No entanto, um problema é como pegar uma sequência identificadora ou GUID de um arquivo de dados e usá-la para construir um componente para uma Entidade. Obviamente, eu poderia ter apenas uma grande função de análise:
Component* ParseComponentType(const std::string &typeName)
{
if (typeName == "RenderComponent") {
return new RenderComponent();
}
else if (typeName == "TransformComponent") {
return new TransformComponent();
}
else {
return NULL:
}
}
Mas isso é realmente feio. Pretendo adicionar e modificar componentes com freqüência, e espero criar algum tipo de ScriptedComponentComponent, para que você possa implementar um componente e sistema em Lua para fins de prototipagem. Eu gostaria de poder escrever uma classe herdada de alguma BaseComponent
classe, talvez lançar algumas macros para fazer tudo funcionar e depois ter a classe disponível para instanciação em tempo de execução.
Em C # e Java, isso seria bem simples, pois você obtém boas APIs de reflexão para procurar classes e construtores. Mas, estou fazendo isso em C ++ porque quero aumentar minha proficiência nesse idioma.
Então, como isso é feito em C ++? Eu li sobre a ativação do RTTI, mas parece que a maioria das pessoas tem receio disso, especialmente em uma situação em que eu só preciso dele para um subconjunto de tipos de objetos. Se eu preciso de um sistema RTTI personalizado, onde posso começar a aprender a escrever um?
fonte
Respostas:
Um comentário:
A implementação da Artemis é interessante. Eu vim com uma solução semelhante, exceto que chamei meus componentes de "Atributos" e "Comportamentos". Essa abordagem de separar tipos de componentes funcionou muito bem para mim.
Em relação à solução:
O código é fácil de usar, mas pode ser difícil seguir a implementação se você não tiver experiência com C ++. Tão...
A interface desejada
O que fiz foi ter um repositório central de todos os componentes. Cada tipo de componente é mapeado para uma determinada sequência (que representa o nome do componente). É assim que você usa o sistema:
A implementação
A implementação não é tão ruim, mas ainda é bastante complexa; requer algum conhecimento de modelos e ponteiros de função.
Nota: Joe Wreschnig fez alguns pontos positivos nos comentários, principalmente sobre como minha implementação anterior fez muitas suposições sobre o quão bom o compilador é na otimização de código; a questão não foi prejudicial, imo, mas também me incomodou. Notei também que a
COMPONENT_REGISTER
macro anterior não funcionava com modelos.Alterei o código e agora todos esses problemas devem ser corrigidos. A macro trabalha com modelos e os problemas que Joe levantou foram abordados: agora é muito mais fácil para os compiladores otimizarem códigos desnecessários.
component / component.h
component / detail.h
component / component.cpp
Estendendo com Lua
Devo observar que, com um pouco de trabalho (não é muito difícil), isso pode ser usado para trabalhar perfeitamente com componentes definidos em C ++ ou Lua, sem ter que pensar nisso.
fonte
shared_ptr
, mas seu conselho ainda é bom.Parece que o que você quer é uma fábrica.
http://en.wikipedia.org/wiki/Factory_method_pattern
O que você pode fazer é ter seus vários componentes registrados na fábrica a que nome eles correspondem e, em seguida, você tem algum mapa do identificador de string para a assinatura do método construtor para gerar seus componentes.
fonte
Component
classes, chamandoComponentSubclass::RegisterWithFactory()
, certo? Existe uma maneira de configurar isso de forma mais dinâmica e automagicamente? O fluxo de trabalho que procuro é 1. Escreva uma classe, observando apenas o cabeçalho correspondente e o arquivo cpp 2. Re-compile o jogo 3. Inicie o editor de nível e a nova classe de componente estará disponível para uso.Trabalhei com o design de Paul Manta a partir da resposta escolhida por um tempo e, finalmente, cheguei a esta implementação de fábrica mais genérica e concisa abaixo, que estou disposto a compartilhar para qualquer pessoa que venha a essa pergunta no futuro. Neste exemplo, todo objeto de fábrica deriva da
Object
classe base:A classe estática Factory é a seguinte:
A macro para registrar um subtipo de
Object
é a seguinte:Agora o uso é o seguinte:
A capacidade de muitos IDs de string por subtipo foi útil no meu aplicativo, mas a restrição a um único ID por subtipo seria bastante direta.
Espero que isso tenha sido útil!
fonte
Com base na resposta de @TimStraubinger , construí uma classe de fábrica usando os padrões C ++ 14 que podem armazenar membros derivados com um número arbitrário de argumentos . Meu exemplo, ao contrário do Tim, leva apenas um nome / chave por função. Como o de Tim, toda classe armazenada é derivada de uma classe Base , a minha sendo chamada Base .
Base.h
EX_Factory.h
main.cpp
Saída
Espero que isso ajude as pessoas que precisam usar um design de fábrica que não exija um construtor de identidade para funcionar. Foi divertido projetar, por isso espero que ajude as pessoas que precisam de mais flexibilidade em seus projetos de fábrica .
fonte