uqbar-project / wollok

Wollok Programming Language
GNU General Public License v3.0
61 stars 16 forks source link

Add z-depth to positions #1319

Open PalumboN opened 6 years ago

PalumboN commented 6 years ago

One UNQ student suggests to add some z coord to positions for control the order in which visual objects are printed in screen.

fdodino commented 5 years ago

Proposal for new Wollok Game.

isaiaslafon commented 5 years ago

instead of Z (that is more like 3D approach) can use a layer, but to not make it too complex can use something like a back-middle-top layer or something like that, or with numers (could not be clear if 0 is background or top).

npasserini commented 5 years ago

A mí me parece que ponerle un número de layer debería ser fácil tanto de implementar como de comprender.

El sáb., 13 de jul. de 2019 a la(s) 14:15, Isaias Lafon ( notifications@github.com) escribió:

instead of Z (that is more like 3D approach) can use a layer, but to not make it too complex can use something like a back-middle-top layer or something like that, or with numers (could not be clear if 0 is background or top).

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/1319?email_source=notifications&email_token=ABDLKOL6CRZHLHN63DUTWO3P7IETTA5CNFSM4EIUXBNKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODZ3VY6I#issuecomment-511138937, or mute the thread https://github.com/notifications/unsubscribe-auth/ABDLKOOJAVXH4JVCPKMV523P7IETTANCNFSM4EIUXBNA .

isaiaslafon commented 5 years ago

I agree, after think it again, hehe. Then they can reference a layer number with a constant to name it whatever they want.

A mí me parece que ponerle un número de layer debería ser fácil tanto de implementar como de comprender. El sáb., 13 de jul. de 2019 a la(s) 14:15, Isaias Lafon ( notifications@github.com) escribió: instead of Z (that is more like 3D approach) can use a layer, but to not make it too complex can use something like a back-middle-top layer or something like that, or with numers (could not be clear if 0 is background or top). — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub <#1319?email_source=notifications&email_token=ABDLKOL6CRZHLHN63DUTWO3P7IETTA5CNFSM4EIUXBNKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODZ3VY6I#issuecomment-511138937>, or mute the thread https://github.com/notifications/unsubscribe-auth/ABDLKOOJAVXH4JVCPKMV523P7IETTANCNFSM4EIUXBNA .

PalumboN commented 5 years ago

Estaba pensando en algo como:

game.addVisual(pepita)
game.moveToLayer(pepita, 1)

Y así pepita quedaría siempre visible.

La lógica sería

@ldevoto este podría ser un issue interesante para seguir laburando con WG :wink:

isaiaslafon commented 5 years ago

No se si le agrega mucha complejidad, pero quizas que lea de un metodo como image() pero qye tenga layer, que si es el addvisualin hay que agregarle tambien la capa aparte de la posicion.

PalumboN commented 5 years ago

Sí, también podría estar en el dominio, pero me pareció que era algo más externo para que el juego se vea bien, y no implicaba una lógica de dominio.

nscarcella commented 5 years ago

Al final acá se escribe en inglés o en español?

Un dato de color para el nombre es que la mayoría de los frameworks renderizan en el mismo orden que ejecutan el update, así que es probable que cambiar de z o layer implique cambiar el orden en que los elementos ejecutan también su lógica. Entiendo que idealmente esto es transparente para el alumno pero hay varios casos dónde puede impactarle o causar issues como que un objeto cambie de layer, se ejecute dos veces y ponele, no detecte una colisión.

Por otro lado, también podría uno querer pensar cómo impacta esto a otros comportamientos, para asegurarnos de no tener que contar esto de prepo porque sino no cierra uno de los primeros ejemplos. Por ejemplo, que debería pasar si un objeto en layer N se mueve a un casillero donde ya hay un objeto en el layer N? Va arriba, abajo o se turnan para dibujarse un ratito cada uno de forma intermitente (a lo Dwarf Fortress)? Si cada layer es un stack (ponele) y hay más de un objeto ahí cómo se controla cual está arriba de cuál?

Fuera de eso, sería bueno mantener la manera idiomática que hay en otras areas para referirse a un layer de forma relativa (onda, atrásDe, adelanteDe). En ese sentido podría estar bueno representar el layer como parte de la posición (aunque le digas layer) porque permitiría mantener el contrato:

