¿Actualizaciones de objetos, internas o externas?

8

El título es un poco confuso, pero no puedo pensar en cómo explicar mi pregunta en una frase corta. Asi que aqui esta:

Cada vez que escribo motores de juegos, ya sea física / basada en mosaicos, etc., siempre llego al punto en el que no estoy seguro de cómo debo manejar las cosas. ¿Deberían las entidades del mundo manejar por sí mismas, o debería haber algún sistema global que las administre?

Aquí hay un ejemplo fácil: mover cosas. En caso de que cada objeto vea el mundo a su alrededor (verifique las colisiones) y muévase según eso.

[nota, este es un juego basado en fichas donde el objeto se mueve por ficha, así que no estoy usando física para mover de una ficha a otra]

public class Actor : GameObject
    {
        private void MoveTo(Vector2 location)
        {
            if (world.getTile(location) != solid && world.objAtTile(location) == null)
            {
                Tweener.addTween(this, location);
            }
          }
        }

¿O debería manejarse el movimiento de cada objeto en el mundo, donde el mundo verifica todo?

public class Actor : GameObject
    {
        private void MoveTo(Vector2 location)
        {
            world.moveTo(location);
          }
        }

public class World
{

    public void moveObject(GameObject obj, Vector2 location)
    {
      //called from object

      if (getTile(location) != solid && objAtTile(location) == null)
         {
                Tweener.addTween(obj, location);
         }
    }
}

Realmente no importa mucho para este ejemplo, supongo, pero puedo verme en problemas más adelante. Gracias por adelantado.

omgnoseat
fuente
1
Más a la pregunta, debe Actorsaber sobre worldtodo?
desaceleratedcaviar
1
Creo que estás combinando Objetos con Componentes y eso confunde el problema de quién está haciendo qué. Puede ser un problema semántico ya que Componente es un término muy sobrecargado. Creo que si aclaras qué es un Objeto, qué quieres decir con Componente y cómo organizas los pequeños motores que realmente hacen el trabajo, entonces puedes responder tu propia pregunta.
Patrick Hughes el
1
Tengo que estar de acuerdo con los demás en que usted pregunta si el objeto del juego se mueve solo o si el mundo mueve el objeto del juego. El código anterior no me lleva a creer que realmente se refiere a un sistema de tipo de componente y, por lo tanto, puede ser engañoso tenerlo en el título de su pregunta.
James
Utilicé algunos términos engañosos ahora que ustedes lo mencionan. ¡Voy a ajustarlo ahora! Editar: Mira que ya se ha cambiado, ¡gracias!
omgnoseat
@daniel Los actores claramente necesitan estar bien informados sobre el mundo que los rodea para poder moverse de manera efectiva, aunque normalmente diseñaría alrededor de un contenedor que encapsule ambos pero ninguno lo sepa. p.ej. El mundo contiene tanto actor como nivel; El actor sabe sobre el Nivel (o más bien se le informa sobre el Nivel cuando es necesario, pero no una referencia permanente) pero no sobre el Mundo.
jhocking

Respuestas:

2

Veo que sus ejemplos están en Java, pero sus etiquetas no especifican ningún idioma. En C ++, la respuesta es ninguna de las dos : ¡no debería usar una función miembro para esto!

void move_to(actor *a, world *w, vec2 location);

fuente
44
Tengo que decir gran admirador de los libros que escribió el autor del artículo. Sin embargo, el razonamiento del artículo es, por lo que pude ver, una buena teoría pero no un código práctico. Al mover métodos como estos fuera de los objetos a los que pertenecen, agrega más ubicaciones para buscar la funcionalidad que les pertenece, lo que solo causa confusión y niveles más altos de mantenimiento requerido. Solo mi punto de vista, así que no + o -. Solo dos centavos.
James
1
Además, debo decir que se parece a C # con la herencia usando: ClassName en lugar de extender ClassName.
James
Es un pseudocódigo basado en C #, pero eso no debería ser relevante para mi pregunta :)
omgnoseat
1
@omgnoseat: es relevante porque el consejo es imposible en un idioma sin funciones básicas, y su utilidad se reduce en cualquier idioma sin ADL. La buena arquitectura no es independiente de la elección del idioma.
2
@James: No hay nada que no sea OO en las funciones simples, si las funciones mismas invocan métodos que siguen los principios de diseño OO. Ese es el punto del artículo. (OO tampoco es sinónimo de buen diseño. Si un diseño claro o simple requiere que no use OO, abandone OO, no el diseño.)
1

