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:
- 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.
- 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?
fuente
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 yvec2(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 en1.0
y 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.
fuente
Ball
que contiene toda la lógica de la pelota como movimiento, rebote, etc. y unPaddle
componente 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.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:
Invalidate()
oSetDirty()
llamadas a otros componentes.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)
fuente