game.AddVisualIn(pepita, game.atrasDe(pepota))

npasserini commented 5 years ago

Ninguein weiss in cuale language on ècris.

Yo digo que (así sacándomelo medio del culo en este momento, pero en base a cosas que medio venimos charlando): 1) los addVisualIn y sus familiares deben desaparecer. 2) La respuesta a "quiero desacoplar el dominio de la UI" debería ser, crear un app model, o sea si no querés que Pepita sepa del cominio crea un PepitaView. 3) Vale agregar más mensajes al contrato de los objetos visuales si hace falta, en tanto sean opcionales (o sea podría ser válido preguntarle al objeto en qué layer quiere estar => te molesta que pepita entienda layer => ver punto 2). No sé si quiero agregar layer, pero mientras sea opcional, sería válido. 4) Me pregunto si podemos hacer gue los mensajes tipo image o position tengan valores de retorno mixtos, por ejemplo:

Y a todo esto no hay que olvidarse de que vamos a tener los roles.

El jue., 22 de ago. de 2019 a la(s) 17:13, nscarcella ( notifications@github.com) escribió:

Al final acá se escribe en inglés o en español?

Un dato de color para el nombre es que la mayoría de los frameworks renderizan en el mismo orden que ejecutan el update, así que es probable que cambiar de z o layer implique cambiar el orden en que los elementos ejecutan también su lógica. Entiendo que idealmente esto es transparente para el alumno pero hay varios casos dónde puede impactarle o causar issues como que un objeto cambie de layer, se ejecute dos veces y ponele, no detecte una colisión.

Por otro lado, también podría uno querer pensar cómo impacta esto a otros comportamientos, para asegurarnos de no tener que contar esto de prepo porque sino no cierra uno de los primeros ejemplos. Por ejemplo, que debería pasar si un objeto en layer N se mueve a un casillero donde ya hay un objeto en el layer N? Va arriba, abajo o se turnan para dibujarse un ratito cada uno de forma intermitente (a lo Dwarf Fortress)? Si cada layer es un stack (ponele) y hay más de un objeto ahí cómo se controla cual está arriba de cuál?

Fuera de eso, sería bueno mantener la manera idiomática que hay en otras areas para referirse a un layer de forma relativa (onda, atrásDe, adelanteDe). En ese sentido podría estar bueno representar el layer como parte de la posición (aunque le digas layer) porque permitiría mantener el contrato:

game.AddVisualIn(pepita, game.atrasDe(pepota))

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/1319?email_source=notifications&email_token=ABDLKOOUMUU67QPOK27KKBLQF3XNLA5CNFSM4EIUXBNKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD46IG7Q#issuecomment-524059518, or mute the thread https://github.com/notifications/unsubscribe-auth/ABDLKOIQ5ZXKTCJT7XPNMF3QF3XNLANCNFSM4EIUXBNA .

asanzo commented 3 years ago

Refloto esto.

Si bien jugar a tener una dimensión más me resulta interesante, creo que a priori hay un problema que resolver y es dejar al usuario elegir qué cosa se ve encima de qué otra.

object pepita {
    var property position = game.at(3,3)
    method image() = "pepita.png"
    method layer() = game.bottomLayer() // default
}

object pepona {
    var property position = game.at(3,3)
    method image() = "pepona.png"
    method layer() = pepita.layer().above()
}

program {
     game.addVisual(pepona)
     game.addVisual(pepita)
     // y pepona se muestra encima de pepita
}

/*
game también entendería:
game.bottomLayer()
game.topLayer()

y los Layer:
layer.above() // la layer de arriba. En la topLayer da error.
layer.below() // la layer de abajo. En la bottomLayer da error.

*/

Así sin números, todo relativo. Y nosotros garantizamos que la bottomLayer() sea la de más abajo y la topLayer() la de más arriba, aunque se agreguen intermedias, infinitas intermedias.

De hecho, podríamos tener middleLayer() con una lógica parecida, y que sea la default.

¿Qué opinan?

asanzo commented 3 years ago

Hmmm quizás en lugar de top y bottom se pueda hablar de front y back, puede ser mejor nombre. (below y above se mantendrían)

npasserini commented 3 years ago