No hay una respuesta simple.

Como con la mayoría de las cosas en programación, es una compensación. Dar más potencia a los objetos individuales los hace más grandes y, por lo tanto, más lentos, pero hace que el motor sea más fácil de entender y extender. Tener una megaclase que pueda manejar todo junto podría ser más rápido, pero a costa de tener una megaclase; es decir, generalmente se considera mala forma hacer clases supermasivas.

He leído algunos artículos sobre diseño basado en componentes y datos, donde tiene una sola clase que representa todos los objetos de un cierto tipo, almacena sus datos en listas y pasa solo índices para obtener propiedades de un objeto individual. Aunque puedo ver esto como un tipo de arquitectura viable, creo que se atormenta con todo el punto de orientación del objeto, que también ha recibido una buena cantidad de críticas.

Yo personalmente recomiendo dar más poder a los objetos. Tiene más sentido en un lenguaje orientado a objetos y (me imagino) más fácil de entender y mantener con el tiempo.

Casi perfecto
fuente
De alguna manera, usted subestima las cosas al decir que el estilo del diseño basado en componentes y datos que describió como "tornillos con todo el objetivo de OO", no es OO en absoluto. Al menos como se describe en "Los sistemas de entidades son el futuro del desarrollo de MMOG" (vinculado en la respuesta de pek), los sistemas de entidades son un paradigma de programación completamente diferente y si te encuentras tratando de hacer que tus componentes o entidades sean OO más allá de la simple conveniencia de empaque, Tú ' re Haciéndolo mal.
Dave Sherohman
66
Están perfectamente orientados a objetos. Simplemente no es el OO al que la mayoría de la gente está acostumbrada, pero está bien. La orientación a objetos se trata de acoplar el estado con los métodos que operan en ese estado, eso es todo. No requiere que cada objeto tenga que ser un objeto de la vida real ni nada por el estilo.
Kylotan
@Kylotan: Nuevamente, estoy hablando específicamente de modelos como los descritos en "Los sistemas de entidades son el futuro del desarrollo MMOG", donde los componentes son datos tontos operados por "sistemas" externos. No son objetos ya que no tienen comportamiento / métodos; son relaciones (piense en filas de bases de datos o estructuras de C). Una equivalencia errónea entre OOP y el uso diario de "objeto" no tiene nada que ver con eso. De la Parte 3: "Estoy decididamente en contra de usar OOP en cualquier lugar de una implementación de ES; es demasiado fácil infiltrar algo de OOP donde sea inapropiado y engañarme pensando que es una buena idea".
Dave Sherohman
Conozco el artículo, pero es solo la opinión de una persona sobre cómo se deben hacer estas cosas: no es la forma estándar, o necesariamente la mejor manera, por lo que no es correcto decir que el diseño impulsado por componentes es completamente diferente paradigma de programación Es perfectamente posible, y mucho más común, implementar una arquitectura basada en componentes con código OO, y el uso del código OO no daña de ninguna manera la "componente" de él más que el uso de procedimientos almacenados daña el aspecto relacional de una base de datos. .
Kylotan
@Dave, esta pregunta no es en absoluto acerca de los sistemas de entidad-componente. Ni ES ni el diseño basado en datos no son OO, eso es un error común, pero es un gran error asumirlo. si buscas en Google, encontrarás algunos buenos artículos que descubrirán este mito. Sin embargo, esta pregunta trata sobre los principios generales de OO de cómo diseñar sus objetos, los patrones de diseño de nivel superior no responden esa pregunta en absoluto.
Maik Semder
0

