¿Cómo evitar "Blob-Systems" en un sistema de componentes de entidad?

10

Actualmente estoy enfrentando el siguiente problema:

Estoy tratando de escribir un clon pong utilizando un sistema de componentes de entidad (ECS). Escribí el "marco" todo por mí mismo. Entonces hay una clase que administra las entidades con todos los componentes. Luego están las clases de componentes en sí. Y por último, están mis sistemas que solo obtienen todas las entidades que tienen componentes que el sistema necesita.

Entonces, por ejemplo, mi sistema de movimiento busca todas las entidades que tienen un componente de posición y un componente de movimiento. El componente de posición solo mantiene la posición y el componente de movimiento mantiene la velocidad.

Pero el problema real es mi sistema de colisión. Esta clase es como un blob lógico. Tengo muchos casos especiales en esta clase.

Por ejemplo: mis paletas pueden chocar con los bordes. Si esto sucede, su velocidad se establece en cero. Mi pelota también puede chocar con los bordes. Pero en este caso, su velocidad se refleja en la normalidad del borde, por lo que se refleja. Para hacer esto, le di a la pelota un componente de física adicional que simplemente dice: "Oye, esto no se detiene, se refleja". Entonces, en realidad, el componente de física no tiene datos reales. Es una clase vacía que está ahí para decirle al sistema si un objeto se refleja o se detiene.

Luego viene esto: quiero renderizar algunas partículas cuando la pelota colisiona con las paletas o los bordes. Así que creo que la pelota tiene que obtener otro componente que le dice al sistema de colisión que cree partículas en la colisión.
Entonces quiero tener potenciadores que pueden chocar con las paletas pero no con los bordes. Si eso sucede, los power-ups tienen que desaparecer. Por lo tanto, necesitaría muchos más casos y componentes (para decirle al sistema que algunas entidades solo pueden colisionar con ciertas otras, pero no con todas, incluso si algunas otras realmente pueden colisionar, además, el sistema de colisión tuvo que aplicar los poderes para las paletas, etc., etc., etc.).

Veo que el sistema de componentes de la entidad es bueno porque es flexible y no tiene problemas con la herencia. Pero estoy totalmente atrapado actualmente.

¿Estoy pensando demasiado complicado? ¿Cómo debo hacer frente a este problema?

Claro, tengo que crear sistemas que sean realmente responsables de la "colisión posterior", por lo que el sistema de colisión solo dice "Sí, tenemos una colisión en el último fotograma" y luego hay un montón de sistemas de "colisión posterior" que todos requieren diferentes (combinaciones de) componentes y luego cambian los componentes. Por ejemplo, habría un sistema de movimiento posterior a la colisión que detiene las cosas que deben detenerse cuando ocurre la colisión. Luego, un sistema de física posterior a la colisión que refleja las cosas, etc.

Pero tampoco parece ser una solución adecuada para mí porque, por ejemplo:

  1. Mi sistema de movimiento posterior a la colisión necesitaría entidades que tengan un componente de posición, un componente de movimiento y un componente de colisión. Entonces establecería la velocidad de la entidad a cero.
  2. El sistema de física posterior a la colisión necesitaría entidades que tengan un componente de posición, un componente de movimiento, un componente de colisión y un componente de física. Entonces reflejaría el vector de velocidad.

El problema es obvio: el movimiento posterior a la colisión necesita entidades que son un subconjunto de las entidades en el sistema de física posterior a la colisión. Por lo tanto, dos sistemas posteriores a la colisión operarían con los mismos datos, siendo el efecto: aunque una entidad tiene un componente físico, su velocidad sería cero después de una colisión.

¿Cómo se resuelven estos problemas en general en un sistema de componentes de entidad? ¿Son esos problemas habituales o estoy haciendo algo mal? En caso afirmativo, ¿qué y cómo se debe hacer en su lugar?

M0rgenstern
fuente

Respuestas:

11

Sí, estás pensando demasiado complicado.

Parece que muchos de sus problemas podrían resolverse con un sistema de mensajería y algunos atributos adicionales que le permitan especificar algunos filtros , y finalmente no preocuparse por ser tan estricto con las entidades / componentes.

La mensajería lo ayudará con algunos aspectos, como la activación de partículas, potenciadores, etc. Por ejemplo, podría tener un objeto mundial que se suscriba a eventos de partículas y cree partículas en la posición descrita en el evento.

Los filtros te ayudarán mucho en colisiones. Los filtros pueden definir si un objeto colisiona con otro y qué respuesta tendrá. Agrega algunos atributos a su componente de física que define qué tipo de cuerpo de física es, con qué otros tipos de cuerpos de física choca y cuál debería ser la respuesta. Por ejemplo, un objeto de física de pelota colisiona con un objeto de física de paleta y responde con reflexión y partículas.

