Estoy trabajando en un sistema de componentes de entidad en C ++ que espero seguir el estilo de Artemis (http://piemaster.net/2011/07/entity-component-artemis/) en que los componentes son principalmente bolsas de datos y es el Sistemas que contienen la lógica. Espero aprovechar el enfoque centrado en los datos de este enfoque y construir algunas buenas herramientas de contenido.
Sin embargo, un obstáculo con el que me encuentro es cómo tomar una cadena de identificación o GUID de un archivo de datos y usarlo para construir un componente para una Entidad. Obviamente, podría tener una gran función de análisis:
Component* ParseComponentType(const std::string &typeName)
{
if (typeName == "RenderComponent") {
return new RenderComponent();
}
else if (typeName == "TransformComponent") {
return new TransformComponent();
}
else {
return NULL:
}
}
Pero eso es realmente feo. Tengo la intención de agregar y modificar componentes con frecuencia, y espero construir algún tipo de ScriptedComponentComponent, de modo que pueda implementar un componente y sistema en Lua con el propósito de crear prototipos. Me gustaría poder escribir una clase heredada de alguna BaseComponent
clase, tal vez agregar un par de macros para que todo funcione y luego tener la clase disponible para instanciación en tiempo de ejecución.
En C # y Java, esto sería bastante sencillo, ya que obtienes buenas API de reflexión para buscar clases y constructores. Pero, estoy haciendo esto en C ++ porque quiero aumentar mi dominio en ese lenguaje.
Entonces, ¿cómo se logra esto en C ++? He leído sobre cómo habilitar RTTI, pero parece que la mayoría de las personas desconfían de eso, especialmente en una situación en la que solo lo necesito para un subconjunto de tipos de objetos. Si un sistema RTTI personalizado es lo que necesito allí, ¿dónde puedo ir para comenzar a aprender a escribir uno?
fuente
Respuestas:
Un comentario:
La implementación de Artemis es interesante. Se me ocurrió una solución similar, excepto que llamé a mis componentes "Atributos" y "Comportamientos". Este enfoque de separar tipos de componentes me ha funcionado muy bien.
En cuanto a la solución:
el código es fácil de usar, pero la implementación puede ser difícil de seguir si no tiene experiencia con C ++. Asi que...
La interfaz deseada
Lo que hice fue tener un repositorio central de todos los componentes. Cada tipo de componente se asigna a una determinada cadena (que representa el nombre del componente). Así es como usa el sistema:
La implementación
La implementación no es tan mala, pero sigue siendo bastante compleja; requiere algunos conocimientos de plantillas y punteros de función.
Nota: Joe Wreschnig ha hecho algunos buenos comentarios en los comentarios, principalmente sobre cómo mi implementación anterior hizo demasiados supuestos sobre qué tan bueno es el compilador para optimizar el código; El problema no fue perjudicial, en mi opinión, pero también me molestó. También noté que la
COMPONENT_REGISTER
macro anterior no funcionaba con plantillas.He cambiado el código y ahora todos esos problemas deberían solucionarse. La macro funciona con plantillas y se han abordado los problemas que planteó Joe: ahora es mucho más fácil para los compiladores optimizar el código innecesario.
componente / componente.h
componente / detalle.h
componente / componente.cpp
Extendiéndose con Lua
Debo señalar que con un poco de trabajo (no es muy difícil), esto se puede usar para trabajar sin problemas con componentes definidos en C ++ o Lua, sin tener que pensar en ello.
fuente
shared_ptr
, pero su consejo sigue siendo bueno.Parece que lo que quieres es una fábrica.
http://en.wikipedia.org/wiki/Factory_method_pattern
Lo que puede hacer es hacer que sus diversos componentes se registren en la fábrica a qué nombre corresponden, y luego tiene un mapa del identificador de cadena a la firma del método del constructor para generar sus componentes.
fuente
Component
clases, llamandoComponentSubclass::RegisterWithFactory()
, ¿verdad? ¿Hay alguna forma de configurar esto de manera más dinámica y automática? El flujo de trabajo que estoy buscando es 1. Escriba una clase, mirando solo el encabezado correspondiente y el archivo cpp 2. Vuelva a compilar el juego 3. El editor de nivel de inicio y la nueva clase de componente están disponibles para su uso.Trabajé con el diseño de Paul Manta a partir de la respuesta elegida por un tiempo y finalmente llegué a esta implementación de fábrica más genérica y concisa a continuación que estoy dispuesto a compartir para cualquier persona que venga a esta pregunta en el futuro. En este ejemplo, cada objeto de fábrica deriva de la
Object
clase base:La clase estática Factory es la siguiente:
La macro para registrar un subtipo de
Object
es la siguiente:Ahora el uso es el siguiente:
La capacidad de muchas ID de cadena por subtipo fue útil en mi aplicación, pero la restricción a una sola identificación por subtipo sería bastante sencilla.
¡Espero que esto haya sido útil!
fuente
Partiendo de la respuesta de @TimStraubinger , construí una clase de fábrica utilizando estándares C ++ 14 que pueden almacenar miembros derivados con un número arbitrario de argumentos . Mi ejemplo, a diferencia de Tim, solo toma un nombre / tecla por función. Al igual que Tim, cada clase que se almacena se deriva de una clase Base , la mía se llama Base .
Base.h
EX_Factory.h
main.cpp
Salida
Espero que esto ayude a las personas que necesitan usar un diseño de Fábrica que no requiere un constructor de identidad para funcionar. Fue divertido diseñar, así que espero que ayude a las personas que necesitan más flexibilidad en sus diseños de fábrica .
fuente