Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feedback #1

Open
wants to merge 56 commits into
base: feedback
Choose a base branch
from
Open

Feedback #1

wants to merge 56 commits into from

Conversation

github-classroom[bot]
Copy link

@github-classroom github-classroom bot commented Sep 27, 2024

👋! GitHub Classroom created this pull request as a place for your teacher to leave feedback on your work. It will update automatically. Don’t close or merge this pull request, unless you’re instructed to do so by your teacher.
In this pull request, your teacher can leave comments and feedback on your code. Click the Subscribe button to be notified if that happens.
Click the Files changed or Commits tab to see all of the changes pushed to the default branch since the assignment started. Your teacher can see this too.

Notes for teachers

Use this PR to leave feedback. Here are some tips:

  • Click the Files changed tab to see all of the changes pushed to the default branch since the assignment started. To leave comments on specific lines of code, put your cursor over a line of code and click the blue + (plus sign). To learn more about comments, read “Commenting on a pull request”.
  • Click the Commits tab to see the commits pushed to the default branch. Click a commit to see specific changes.
  • If you turned on autograding, then click the Checks tab to see the results.
  • This page is an overview. It shows commits, line comments, and general comments. You can leave a general comment below.
    For more information about this pull request, read “Leaving assignment feedback in GitHub”.

Subscribed: @Lucard02 @MaxiAlexVargas @gonzgrillo @joaquin-burgio @FranBarroso2

Copy link

@tfloxolodeiro tfloxolodeiro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aca les dejo las correcciones del TP. Corrijan el comentario que puse y sigan con estas ideas:

  • Agregar un powerup que haga inmune al dino por unos segundos. Si el dino salta mientras que está inmune, pierde la inmunidad.
  • Agregar un powerup que permita al dino hacer doble salto por unos segundos.
  • El dino no puede estar inmune y poder hacer doble salto al mismo tiempo. Si el dino esta inmune y agarra un power up de doble salto, el "estado" de doble salto pisa la inmunidad, y viceversa.
  • Cambiar la forma en la que aparecen obstaculos/powerups/monedas en la pantalla. Hacer que cada 1 segundo se decida aleatoriamente cual va a ser el proximo elemento en aparecer en game.at(game.width()-1,0). Puede ser un cactus, una moneda, alguno de los power ups o nada.
    • Esto significa que puede haber cualquier cantidad de un cierto elemento en la pantalla. Osea que, por ejemplo, pueden llegar a haber 5 o mas cactuses en la pantalla en un determinado momento, y asi con cualquier de los otros elementos.
    • Si se decide crear un powerup, tambien se tiene que decidir aleatoreamente en que altura crearlo. Un powerup puede estar en el piso o en el aire, de forma tal que el dino tenga que saltar para agarrarlo. Los cactuses y monedas siempre se generan en el piso.

Estas ideas, que parecen ser rebuscadas y hechas para molestar, estan pensadas para llevarlos a un problema de modelado que se resuelve bien con un patron de diseño que suele aparecer en los parciales, y que es comun usarlo al programar con objetos en general.

La forma de resolver estas cosas no es obvia, asi que es posible que se traben o lleguen a una solucion que no les guste. Por esto, vayan pensando en como resolverlo y codeando lo que se les ocurra y consulten por el discord.

Como me demore 1 semana en corregirlo, no es necesario que tengan todo esto resuelto perfectamente para la siguiente entrega, pero dediquenle un tiempo para tratar de hacerlo.

example.wlk Outdated Show resolved Hide resolved
Copy link

@tfloxolodeiro tfloxolodeiro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acá les dejo las correccion del juego hasta ahora. Son correcciones que les va a servir para mejorar el juego, pero principalmente les va a servir para entender mejor la forma de modelar con objetos, lo cual no es trivial.