Finalmente, no sea tan estricto con su implementación. Si puede encontrar una manera de hacerlo funcionar, pero no es realmente un sistema EC, hágalo. Como en mi ejemplo anterior, las partículas no necesitan ser manejadas por un sistema o parte del sistema EC. Es más importante terminar el juego que seguir estrictamente un método que ya está bastante mal definido.

MichaelHouse
fuente
Muchas gracias. Quería construir un juego usando un ECS solo para ver cómo se escala y si es realmente tan agradable de usar como leí en artículos y tutoriales. Mi problema fue que pensé: "Tengo un ECS ahora y todo tiene que ser manejado por esto". Así que también planeé escribir el sistema de partículas en relación con el ECS. Además, leí en algunos artículos que cada componente solo debería tener algunos datos básicos y nada más. Ese es a menudo mi problema ... creo que es demasiado complicado.
M0rgenstern
Quería construir un juego usando un ECS solo para ver cómo se escala y si es realmente tan agradable de usar como leí en artículos y tutoriales . Si ese es su objetivo, le recomiendo que mire los sistemas existentes de componentes / entidades en lugar de construir el suyo propio. Descargue Unity3D, que probablemente sea "Componentes tan puros como pueda" y juegue por ahí. Percepciones mucho más rápidas, en mi humilde opinión.
Imi
3
@lmi: Unity no es un sistema de componentes de entidad, aunque está basado en componentes. ECS tiene algunas pautas más estrictas ( nunca piense en un patrón como reglas) que simplemente tener y usar componentes de objetos de juego. Debido a una serie de artículos, ECS es popular entre algunos segmentos de desarrolladores de juegos en este momento, por lo que hay muchas preguntas sobre ECS específicamente en lugar del diseño basado en componentes en general.
Sean Middleditch
12

Estás complicando demasiado las cosas. Llegaría al extremo de decir que incluso usar un diseño basado en componentes es una exageración para un juego tan simple. Haga las cosas de la manera que haga que su juego sea rápido y fácil de desarrollar. Los componentes ayudan con la iteración en proyectos más grandes con una gran variedad de comportamientos y configuraciones de objetos de juego, pero su beneficio para un juego tan simple y bien definido es más cuestionable. El año pasado hablé sobre esto: puedes construir pequeños juegos divertidos en unas pocas horas si te enfocas en hacer un juego en lugar de adherirte a una arquitectura . La herencia se rompe cuando tienes 100 o incluso 20 tipos diferentes de objetos, pero funciona bien si solo tienes un puñado.

Suponiendo que desea seguir usando componentes con fines de aprendizaje, hay algunos problemas obvios con su enfoque que se destacan.

Primero, no haga que sus componentes sean tan pequeños. No hay razón para tener componentes de grano fino como 'movimiento'. No hay movimiento genérico en tu juego. Tiene paletas, cuyo movimiento está estrechamente vinculado a la entrada o AI (y realmente no usa velocidad, aceleración, restitución, etc.), y tiene la pelota, que tiene un algoritmo de movimiento bien definido. Solo tenga un componente PaddleController y un componente BouncingBall o algo por el estilo. Si / cuando obtienes un juego más complicado, entonces puedes preocuparte por tener un componente PhysicsBody más genérico (que en los motores 'reales' es básicamente solo un enlace entre el objeto del juego y cualquier objeto API interno utilizado por Havok / PhysX / Bullet / Box2D / etc.) Que maneja una variedad más amplia de situaciones.

Incluso un componente de "posición" es cuestionable, aunque ciertamente no es raro. Los motores de física suelen tener su propia idea interna de dónde está un objeto, los gráficos pueden tener una representación interpolada, y la IA puede tener otra representación de los mismos datos en un estado diferente. Puede ser ventajoso simplemente dejar que cada sistema administre su propia idea de la transformación en los componentes del sistema y luego asegurarse de que haya una comunicación fluida entre el sistema. Vea la publicación de blog de BitSquid en transmisiones de eventos .

Para motores de física personalizados, recuerde que puede tener datos sobre sus componentes. Tal vez un componente genérico de física de Pong tenga datos que indiquen en qué ejes puede moverse (por ejemplo, vec2(0,1)como un multiplicador para paletas que solo pueden moverse en el eje Y y vec2(1,1)para la bola que indica que puede moverse), una bandera o flotador que indica el rebote (el la pelota típicamente estaría en 1.0y las paletas en0.0), características de aceleración, velocidad, etc. Tratar de dividir esto en un billón de microcomponentes diferentes para cada pieza de datos altamente relacionados es contrario a lo que ECS estaba destinado originalmente a hacer. Mantenga las cosas que se usan juntas en el mismo componente cuando sea posible y solo divídalas cuando haya una gran diferencia en cómo cada objeto del juego usa esos datos. Hay un argumento para hacer que para Pong la física entre la pelota y las palas sea lo suficientemente diferente como para ser componentes separados, pero para un juego más grande, hay pocas razones para intentar hacer 20 componentes para hacer lo que funciona bien en 1-3.

