Nota: estou programando isso em Javascript, mas deve ser independente da linguagem na maior parte.
Estou pensando em converter meu mecanismo para um baseado em ECS.
Eu recebo a ideia básica ( nota: isso está errado, veja minha resposta ):
Entidades são objetos de jogo.
Componentes são bits de funcionalidade ( reactToInput()
) ou state ( position
) que podem ser "colados" às entidades.
Os sistemas têm uma lista de entidades que gerenciam e atualizam.
Mas não tenho certeza de obter a implementação e alguns detalhes ...
Pergunta: um sistema pode operar em diferentes tipos de entidades? Normalmente, dou o exemplo de uma classe chamada Scene
no meu mecanismo, e isso também servirá a esse propósito agora. Uma cena é um contêiner de todos os objetos que podem ser renderizados, atualizados, afetam a renderização (luzes) e, talvez, no futuro, até 2DSoundEmitter
objetos. Ele possui uma interface de alto nível, para que o usuário não precise se preocupar com o tipo de objeto que está scene.add()
inserindo e todo esse tipo de coisa.
Eu percebo que o Scene
poderia ser um sistema. Ele pega entidades, as armazena e, em seguida, pode chamar seus métodos de atualização, e talvez até fazer algumas alterações de estado. Mas há um problema: como descrevi acima, eles Scene
podem ser alimentados com diferentes tipos de objetos! O que devo fazer em, digamos, uma situação em que uma cena tenha objetos renderizáveis ("desenháveis") e luzes nela? Devo fazê-lo verificar entidades antes de interagir? Ou devo resolvê-lo em um nível ainda mais baixo: crie um LightSource
componente que possa ser adicionado a qualquer objeto, e a luz seria apenas uma entidade com LightSource
e Position
componentes. Isso é aceitável?
Além disso, é uma boa prática ainda usar herança convencional e classes tradicionais? Por exemplo, eu simplesmente não consigo descobrir o que seria meu Renderer
! Não é um sistema, pois sua única função é capturar uma câmera e uma cena, renderizar tudo e aplicar efeitos (como sombras). Ele também gerencia o contexto, a largura e a altura do jogo, faz traduções ... Mas ainda não é um sistema!
Editar: você poderia vincular algum recurso encontrado no ECS? Estou tendo problemas para encontrar bons.
Respostas:
Deixe-me ver se, tentando entender como um desenvolvedor de JS da Web / UI, posso ajudar. Além disso, não exagere no agnosticismo da linguagem. Vale a pena estudar muitos padrões estabelecidos em outros idiomas, mas podem ser aplicados de maneira muito diferente no JS devido à sua flexibilidade ou realmente não são necessários devido à natureza maleável do idioma. Você pode aproveitar algumas oportunidades se escrever seu código pensando em JS como tendo o mesmo conjunto de limites que uma linguagem mais clássica orientada a OOP.
Primeiro, no fator "não use OOP", lembre-se de que os objetos JavaScript são como playdough em comparação com outros idiomas e você realmente precisa se esforçar para criar um pesadelo de esquema de herança em cascata, já que JS não é classe baseado em composições é muito mais natural. Se você estiver implementando algum sistema bobo de classe ou protótipo em seu JS, considere abandoná-lo. Em JS, usamos fechamentos, protótipos e passamos funções como doces. É nojento, sujo e errado, mas também poderoso, conciso e é assim que gostamos.
As abordagens pesadas da herança são na verdade apontadas como antipadrão nos Padrões de Design e, por uma boa razão, como qualquer pessoa que percorreu mais de 15 níveis de classe ou estruturas semelhantes a classes para tentar descobrir onde diabos a versão falida de um método estava vindo posso lhe dizer.
Não sei por que tantos programadores adoram fazer isso (especialmente os caras do Java que escrevem JavaScript por algum motivo), mas é horrível, ilegível e completamente impossível de manter quando usado em excesso. A herança é boa aqui e ali, mas não é realmente necessária no JS. Nas linguagens em que é um atalho mais atraente, ele deve ser realmente reservado para preocupações de arquitetura mais abstrata do que para esquemas de modelagem mais literais, como auxiliar na implementação de zumbis por meio de uma cadeia de herança que incluiu um BunnyRabbit porque funcionava. Isso não é uma boa reutilização de código. É um pesadelo de manutenção.
Como um mecanismo baseado em Entidade / Componente / Sistema do JS dev, parece-me um sistema / padrão para dissociar preocupações de design e depois compor objetos para implementação em um nível altamente granular. Em outras palavras, brincadeira de criança em uma linguagem como JavaScript. Mas deixe-me ver se estou grocando corretamente primeiro.
Entidade - A coisa específica que você está projetando. Estamos falando mais na direção de nomes próprios (mas não na verdade, é claro). Não é "Cena", mas "IntroAreaLevelOne". IntroAreaLevelOne pode estar dentro de uma caixa sceneEntity de algum tipo, mas estamos focando em algo específico que varia de outras coisas relacionadas. No código, uma entidade é realmente apenas um nome (ou ID) vinculado a um monte de coisas que ele precisa ter implementado ou estabelecido (os componentes) para ser útil.
Componentes - tipos de coisas que uma entidade precisa. Estes são substantivos gerais. Como WalkingAnimation. No WalkingAnimation, podemos ser mais específicos, como "Shambling" (boa opção para zumbis e monstros de plantas) ou "ChickenWalker" (ótimo para tipos de robôs com articulação reversa ed-209ish). Nota: Não tenho certeza de como isso poderia se dissociar da renderização de um modelo 3D como esse - talvez um exemplo de merda, mas eu sou mais um profissional em JS do que um desenvolvedor de jogos experiente. Em JS, eu colocaria o mecanismo de mapeamento na mesma caixa que os componentes. É provável que os componentes por si só sejam leves na lógica e mais um roteiro que diga aos seus sistemas o que implementar se forem necessários (na minha tentativa no ECS, alguns componentes são apenas conjuntos de conjuntos de propriedades). Depois que um componente é estabelecido, é '
Sistemas - A verdadeira carne do programa está aqui. Os sistemas de IA são construídos e vinculados, a renderização é alcançada, as sequências de animações são estabelecidas, etc. Estou copiando e deixando essas informações principalmente para a imaginação, mas no exemplo System.AI pega várias propriedades e cospe uma função que é usado para adicionar manipuladores de eventos ao objeto que acaba sendo usado na implementação. O principal do System.AI é que ele cobre vários tipos de componentes. Você pode resolver todas as coisas da IA com um componente, mas fazer isso é entender mal o objetivo de tornar as coisas mais granulares.
Tenha em mente os objetivos: queremos facilitar a conexão de algum tipo de interface GUI para que os não designers ajustem facilmente diferentes tipos de coisas, maximizando e combinando componentes dentro de um paradigma que faça sentido para eles, e queremos nos afastar. esquemas de código arbitrário populares que são muito mais fáceis de escrever do que modificar ou manter.
Então, em JS, talvez algo parecido com isto. Desenvolvedores de jogos, por favor, me digam se estou errado:
Agora, sempre que precisar de um NPC, você poderá construir com
npcBuilders.<npcName>();
Uma GUI pode conectar-se aos objetos npcEntities e components e permitir que os designers ajustem entidades antigas ou criem novas entidades simplesmente misturando e combinando componentes (embora não exista um mecanismo para componentes não padrão, mas componentes especiais podem ser adicionados rapidamente código, desde que houvesse um componente definido para ele.
fonte
Eu li o Entity Systems nos artigos que pessoas gentis forneceram nos comentários, mas ainda tinha algumas dúvidas, então fiz outra pergunta .
Primeiro, minhas definições estavam erradas. Entidades e componentes são apenas detentores de dados burros, enquanto os sistemas fornecem todas as funcionalidades.
Eu aprendi o suficiente para cobrir a maior parte da minha pergunta aqui, então responderei.
A
Scene
classe que eu estava falando não deveria ser um sistema. No entanto, deve ser um gerente central que possa conter todas as entidades, facilitar mensagens e talvez até gerenciar sistemas. Também pode funcionar como uma espécie de fábrica para entidades, e eu decidi usá-lo assim. Ele pode tomar qualquer tipo de entidade, mas então ele deve alimentar essa entidade para um sistema adequado (o que, por motivos de desempenho, não deve realizar qualquer tipo de verificações, a menos que eles estão bit a bit).Não devo usar OOP durante a implementação de um ES, sugere Adam, mas não encontro motivo para não ter objetos com métodos para entidades e componentes, não apenas para detentores de dados burros.
O
Renderer
pode simplesmente ser implementado como um sistema. Ele manteria uma lista de objetos desenháveis e chamaria odraw()
método do componente de renderização a cada 16ms.fonte
Introdução ao gerenciamento de dependências 101.
Este curso pressupõe que você tenha conhecimentos básicos de injeção de dependência e design de repositório.
A injeção de dependência é apenas uma maneira elegante de os objetos se comunicarem (via mensagens / sinais / delegados / o que for) sem serem acoplados diretamente.
Ele segue a frase: "
new
é cola".Vou demonstrar isso em c #.
Este é um sistema básico para entrada, movimento e renderização. A
Player
classe é a entidade nesse caso e os componentes são as interfaces. APlayer
classe não sabe nada sobre os detalhes internos de como um concretoIRenderSystem
,IMovementSystem
ouIInputSystem
trabalho. No entanto, as interfaces fornecem uma maneira dePlayer
enviar sinais (por exemplo, invocar Draw no IRenderSystem) sem depender de como o resultado final é alcançado.Por exemplo, considere minha implementação do IMovementSystem:
MovementSystem
pode ter suas próprias dependências ePlayer
elas nem se importam. Usando interfaces, uma máquina de estado do jogo pode ser criada:E esse é o começo de um jogo adorável (que também é testável por unidade).
E com algumas adições e algum polimorfismo:
Agora temos um sistema de entidade / componente primitivo baseado em interfaces de agregação e herança solta.
fonte