Al principio dudé pero luego de meditarlo un poco, creo que me gusta la idea de manejarlo como una coordenada independiente, separada del objeto position. +1 a eso.

Lo que no me compro mucho es lo de manejarlo sin números. Primero por una cuestión implementativa, lo de "infinitas intermedias" es de implementación engorrosa y posiblemente problemas de performance. Uno podría mantener la interfaz propuesta, pero por debajo traducir a números. Si querés traducí topLayer como MAX_INT y son virtualmente infinitas, deberían ser suficiente para cualquier propósito razonable, de hecho con 640k deberían alcanzar 😜. Hablando en serio, le pondría un número mucho más bajo, onda 10 o, si te sentís muy apretado, 100.

Si me pongo teórico, un problema de no tener números es que eso define una relación de orden incompleta, por ejemplo si hago dos veces defaultLayer.above()... es dos veces el mismo layer? Y si hago default.above().below() es de nuevo el layer default? Si la respuesta en ambos casos es sí, entonces estás pensando en números y no vale la pena tratar de esquivar el bulto. Si la respuesta es no, me parece complejo de manejar y (como dije arriba) poco performante para implementar. Además de que posiblemente el framework subyacente usa layers numéricos.

De paso, tampoco sé si es necesaria la limitación de que arriba de la top layer no pueda haber nada, eventualmente va a ser una limitación. Pensemos la top layer no como una garantía de "es la más alta" sino como "una muy alta que viene por default"... algún día vas a querer poner algo adelante y todo se complica.

Y finalmente, si tengo 10 layers, creo que tener que encontrar la tercera como top.below().below().below() puede ser problemático. O bien tengo que estar seguro de tener algún otro componente de ese layer a mano para poder ponerme al lado / arriba / abajo de ese, o bien tengo que encadenar llamadas a below/above... molesto. En definitiva, banco la existencia de esos métodos y la propuesta de que sean la primera forma de acercarse al problema, pero agregaría un método que sea "dame el layer 7".

Una más: +1 a usar front/back, pero eso obligaría a modificar también above/below, para ser consistente.

On Wed, Oct 28, 2020 at 12:40 PM asanzo notifications@github.com wrote:

Hmmm quizás en lugar de top y bottom se pueda hablar de front y back, puede ser mejor nombre. (below y above se mantendrían)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/1319#issuecomment-718019217, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABDLKOK5IWHWHBI6DALFZVLSNA3NTANCNFSM4EIUXBNA .

asanzo commented 3 years ago

Ok, va una nueva propuesta:

object pepita {
    var property position = game.at(3,3)
    method image() = "pepita.png"
    method layer() = game.front() // default
}

object pepona {
    var property position = game.at(3,3)
    method image() = "pepona.png"
    method layer() = pepita.layer().above()
}

program {
     game.addVisual(pepona)
     game.addVisual(pepita)
     // y pepona se muestra encima de pepita
}

/*
game entendería:
game.back() // ¿O quizás game.backLayer()?
game.front() // ¿O quizás game.frontLayer()?
game.layer(n) // devuelve el layer correspondiente

assert.equals(game.layer(-100), game.back()) // idea, para que se entienda que puede ser negativo
assert.equals(game.layer(100), game.front()) 

y los Layer:
layer.above() // la layer inmediatamente superior
layer.below() // la layer de abajo

*/
npasserini commented 3 years ago

Por mí va como piña. No sé si hace falta que sean wkos. Con que se comparen por número es suficiente, supongo, en todo caso midamos. Me queda la duda sobre sí hay un mejor vocabulario que mezclar front/back con above/below... pero bueno, es un detalle. ¿Y por qué el default es front (aka layer 100) y no layer 0?

On Mon, Dec 14, 2020 at 7:25 PM asanzo notifications@github.com wrote:

Ok, va una nueva propuesta:

object pepita {

var property position = game.at(3,3)

method image() = "pepita.png"

method layer() = game.front() // default

}

object pepona {

var property position = game.at(3,3)

method image() = "pepona.png"

method layer() = pepita.layer().above()

}

program {

 game.addVisual(pepona)

 game.addVisual(pepita)

 // y pepona se muestra encima de pepita

}