Recuerde, si / cuando su versión de ECS se interponga, haga lo que necesita para hacer su juego y olvide la adherencia obvia a un patrón / arquitectura de diseño.

Sean Middleditch
fuente
1
¡De verdad gracias! Sé que un ECS no escala muy bien para un juego pequeño como el pong. Pero lo usé solo para ver cómo se implementa tal cosa y cómo funciona. Hice los componentes tan pequeños, porque eso era lo que leía principalmente en algunos artículos. Tener muchos componentes y cada componente solo contiene datos elementales. Entonces, ¿te entiendo bien, que sugieres usar una mezcla entre herencia y ECS? Como dices "la pelota y las paletas son lo suficientemente diferentes como para ser componentes separados". Entonces, por ejemplo, les doy un componente de Movimiento / Posición (tal vez como un componente) y
M0rgenstern
1
Lo que sea que funcione. Concéntrate en hacer el juego. Literalmente, solo tendría un componente llamado Ballque contiene toda la lógica de la pelota como movimiento, rebote, etc. y un Paddlecomponente que toma información, pero ese soy yo. Lo que tenga más sentido para usted y se salga de su camino y le permita hacer que el juego sea la "forma correcta" de hacer las cosas.
Sean Middleditch
3
Literalmente, solo tendría un componente llamado Ball que contiene toda la lógica de la pelota como movimiento, rebote, etc. y un componente Paddle que toma datos, pero ese soy yo. Y es por eso que hay una opinión para cada programador sobre "de qué se trata un sistema de componentes". Recomiendo NO hacerlo como esta sugerencia, excepto que está pensando totalmente en los sistemas de entidad clásicos y se ve obligado a usar un sistema de componentes pero no quiere ver cuáles son las diferencias en realidad.
Imi
2
@lmi: después de haber trabajado en algunos juegos / motores grandes con componentes y ver de primera mano por qué usamos componentes, no, los componentes demasiado granulares son más problemas de lo que valen. Los componentes no son una bala mágica; son una de las muchas herramientas en la caja de herramientas de un desarrollador de juegos. Úselos de una manera y lugar que ayuden y no de manera que simplemente agreguen más gastos mentales y de tiempo de ejecución al sistema. Si lo único que tiene física de la pelota es la pelota, no hay ninguna ventaja en separarla de otras propiedades de la pelota. Si y cuando eso cambia, divídalo solo entonces.
Sean Middleditch
1
Estoy de acuerdo con el principio de ser pragmático y no dejar que un patrón particular se interponga en su camino. PERO si ECS no puede manejar este juego trivial sin desviaciones, qué esperanza hay para un juego grande. Yo también estoy tratando de aprender cómo usar ECS de manera efectiva, pero estoy tratando de mantenerme lo más cerca posible de la filosofía de ECS, de lo contrario, una vez que comience a hacer excepciones y casos especiales, sé que terminaré con un desastre imposible de mantener.
Ken
-2

En mi opinión *), su mayor problema con los componentes es este: los componentes NO están aquí para decirle a nadie más qué hacer. Los componentes están aquí para HACER cosas. No tiene un componente solo para mantener la memoria de algo y luego hacer que otros componentes operen en esto. Desea componentes que HAGAN cosas con los datos que obtuvieron.

Si se ve a sí mismo probando la presencia de otros componentes (y luego llama a funciones allí), entonces esta es una señal clara de que cualquiera de las dos cosas es cierta:

  • En realidad, desea invertir la dependencia: el otro componente debe escuchar eventos / mensajes / broadcasts / hooks / howeveryounamethem para ejecutar su lógica en respuesta a su componente actual. El componente actual ni siquiera tiene que saber que hay un componente "otro". Este suele ser el caso, si te encuentras llamando a diferentes componentes (incluso en diferentes bloques / mayúsculas y minúsculas) con una funcionalidad que no está realmente conectada a tu método actual. Piense en todos estos Invalidate()o SetDirty()llamadas a otros componentes.
  • Es posible que tenga demasiados componentes. Si dos componentes simplemente no pueden vivir el uno sin el otro y constantemente necesitan recuperar datos y métodos de llamada entre sí, entonces simplemente combínelos. Obviamente, la funcionalidad que proporcionan está tan enredada, que en realidad es solo una cosa.

Por cierto, esto se aplica a todo tipo de sistemas, no solo a los sistemas de entidad / componente, sino también a la herencia clásica con simples "GameObject" o incluso funciones de biblioteca.

*) Realmente solo mío . Las opiniones varían mucho sobre Whats Da Real Component Systemz (TM)

Imi
fuente