Atualmente, estou tentando implementar um sistema de entidades baseado em componentes, em que uma entidade é basicamente apenas um ID e alguns métodos auxiliares que unem vários componentes para formar um objeto de jogo. Alguns objetivos incluem:
- Os componentes contêm apenas estado (por exemplo, posição, saúde, quantidade de munição) => a lógica entra em "sistemas", que processam esses componentes e seu estado (por exemplo, PhysicsSystem, RenderSystem, etc.)
- Quero implementar componentes e sistemas em C # puro e por meio de scripts (Lua). Basicamente, quero poder definir componentes e sistemas completamente novos diretamente em Lua sem precisar recompilar minha fonte de C #.
Agora, estou procurando idéias sobre como lidar com isso de maneira eficiente e consistente, portanto, não preciso usar sintaxe diferente para acessar componentes C # ou componentes Lua, por exemplo.
Minha abordagem atual seria implementar componentes C # usando propriedades públicas regulares, provavelmente decoradas com alguns atributos que informam o editor sobre valores e coisas padrão. Então eu teria uma classe C # "ScriptComponent", que apenas agrupa uma tabela Lua internamente, com essa tabela sendo criada por um script e mantendo todo o estado desse tipo de componente específico. Eu realmente não quero acessar muito esse estado do lado do C #, pois não saberia em tempo de compilação quais ScriptComponents com quais propriedades estariam disponíveis para mim. Ainda assim, o editor precisará acessar isso, mas uma interface simples como a seguinte deve ser suficiente:
public ScriptComponent : IComponent
{
public T GetProperty<T>(string propertyName) {..}
public void SetProperty<T>(string propertyName, T value) {..}
}
Isso simplesmente acessaria a tabela Lua e definiria e recuperaria essas propriedades de Lua, além de poder ser facilmente incluído nos componentes C # puros (mas use as propriedades C # regulares, por meio de reflexão ou algo assim). Isso seria usado apenas no editor, não pelo código normal do jogo; portanto, o desempenho não é tão importante aqui. Seria necessário gerar ou escrever à mão algumas descrições de componentes, documentando quais propriedades um determinado tipo de componente realmente oferece, mas isso não seria um problema enorme e poderia ser suficientemente automatizado.
No entanto, como acessar componentes do lado da Lua? Se eu telefonar para algo como getComponentsFromEntity(ENTITY_ID)
provavelmente vou obter vários componentes nativos do C #, incluindo "ScriptComponent", como dados do usuário. Acessar os valores da tabela embrulhada Lua resultaria na chamada do GetProperty<T>(..)
método, em vez de acessar as propriedades diretamente, como nos outros componentes do C #.
Talvez escreva um getComponentsFromEntity()
método especial apenas para ser chamado de Lua, que retorna todos os componentes C # nativos como dados do usuário, exceto "ScriptComponent", onde retornará a tabela agrupada. Mas haverá outros métodos relacionados a componentes e eu realmente não quero duplicar todos esses métodos para serem chamados do código C # ou do script Lua.
O objetivo final seria lidar com todos os tipos de componentes da mesma forma, sem nenhuma sintaxe especial de caso diferenciando componentes nativos e componentes Lua - especialmente do lado Lua. Por exemplo, eu gostaria de poder escrever um script Lua assim:
entity = getEntity(1);
nativeComponent = getComponent(entity, "SomeNativeComponent")
scriptComponent = getComponent(entity, "SomeScriptComponent")
nativeComponent.NativeProperty = 5
scriptComponent.ScriptedProperty = 3
O script não deve se importar com o tipo de componente que ele realmente possui e eu gostaria de usar os mesmos métodos que usaria no lado do C # para recuperar, adicionar ou remover componentes.
Talvez haja algumas implementações de exemplo de integração de scripts com sistemas de entidades como esse?
Respostas:
Um corolário da resposta do FxIII é que você deve projetar tudo (que seria modificável) na linguagem de script primeiro (ou pelo menos uma parte bastante decente) para garantir que sua lógica de integração realmente preveja a modificação. Quando você tiver certeza de que sua integração de script é versátil o suficiente, reescreva os bits necessários em C #.
Eu pessoalmente odeio o
dynamic
recurso no C # 4.0 (eu sou um viciado em linguagem estática) - mas este é sem dúvida um cenário em que deve ser usado e é onde realmente brilha. Seus componentes C # seriam simplesmente objetos fortes / expando comportamentais .Seus componentes Lua seriam completamente dinâmicos (o mesmo artigo acima deve fornecer uma idéia de como implementar isso). Por exemplo:
Agora, como você está usando,
dynamic
você pode usar objetos Lua como se fossem classes reais em C #.fonte
Prefiro fazer o oposto: escreva tudo usando uma linguagem dinâmica e, em seguida, localize as garrafas (se houver). Depois de encontrar um, você pode reimplementar seletivamente as funcionalidades envolvidas usando um C # ou (melhor) C / C ++.
Digo isso principalmente porque o que você está tentando fazer é confinar as flexibilidades do seu sistema na parte C #; À medida que o sistema se torna complexo, seu "mecanismo" de C # começará a parecer um intérprete dinâmico, provavelmente não o melhor. A questão é: você está disposto a investir a maior parte do seu tempo na criação de um mecanismo C # genérico ou a ampliar seu jogo?
Se o seu alvo for o segundo, como eu acredito, você provavelmente usará seu tempo melhor se concentrando na sua mecânica de jogo, permitindo que um intérprete experiente execute o código dinâmico.
fonte