/* game entendería: game.back() // ¿O quizás game.backLayer()? game.front() // ¿O quizás game.frontLayer()? game.layer(n) // devuelve el layer correspondiente

assert.equals(game.layer(-100), game.back()) // idea, para que se entienda que puede ser negativo assert.equals(game.layer(100), game.front())

y los Layer: layer.above() // la layer inmediatamente superior layer.below() // la layer de abajo

*/

  • Para no caer en el problema de performance de las posiciones, las layer deberían ser well known objects como los de Gamma: si yo escribo game.layer(1) y game.layer(2).below() deberían ser el mismo objeto.
  • Les juro que quería decir algo más sobre la implementación, pero no recuerdo qué y quiero pushear este comment XD. Bueno, no sé si tendría problemas de implementación esto que propongo.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/1319#issuecomment-744749192, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABDLKOOA7D3YZ26J6S7EQF3SU2GFLANCNFSM4EIUXBNA .

nscarcella commented 3 years ago

Yo tampoco usaría above y below con front/back.

Propongo: inFrontOf / behindOf / front / back

npasserini commented 3 years ago

+1 a eso.

Y no nos olvidemos de un layer "default"

On Tue, Dec 15, 2020 at 1:46 AM nscarcella notifications@github.com wrote:

Yo tampoco usaría above y below con front/back.

Propongo: inFrontOf / behindOf / front / back

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/1319#issuecomment-745049402, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABDLKOOKJ5RRDN6K4MXLSILSU3SZ5ANCNFSM4EIUXBNA .

asanzo commented 3 years ago

Estoy a 1 modificación de tirar esto en un doc.

Va otra propuesta.

object pepita {
    var property position = game.at(3,3)
    method image() = "pepita.png"
    method layer() = game.backLayer()
    // si un objeto no define el método layer(), por defecto el layer es game.layer(0)
}

object pepona {
    var property position = game.at(3,3)
    method image() = "pepona.png"
    method layer() = pepita.layer().front(1) // imitando position.left(1)
}

program {
     game.addVisual(pepona)
     game.addVisual(pepita)
     // y pepona se muestra encima de pepita
}

/*
game entendería:
game.backLayer()
game.frontLayer()
game.defaultLayer()
game.layer(n) // devuelve el layer correspondiente

assert.equals(game.layer(-100), game.backLayer()) // para que se entienda que puede ser negativo, y para que quede por detrás del default (0)
assert.equals(game.layer(100), game.frontLayer()) 
assert.equals(game.layer(0), game.defaultLayer()) 

y los Layer:
layer.front(n) // La layer n niveles arriba de esta
layer.behind(n) // La layer n niveles abajo de esta
layer.number() // El número de layer

*/

¿Estamos segures que no vamos a tener el mismo problema (el mismo gran problema) de performance que tenemos hoy con las Position? ¿No queremos pensarlo optimizado? (Pasó el fantasma de Knuth a callarme y no lo logró)

asanzo commented 3 years ago

Ahí agregué el método defaultLayer al ejemplo de arriba.

Igual no me queda claro que hayamos discutido / descartado alternativas como:

  1. Que lo que devuelva el método layer() sea un número y listo. No sobrediseñar, no tener la clase Layer.
  2. Sí tener la clase Layer, pero no tener el método layer(). Que el Layer sea una cosa donde ponemos los objetos, similar al método deprecado "game.addVisualIn". game.frontLayer().addVisual(pepita), o algo así.

Para decidir podemos chusmear algún juego complejo ¿EscapeBoss? no sé bien. O bien este draft isométrico que hice, que juega bastante con los layers.

jugandoconZ

nscarcella commented 3 years ago

Reificar el layer va a ser una buena idea en la medida en que le pongas comportamiento. Sí el plan es sólo compararlos me parece mejor dejarlo como números, porque eso también permite eventualmente usar el layer como parte del algebra de algún juego (por ejemplo, en un scroller horizontal podés decidir que un tiro impacta si el bicho está a cierta distancia en los layers). Obviamente podés modelar eso con clases, pero no sé para qué reinventar los números. Si tenés una interfaz más concreta es otra historia.

De todos modos, no me preocuparía por la performance porque, salvo que hagas algo muy hijo de puta, la cantidad de layers que podés necesitar es limitada y pueden ser pooleados.

A mi la variante game.frontLayer().addVisual(pepita) no me gusta. Sugiere una relación de conocimiento del layer al objeto y creo que eso no está alineado con la implementación. Qué pasa si hacés:

game.frontLayer().addVisual(pepita)
game.backLayer().addVisual(pepita)

La cambiás de layer? La agregás dos veces con dos layers distintos? Cómo se lleva eso con la idea de que el visual está en un sólo layer?

Si me apurás, tampoco me gusta que los layers estén en el objeto game. Está quedando un God-Object super cargado al pedo. Podría solamente ser:

object layer {
  method default()
  method front()
  method back()
  ...
}

Y el que no necesita pensar en layers no lo ve constantemente en el autocompletar.

Por último, estoy seguro de que tu ejemplo isométrico sería mucho más claro y declarativo si los objetos tuvieran un método layer, porque podrías calcularlo a partir de su posición en un método hermoso en lugar de estar pisándolo cada vez que movés tu pieza, pero... Eso SEGURO tiene un impacto en performance. Pensá que el layer determina el orden de dibujado y, por consiguiente, el orden en el que hay que recorrer los elementos para dibujarlos. Hacer un sort en cada iteración sólo por si se te da por cambiar el layer es bastante espeso. Entonces vos querés que el cambio de layer sea explicito, a través de un punto que puedas controlar desde el motor (onda game.moveToLayer(1, pepita).

Si eso no te cierra, hay técnicas para lidiar con eso como, por ejemplo, renderizar cada layer en cualquier orden a una "imagen" diferente en memoria y luego "sumarlas" antes de dibujar en pantalla. Eso te ahorra tiempo pero consume memoria.

Otra es tener un observer que reacomoda los elementos cuando un cambia de layer, pero lo veo difícil de hacer transparente a los pibes (sobre todo si no podemos contar con que sus elementos heredan de un lugar).

No hay una respuesta fácil.

Yo cortaría por lo sano y haría que los componentes se creen en un layer por default (ponele, 0) y tendría un mensaje destructivo que explicitamente los mueva, cosa de poder indexar por layer a los objetos.

asanzo commented 3 years ago

Qué paja. Bueno, como digan.

npasserini commented 3 years ago

Reificar el layer... no sé, tal vez es cuestión de estilo, yo lo haría. No creo que cambie mucho la performance, porque los números también son objetos y entienden mensajes. Salvo que tengas una lógica loca de jitteado de los números para poder hacer cuentas nativas, evaluar 2 + 2 en wollok implica construir dos WollokObjects, hacer un message send, luego llamar a la operación nativa haciendo el unboxing y finalmente reboxear el resultado. Hacerlo con layers no mueve mucho la aguja. No sé decir si la implementación ts es más inteligente que todo eso.

Igual es sutil la discusión entre objeto específico y número, me banco la decisión de la mayoría.

Objeto layer independiente... creo que tiene sentido, en la medida en que garanticemos que no hay que agregar ooootro import, porque paja (bueno, tampoco es un criterio excluyente si es UN import, pero si va a ser criterio para más cosas en el futuro, conviene pensarlo).

Me pregunto si no sería piola que el nombre fuera "layers" en plural... o alguna otra cosa, como "keyboard" me da las keys, la dualidad "layer" / "Layer" me parece que puede confundir.

Versión imperativa esto cambia un poco la forma declarativa/funcional en la que se renderizan los componentes de Wollok, así que hay que ser cuidadosos con esta decisión... pero escucharía a los que tengan más experiencia haciendo juegos. Si están convencidos de que el layer un elemento que realmente no suele cambiar, entonces puede ser razonable tener un game.addElement sobrecargado para pasar un layer por parámetro.

Un detalle más es que si los objetos dejan de saber su layer, tal vez sea piola poder preguntárselo a alguien más, reemplazando el pepita.layer() por layers.of(pepita) o alguna cosa similar dependiendo de cómo se defina la obtención de layers (esto es necesario aún si los layers son números, necesito poder consultar en cuál está pepita... supongo).

On Thu, Dec 17, 2020 at 10:40 AM asanzo notifications@github.com wrote:

Qué paja. Bueno, como digan.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/uqbar-project/wollok/issues/1319#issuecomment-747445112, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABDLKOICZBD5KO6G7P5LGDTSVIC6RANCNFSM4EIUXBNA .