Estoy actualizando mi respuesta porque muchas cosas no estaban claras antes de los comentarios. Por favor, descuida conmigo mientras explico mis pensamientos.

En general, dos aspectos clave a considerar en cualquier diseño es la cohesión y el acoplamiento . Todos sabemos que necesitamos una alta cohesión y un bajo acoplamiento para poder hacer un diseño más reutilizable y extensible.

Entonces, si el mundo tiene que manejar todo, eso significa que tiene una baja cohesión y un acoplamiento estrecho (porque necesita saber y hacer todo). Sin embargo, este también es el caso cuando una entidad de juego tiene que hacer todo. Actualice su ubicación, renderice su textura, etc. etc.

Lo que realmente le interesa es crear sistemas que se centren en un aspecto de la entidad. Por ejemplo, una entidad del juego podría tener una Textura, pero un Representante sería responsable de representar esa textura en la pantalla. Al Renderer no le importa qué otras propiedades tenga la entidad.

Yendo un poco más allá, una entidad de juego es simplemente una bolsa de propiedades. Estas propiedades son manipuladas por sistemas que se centran en propiedades específicas. Y aquí es donde entran en juego los sistemas de entidad basados ​​en componentes (CBES), donde propiedades = componentes.

Específicamente, CBES con sistemas (o subsistemas). Este diseño tiende a tener unos pocos sistemas que se centran en componentes específicos de una entidad sin importar qué otros componentes tiene la entidad. Además, los sistemas se combinan solo con la información que necesitan para procesar estos componentes.

Tomemos tu ejemplo. Dado que la entrada de dónde mover la entidad se basa en el controlador del jugador, es probable que tenga un PlayerControllerSystem. Este sistema controlaría, además de muchas otras cosas, el Componente de posición de la entidad. En este caso, el PlayerControllerSystem necesitaría saber sobre el Nivel y el Componente de Posición. Si más tarde decide agregar la detección de colisión, crearía un CollisionSystem que nuevamente usaría la posición de las entidades, pero esta vez para calcular cuadros delimitadores (o podría tener un BoundingBoxComponent, su llamada). El hecho es que puede activar o desactivar fácilmente el comportamiento (incluso sobre la marcha) simplemente agregando / eliminando componentes. Entonces, más comportamiento significa que más sistemas están manipulando los componentes de una entidad, pero todos están en una clase bien definida con bajo acoplamiento. ¿Quieres scripting? Agregar un componente de secuencia de comandos. BAM! Acaba de agregar capacidades de scripting con 2 clases. ¿Física? ¿Sonido? Lo mismo de nuevo.

Entonces, la razón por la que estoy abogando por CBES con SubSystems es porque es perfectamente OO y un sistema general fácil de mantener / extensible. Agregar un comportamiento a una entidad es tan simple como decidir qué datos necesita ese comportamiento y qué entidades lo necesitan.

Para obtener más información sobre los sistemas de entidades basados ​​en componentes con subsistemas, hay una excelente serie de publicaciones de blog de T = Machine en Entity Systems que son el futuro del desarrollo de MMOG . El autor incluso llegó a crear un wiki para recopilar varias implementaciones llamadas Entity Systems Project

Una publicación general (y bien conocida) sobre Sistemas de entidades basadas en componentes en general es Evolucionar su jerarquía que creó el sistema para Tony Hawk Pro.

Finalmente, si está buscando una biblioteca con código de ejemplo, no vaya más allá de la biblioteca Artemis . Artemis está principalmente en Java, pero aquí hay un puerto en C # (que actualmente estoy usando en mi proyecto XNA).

pek
fuente
2
-1, ya que esto no resuelve el problema, solo lo mueve. Todavía no tendrá una respuesta clara sobre qué objeto toma la decisión, ya sea que se trate de componentes o no.
Kylotan
1
El segundo enlace proporciona un extenso tutorial de por qué el comportamiento de las entidades del juego debe externalizarse a Entity Systems. Artemisa sigue exactamente la misma arquitectura.
Pek
El segundo enlace es un artículo de opinión. Muchos prefieren los componentes, pero no son una solución para todos los problemas, y definitivamente no tienen una solución intrínseca a este.
Kylotan
La respuesta más votada en esta pregunta comienza "No hay una respuesta simple. Como con la mayoría de las cosas en programación, es una compensación". El segundo enlace proporciona una de muchas soluciones. De hecho, defiende exactamente lo contrario de esa respuesta. Tal vez debería editar mi respuesta para hacerlo más claro. Pero, de nuevo, tal vez esto no cambie los votos negativos: P
pek
@pek: Mi voto negativo es porque "usar componentes" no es una respuesta a esta pregunta. Sí uso componentes, y recomiendo el patrón de componentes, pero "usar un sistema de entidad basado en componentes" no es una respuesta a todos los problemas en el desarrollo de juegos, ni siquiera en la arquitectura de software de juegos. Esta pregunta surge de la misma manera con los componentes o la herencia. jerarquías o tipos de entidades estáticas totalmente no relacionadas!
0

Por lo general, utilizo un diseño en el que los objetos se manejan solos (para eso son los métodos, después de todo), pero el mundo base tiene listas de todos los objetos y usa esas listas para coordinarlos. Entonces algo como:

class World {
  var walls = [];
  var actors = [];
  function update() {
    for each (actor in actors) {
      actor.update(walls);
    }
  }
}

class Actor {
  function update(walls) {
    this.move();
    for each (wall in walls) {
      this.collide(wall);
    }
  }
}
jhocking
fuente
0

Mantenlo SECO, tímido y dile al otro chico.

Es mejor pedirle al mundo que mueva a su actor que preguntarle al mundo si puede moverse a donde quiere ir. De esta manera, puede cambiar fácilmente el algoritmo de búsqueda de ruta en la clase mundial y tal. Por supuesto, podrías dejar esto en manos de una clase base para el actor, pero esa es la dirección que tomaría y el motivo.

Daniel Carlsson
fuente
-1

EDITAR: Reescrito debido a los comentarios que indican que no había dibujado la línea lo suficientemente clara para esta respuesta a la pregunta original.

Me gustaría aclarar la pregunta un poco más si es posible. La lógica que actúa sobre un objeto debe ser interna o externa a ese objeto. La última parte de la publicación menciona específicamente la longevidad del diseño como la base para hacer la pregunta, para evitar problemas más adelante.

Mi respuesta simple de alto nivel es mantener toda la lógica que actúa sobre un objeto dentro de ese objeto.

No necesita que el mundo mueva un objeto, todo lo que necesita es el objeto a mover y el vector que representa la ubicación para moverlo. El mundo puede entrar en juego cuando se elige un destino o cuando se requiere una reacción al medio ambiente debido a una colisión, pero estos están fuera del ejemplo dado para trabajar.

Para abordar la parte subyacente de la pregunta y lograr la longevidad del diseño, sugeriría una arquitectura orientada a componentes. Las arquitecturas de componentes dividen un objeto en conjuntos discretos de datos y funcionalidad (suponiendo que siga con mi respuesta anterior, que establece mantener la lógica con los datos). Si su estructura se parece a CEntity-> CHuman-> CSoldier-> CPlayerCharacter, invariablemente se encontrará con problemas en los que necesita alterar alguna lógica y dependiendo de dónde vaya en ese árbol (¿Cuántos otros tipos de humanos hay, por ejemplo?) Puede tener efectos radicales en múltiples tipos de objetos.

En cambio, un sistema de componentes tendría un conjunto de interfaces que definen de qué está compuesto el objeto CEntity como ICompRenderable, ICompMoveable, ICompHealth, ICompInventory, etc. Donde tendrías CCompMoveableHuman y posiblemente un CCompMoveableSoldier y CCompMoveablePlayer para mantener separados sus patrones de movimiento individuales. Así que digamos que el Soldado está alterado para correr en formaciones. Este cambio solo afectará a las entidades creadas con ese componente.

Entonces, para resumir, sugiero que contenga la lógica con los datos a los que se aplica la lógica. También recomiendo dividir objetos en componentes discretos para reducir el '¿Dónde pongo esto?' preguntas y proporcionar estabilidad en el futuro con facilidad de mantenimiento y es extensible.

Espero que esto ayude.

James
fuente
He estado investigando el patrón de componentes en las últimas horas. (los enlaces de pek son muy agradables). Pero aún no está claro dónde debería tener lugar el "comportamiento" real. Está claro que una entidad tiene diferentes componentes para entrada, representación, física, pero si entendí correctamente, los componentes solo contienen los datos necesarios para poder funcionar, pero en realidad no contienen la implementación. Volvamos a movernos nuevamente; ¿Cómo debo hacer que una entidad se mueva usando los componentes? ¿Debería extender el componente de entrada y codificar la implementación allí? ¿O algo más gestionar la implementación?
omgnoseat
Para mí, mantengo la lógica en los objetos, pero en realidad estoy haciendo un sistema de componentes además del diseño orientado a datos, lo que significa que la mayoría de mis objetos son en realidad solo la lógica que se asigna a ubicaciones dentro de los datos para un procesamiento rápido contiguo ... Y al final del día, aprenda un patrón de diseño a la vez: D En cuanto al -1, agradecería saber por qué se etiquetó así, incluso si es solo 'Me gusta mi respuesta mejor'. Todavía me gustaría saber porque.
James
1
@ James, bastante justo. Voté en contra porque los sistemas de entidad-componente no responden la pregunta original. Un componente de posición aún puede moverse solo o el mundo podría iterar sobre todos los componentes de posición y moverlos. La misma razón por la que Kylotan rechazó la respuesta de Pek, así que pensé que la razón -1 era obvia. E incluso si fuera obligatorio que un sistema de componentes se moviera solo (que no es el caso), aún así no sería una respuesta, solo un caso de uso para 1 de las opciones. Pero la pregunta era sobre las razones para usar uno u otro, sus pros y sus contras. La respuesta no cubre eso.
Maik Semder
@Maik Semder. Gracias por la respuesta. He reescrito la respuesta para tratar de dibujar las líneas con mayor claridad a la pregunta inicial y el impulso general para ello en primer lugar, como se indica al final de la pregunta.
James
@ James, gracias por tomarse el tiempo para editar la respuesta en respuesta a mi comentario, muy apreciado. La respuesta propuesta para "mantener toda la lógica que actúa sobre un objeto dentro de ese objeto" derrota una característica central de OO, a saber, la encapsulación. Como muestra el enlace de Joe, el uso de funciones que no son miembros y no amigos aumenta el nivel de encapsulación de un objeto. Además, introduce un método claro sobre cómo medir realmente la encapsulación. Poner la función de movimiento en el mundo o en el actor disminuye la encapsulación del diseño, lo que resulta en un código menos flexible y más difícil de mantener.
Maik Semder
-1

Le recomiendo que intente leer sobre arquitecturas basadas en componentes. Hay bastantes publicaciones de blog, presentaciones y redacciones sobre ellos que pueden hacer un mejor trabajo que yo.

AltDevBlogADay tiene bastantes publicaciones al respecto, una muy buena es esta serie sobre entidades del juego: http://altdevblogaday.com/2011/07/10/the-game-entity-–-part-ia-retrospect/ Hay múltiples partes que se centran en el problema que está tratando de resolver.

Kyle
fuente