example.wlk Outdated
Comment on lines 1 to 2
const objetosRasos = ["banana.png"]
const objetosConAltura = ["pera.png","moneda.png"]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aca estamos usando el string de imagen para identificar que objeto queremos poner.
Un problema con esto es que si cambiamos el nombre de la imagen vamos a tener que cambiarlo aca, y en todos los lugares donde usemos la imagen como identificador, como aca:

  override method teChocoElDino(objetoChocado) {
    if(objetoChocado=="moneda.png") dinosaurio.sumarPtos()
    if(objetoChocado=="pera.png") dinosaurio.otraCosa()
  }

Incluso si no usamos la imagen como identificador, y usamos strings genericos como "banana", "pera" y "moneda", esto tambien tiene problemas, en especial a medida que vamos incrementando la cantidad de objetos identificados que tenemos en el sistema.
Un problema es que tenemos que recordar que strings identificadores existen y como estan escritos. Otro problema es que es dificil hacer modificaciones sobre el codigo. Por ejemplo, si quisiesemos sacar la banana, tenemos que recordar que tenemos que sacar el "banana.png" de aca y del metodo teChocoElDino().

example.wlk Outdated
Comment on lines 23 to 31

method aparecer(objeto) {
if( objeto != null ) {
const objetoCreado = if (objetosRasos.contains(objeto)) self.crearObjetoRaso(objeto)
else self.crearObjetoConAltura(objeto)

self.apareceYMovete(objetoCreado)
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Un problema con esta solucion es que no es escalable si queremos agregar mas tipos de objetos. Ahora solo tenemos objetos con alturas y objetos rasos, pero si mañana queremos agregar 4 tipos mas de objetos, vamos a tener que agregar 4 ifs mas, lo cual hace mas dificil de mantener el codigo.

example.wlk Outdated
Comment on lines 65 to 80

class ObjetoRaso {
var property position = game.at(game.width() - 1, self.posY())
var property image

method posY() = 0

method teChocoElDino(_) {
game.stop()
}
// si el dino choca con un objeto que va x el suelo el juego termina

method desplazate(n) {
position = position.left(n)
}
}
Copy link

@tfloxolodeiro tfloxolodeiro Nov 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Entendiendo ObjetoRaso como un objeto que pasa por el piso, no es correcto que el juego termine si el dino lo toca, porque un objeto raso podria ser un powerup.

example.wlk Outdated
Comment on lines 82 to 91
class ObjetoConAltura inherits ObjetoRaso {
override method posY() = 4
// cambiar 4 por valor al azar

override method teChocoElDino(objetoChocado) {
if(objetoChocado=="moneda.png") dinosaurio.sumarPtos()
if(objetoChocado=="pera.png") dinosaurio.otraCosa()
}
// si el dino choca con un objeto que va x el aire pueden pasar varias cosas
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No es muy correcto que ObjetoConAltura herede de ObjetoRaso, porque implica que los objetos con altura son objetos rasos.
La herencia es una relacion fuerte, una relacion de "Subclase hereda de Clase" tambien se puede leer como "Subclase es Clase". Por ejemplo, una herencia tipica es "Gato hereda de Mamifero, el cual hereda de Animal", lo cual es lo mismo que "Gato es un Mamifero, el cual es un Animal". Entonces, "class ObjetoConAltura inherits ObjetoRaso" tambien se puede leer como "Los objetos con altura son objetos rasos", lo cual es semanticamente contradictorio. Una razon por la que una herencia es una relacion de "X es Y", es porque la subclase va a heredar todo lo que tenga la clase padre. Osea, que toda la logica que a futuro tenga ObjetoRaso tambien la va a tener ObjetoConAltura, a menos que le hagamos override, lo cual tal vez no queremos que pase. Si mantenemos esta herencia, posiblemente terminemos overrideando casi todos los metodos de la clase padre, por lo que no tiene sentido la herencia.
Es por todo esto que, en vez de heredar de ObjetoRaso, deberiamos identificar que logica comparten ObjetoConAltura y ObjetoRaso y moverla a una clase de la cual hereden ambos. De esta forma, estas clases terminan siendo "hermanas", porque heredan de un mismo padre.

Por otra parte, como mencione antes, en teChocoElDino no es muy mantenible este tipo de soluciones, y podemos hacer mejores soluciones aprovechando el paradigma de objetos.

Si queremos mantener la clase ObjetoConAltura, seria mas conveniente hacer algo como:

class ObjetoConAltura inherits ObjetoRaso {
  override method posY() = 4

  override method teChocoElDino()

}

class Moneda inherits ObjetoConAltura {
  override method teChocoElDino() { dinosaurio.sumarPtos() }
}

class Pera inherits ObjetoConAltura {
  override method teChocoElDino() { dinosaurio.otraCosa() }
}

De esta forma, damos entidad a las monedas y las peras, lo cual nos permite a futuro agregarles logica especifica a ellas mas facilmente.

A pesar de toda esta correccion larga, es posible que no termine habiendo una clase ObjetoConAltura u ObjetoRaso y que la solucion a la que vayamos sea distinta a la que mostre en este ejemplo, pero era importante hacer la aclaracion inicial para poder entender mejor la herencia.

example.wlk Outdated
Comment on lines 23 to 31

method aparecer(objeto) {
if( objeto != null ) {
const objetoCreado = if (objetosRasos.contains(objeto)) self.crearObjetoRaso(objeto)
else self.crearObjetoConAltura(objeto)

self.apareceYMovete(objetoCreado)
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despues de todas estas correcciones, queda la duda de como implementar este metodo, porque no podemos usar strings para identificar que objetos poner, no podemos poner ifs dependiendo del tipo e implique que tampoco son necesarias las clases ObjetoConAltura y ObjetoRaso.

Una forma de resolverlo es hacer objetos que se encarguen de la generacion de los items que se muestran en la pantalla (por no decir "objetos", lo cual puede confundir en este contexto). Osea, en vez de tener una lista

const objetos = objetosRasos + objetosConAltura + [null]

Podriamos tener una lista de generadores, y en el onTick de "aparecer objetos" hacer:

    game.onTick(
      1000,
      "aparecerObjeto",
      { generadores.anyOne().generar() }
    )

Todo esto puede parecer abstracto, pero la implementacion es simple. Por ejemplo, podemos tener un generador de monedas con esta posible implementación:

object generadorDeMonedas {
    
    method posicionDeInicio() = game.at(game.width() - 1, 0)

    method generar(){
        const nuevaMoneda = new Moneda(self.posicionDeInicio()) 
// la posicion de inicio podria definirse dentro de la Moneda, y no aca en el generador.
// Pero lo puse aca para mostrar que tienen un control 
// mas comodo sobre la generacion de items, comparado a la solucion anterior
        game.addVisual(nuevaMoneda)
    }
}

Y definir la lista de generadores que mencione antes como:

const generadores = [generadorDeMonedas, generadorDePeras, generadorDeCactuses, generadorDeOtraCosas ] //todos los generadores que se desee

De esta forma, queda mas simple porque no necesitamos el identificador string de objeto, no necesitamos el metodo aparecer() con varios ifs y tenemos objetos con la logica de generacion encapsulada dentro de ellos, lo cual hace muy simple agregar nuevos generadores, porque solo hay que crear un objeto generador y agregarlo a la lista de generadores.

Por otra parte, tener generadores les vá a permitir hacer mas facilmente lo que les plantie con "Si se decide crear un powerup, tambien se tiene que decidir aleatoreamente en que altura crearlo", lo cual puede hacer innecesaria la existencia de ObjetoRaso y ObjetoConAltura.

La solucion que presenté es un esboce inicial de como se puede ver esta idea de generadores, luego ustedes tendran que ver como adaptarla si es que se encuentran con problemas, como de tener logica repetida entre generadores.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants