Espero que estas divagaciones aclaren mi pregunta: sin embargo, entendería totalmente si no lo harían, así que avíseme si ese es el caso e intentaré aclararme.
Conoce a BoxPong , un juego muy simple que hice para familiarizarme con el desarrollo de juegos orientado a objetos. Arrastra el cuadro para controlar el balón y recoge las cosas amarillas.
Hacer BoxPong me ayudó a formular, entre otras cosas, una pregunta fundamental: ¿cómo puedo tener objetos que interactúen entre sí sin tener que "pertenecer" el uno al otro? En otras palabras, ¿hay alguna forma de que los objetos no sean jerárquicos, sino que coexistan? (Voy a entrar en más detalles a continuación).
Sospecho que el problema de los objetos que coexisten es común, así que espero que haya una forma establecida de resolverlo. No quiero reinventar la rueda cuadrada, así que supongo que la respuesta ideal que estoy buscando es "aquí hay un patrón de diseño que se usa comúnmente para resolver su tipo de problema".
Especialmente en juegos simples como BoxPong, está claro que hay, o debería haber, un puñado de objetos que coexisten en el mismo nivel. Hay una caja, hay una pelota, hay un coleccionable. Sin embargo, todo lo que puedo expresar en lenguajes orientados a objetos, o al menos eso parece, son relaciones estrictas HAS-A . Eso se hace a través de las variables miembro. No puedo comenzar ball
y dejar que haga lo suyo, necesito que pertenezca permanentemente a otro objeto. He configurarlo de manera que el objeto principal del juego tiene una caja, y la caja a su vez tiene una bola, y tiene un contador de puntuación. Cada objeto también tiene unupdate()
método, que calcula la posición, dirección, etc., y sigo un camino similar allí: llamo al método de actualización del objeto principal del juego, que llama a los métodos de actualización de todos sus hijos, y ellos a su vez llaman a los métodos de actualización de todos sus hijos. Esta es la única forma en que puedo ver para hacer un juego orientado a objetos, pero siento que no es la forma ideal. Después de todo, no pensaría exactamente que la pelota pertenece a la caja, sino que está en el mismo nivel e interactuando con ella. Supongo que eso se puede lograr convirtiendo todos los objetos del juego en variables miembro del objeto principal del juego, pero no veo que eso resuelva nada. Quiero decir ... dejando de lado el desorden obvio, ¿cómo habría una manera para que la pelota y la caja se conocieran , es decir, interactuaran?
También está el problema de los objetos que necesitan pasar información entre ellos. Tengo bastante experiencia escribiendo código para SNES, donde tienes acceso a prácticamente toda la RAM todo el tiempo. Digamos que está haciendo un enemigo personalizado para Super Mario World , y desea que elimine todas las monedas de Mario, luego simplemente almacene cero para abordar $ 0DBF, no hay problema. No hay limitaciones para decir que los enemigos no pueden acceder al estado del jugador. Supongo que esta libertad me ha echado a perder, porque con C ++ y similares a menudo me pregunto cómo hacer que un valor sea accesible para otros objetos (o incluso globales).
Usando el ejemplo de BoxPong, ¿y si quisiera que la pelota rebotara en los bordes de la pantalla? width
y height
son propiedades de la Game
clase,ball
tener acceso a ellos. Podría transmitir este tipo de valores (ya sea a través de los constructores o los métodos donde se necesitan), pero eso me grita una mala práctica.
Supongo que mi principal problema es que necesito que los objetos se conozcan, pero la única forma en que puedo hacerlo es mediante una jerarquía estricta, que es fea y poco práctica.
He oído hablar de "clases de amigos" en C ++ y sé cómo funcionan, pero si son la solución final, entonces, ¿cómo es que no veo friend
palabras clave vertidas en todos los proyectos de C ++ y cómo es que Qué concepto no existe en todos los lenguajes OOP? (Lo mismo ocurre con los punteros de función, de los que acabo de enterarme recientemente).
Gracias de antemano por las respuestas de cualquier tipo, y nuevamente, si hay una parte que no tiene sentido para usted, hágamelo saber.
Respuestas:
En general, resulta muy malo si los objetos del mismo nivel se conocen entre sí. Una vez que los objetos se conocen, están atados o acoplados entre sí. Esto los hace difíciles de cambiar, difíciles de probar, difíciles de mantener.
Funciona mucho mejor si hay algún objeto "arriba" que conoce los dos y puede establecer las interacciones entre ellos. El objeto que conoce a los dos pares puede vincularlos mediante la inyección de dependencia o mediante eventos o mediante el paso de mensajes (o cualquier otro mecanismo de desacoplamiento). Sí, eso lleva a un poco de jerarquía artificial, pero es mucho mejor que el desorden de espagueti que se produce cuando las cosas simplemente interactúan de manera involuntaria. Eso es solo más importante en C ++, ya que también necesita algo para poseer la vida útil de los objetos.
En resumen, puede hacerlo simplemente teniendo objetos uno al lado del otro unidos por acceso ad hoc, pero es una mala idea. La jerarquía proporciona orden y propiedad clara. Lo principal a recordar es que los objetos en el código no son necesariamente objetos en la vida real (o incluso en el juego). Si los objetos en el juego no forman una buena jerarquía, una abstracción diferente puede ser mejor.
fuente
¡No!
Creo que el problema principal que tienes es que estás tomando la "Programación Orientada a Objetos" demasiado literalmente. En OOP, un objeto no representa una "cosa" sino una "idea" que significa que una "Bola", "Juego", "Física", "Matemáticas", "Fecha", etc. Todos son objetos válidos. Tampoco se exige que los objetos "sepan" nada. Por ejemplo, le
Date.Now().getTommorrow()
preguntaría a la computadora qué día es hoy, aplique reglas de fecha arcana para determinar la fecha de mañana y se la devolverá a la persona que llama. ElDate
objeto no sabe nada más, solo necesita solicitar información según sea necesario del sistema. Además,Math.SquareRoot(number)
no necesita saber nada más que la lógica de cómo calcular una raíz cuadrada.Entonces, en su ejemplo que cité, la "Bola" no debería saber nada sobre la "Caja". Las cajas y las bolas son ideas completamente diferentes y no tienen derecho a hablar entre ellas. Pero un motor de Física sí sabe qué es un Box and Ball (o al menos, ThreeDShape), y sabe dónde están y qué debería estar pasando con ellos. Entonces, si la bola se encoge porque hace frío, el motor de física le diría a esa instancia de bola que ahora es más pequeña.
Es como construir un auto. Un chip de computadora no sabe nada sobre el motor de un automóvil, pero un automóvil puede usar un chip de computadora para controlar un motor. La simple idea de usar cosas pequeñas y simples juntas para crear una cosa un poco más grande y más compleja, que en sí misma es reutilizable como un componente de otras partes más complejas.
Y en tu ejemplo de Mario, ¿qué pasa si estás en una sala de desafío donde tocar a un enemigo no agota las monedas de Marios, sino que simplemente lo expulsa de esa sala? Está fuera del espacio de ideas de Mario o del enemigo que Mario debe perder monedas al tocar a un enemigo (de hecho, si Mario tiene una estrella invulnerabilidad, mata al enemigo en su lugar). Entonces, cualquier objeto (dominio / idea) que sea responsable de lo que sucede cuando Mario toca a un enemigo es el único que necesita saber acerca de cualquiera de ellos, y debe hacer lo que sea por cualquiera de ellos (en la medida en que ese objeto permita cambios externos impulsados )
Además, con sus afirmaciones sobre los objetos que llaman niños
Update()
, eso es extremadamente propenso a los errores, ¿y siUpdate
se llama varias veces por fotograma de diferentes padres? (incluso si captas esto, eso es tiempo de CPU perdido que puede ralentizar tu juego) Todos deberían tocar lo que necesitan, cuando lo necesitan. Si está utilizando Update (), debería estar utilizando algún tipo de patrón de suscripción para asegurarse de que todas las Update se llamen una vez por cuadro (si esto no se maneja en Unity)Aprender a definir sus ideas de dominio en bloques claros, aislados, bien definidos y fáciles de usar será el factor más importante en la forma en que puede utilizar OOP.
fuente
Parece que le falta el punto de la programación orientada a objetos.
La programación orientada a objetos se trata de administrar dependencias mediante la inversión selectiva de ciertas dependencias clave en su arquitectura para que pueda evitar la rigidez, la fragilidad y la no reutilización.
¿Qué es la dependencia? La dependencia es la dependencia de otra cosa. Cuando almacena cero para abordar $ 0DBF, se basa en el hecho de que esa dirección es donde se encuentran las monedas de Mario y que las monedas se representan como un número entero. Su código de Enemigo personalizado depende del código que implementa Mario y sus monedas. Si realiza un cambio en el lugar donde Mario almacena sus monedas en la memoria, debe actualizar manualmente todo el código que hace referencia a la ubicación de la memoria.
El código orientado a objetos se trata de hacer que su código dependa de abstracciones y no de detalles. Entonces en lugar de
tu escribirias
Ahora, si desea cambiar la forma en que Mario almacena sus monedas de un int a un largo o doble o almacenarlo en la red o almacenarlo en una base de datos o iniciar algún otro proceso largo, realice el cambio en un solo lugar: el Mario, y todos los demás códigos siguen funcionando sin cambios.
Por lo tanto, cuando preguntas
realmente estás preguntando:
que no es programación orientada a objetos.
Le sugiero que comience leyendo todo aquí: http://objectmentor.com/omSolutions/oops_what.html y luego busque en YouTube todo por Robert Martin y mire todo.
Mis respuestas se derivan de él, y algunas de ellas se citan directamente de él.
fuente
mario.coins = 0;
, peromario.loseCoins();
, lo cual está bien y es cierto, pero mi punto es, ¿cómo puede el enemigo tener acceso almario
objeto de todos modos?mario
ser miembro variable deenemy
no me parece correcto.Puede habilitar el acoplamiento suelto aplicando el patrón de mediador. Implementarlo requiere que tenga una referencia a un componente mediador que conozca todos los componentes receptores.
Se da cuenta del tipo de pensamiento "maestro del juego, por favor deja que esto y aquello suceda".
Un patrón generalizado es el patrón de publicación-suscripción. Es apropiado si el mediador no debe contener mucha lógica. De lo contrario, use un mediador hecho a mano que sepa dónde enrutar todas las llamadas y tal vez incluso modificarlas.
Hay variantes síncronas y asíncronas que generalmente se denominan bus de eventos, bus de mensajes o cola de mensajes. Búsquelos para determinar si son apropiados en su caso particular.
fuente