Actualmente estoy tratando de implementar un sistema de entidad basado en componentes, donde una entidad es básicamente una ID y algunos métodos auxiliares que unen un conjunto de componentes para formar un objeto de juego. Algunos objetivos de eso incluyen:
- Los componentes solo contienen estado (por ejemplo, posición, estado, recuento de munición) => la lógica entra en "sistemas", que procesan estos componentes y su estado (por ejemplo, PhysicsSystem, RenderSystem, etc.)
- Quiero implementar componentes y sistemas tanto en C # puro como a través de scripting (Lua). Básicamente quiero poder definir componentes y sistemas completamente nuevos directamente en Lua sin tener que recompilar mi fuente C #.
Ahora estoy buscando ideas sobre cómo manejar esto de manera eficiente y consistente, por lo que no necesito usar una sintaxis diferente para acceder a componentes de C # o componentes de Lua, por ejemplo.
Mi enfoque actual sería implementar componentes de C # usando propiedades públicas regulares, probablemente decoradas con algunos atributos que le informan al editor sobre valores predeterminados y otras cosas. Entonces tendría una clase C # "ScriptComponent", que simplemente envuelve una tabla Lua internamente con esta tabla creada por un script y que contiene todo el estado de ese tipo de componente en particular. Realmente no quiero acceder a ese estado desde el lado de C #, ya que no sabría en el momento de la compilación qué componentes de script con qué propiedades estarían disponibles para mí. Aún así, el editor tendrá que acceder a eso, pero una interfaz simple como la siguiente debería ser suficiente:
public ScriptComponent : IComponent
{
public T GetProperty<T>(string propertyName) {..}
public void SetProperty<T>(string propertyName, T value) {..}
}
Esto simplemente accedería a la tabla Lua y establecería y recuperaría esas propiedades de Lua y también podría incluirse fácilmente en los componentes puros de C # (pero use las propiedades normales de C # entonces, mediante reflexión o algo así). Esto solo se usaría en el editor, no por el código de juego regular, por lo que el rendimiento no es tan importante aquí. Haría necesario generar o escribir a mano algunas descripciones de componentes que documenten qué propiedades ofrece un determinado tipo de componente, pero eso no sería un gran problema y podría automatizarse lo suficiente.
Sin embargo, ¿cómo acceder a los componentes desde el lado de Lua? Si hago una llamada a algo así getComponentsFromEntity(ENTITY_ID)
, probablemente obtendré un montón de componentes nativos de C #, incluido "ScriptComponent", como datos de usuario. Acceder a los valores de la tabla Lua envuelta resultaría en que llame al GetProperty<T>(..)
método en lugar de acceder a las propiedades directamente, como con los otros componentes de C #.
Tal vez escriba un getComponentsFromEntity()
método especial solo para ser llamado desde Lua, que devuelve todos los componentes nativos de C # como datos de usuario, a excepción de "ScriptComponent", donde devolverá la tabla envuelta. Pero habrá otros métodos relacionados con los componentes y realmente no quiero duplicar todos estos métodos tanto para ser llamados desde el código C # o desde el script Lua.
El objetivo final sería manejar todos los tipos de componentes de la misma manera, sin una sintaxis de caso especial que diferencie entre los componentes nativos y los componentes Lua, especialmente desde el lado Lua. Por ejemplo, me gustaría poder escribir un script Lua así:
entity = getEntity(1);
nativeComponent = getComponent(entity, "SomeNativeComponent")
scriptComponent = getComponent(entity, "SomeScriptComponent")
nativeComponent.NativeProperty = 5
scriptComponent.ScriptedProperty = 3
El script no debería importar qué tipo de componente tiene realmente y me gustaría usar los mismos métodos que usaría desde el lado C # para recuperar, agregar o eliminar componentes.
¿Quizás hay algunas implementaciones de muestra de integración de scripts con sistemas de entidades como ese?
Respuestas:
Un corolario de la respuesta de FXIII es que se debe diseñar todo (que sería modificable) en el lenguaje de script primera (o al menos una porción muy decente de la misma) para asegurar que su lógica de integración en realidad disposiciones para modding. Cuando esté seguro de que su integración de scripts es lo suficientemente versátil, vuelva a escribir los bits necesarios en C #.
Personalmente odio la
dynamic
función en C # 4.0 (soy un adicto al lenguaje estático), pero este es sin duda un escenario en el que debería usarse y es donde realmente brilla. Sus componentes de C # serían simplemente objetos fuertes / expansivos de comportamiento .Sus componentes Lua serían completamente dinámicos (el mismo artículo anterior debería darle una idea de cómo implementar esto). Por ejemplo:
Ahora, como lo está usando
dynamic
, puede usar objetos Lua como si fueran clases reales en C #.fuente
Prefiero hacer lo contrario: escribir todo usando un lenguaje dinámico y luego rastrear los cuellos de botella (si los hay). Una vez que encuentre uno, puede volver a implementar selectivamente las funcionalidades involucradas usando un C # o (más bien) C / C ++.
Te digo esto principalmente porque lo que estás tratando de hacer es limitar las flexibilidades de tu sistema en la parte C #; A medida que el sistema se vuelve complejo, su "motor" de C # comenzará a parecerse a un intérprete dinámico, probablemente no el mejor. La pregunta es: ¿estás dispuesto a invertir la mayor parte de tu tiempo en escribir un motor genérico C # o extender tu juego?
Si su objetivo es el segundo, como creo, probablemente usará su tiempo mejor enfocándose en la mecánica de su juego, permitiendo que un intérprete experimentado ejecute el código dinámico.
fuente