2048 clon en libgdx, manejo de activos y remodelación de OOP? -- java campo con object-oriented campo con file campo con libgdx campo con 2048 camp codereview Relacionados El problema

2048 clone in libgdx, handling assets, and OOP overkill?


4
vote

problema

Español

He creado un clon de 2048, quería asumir un proyecto simple ya que nunca he terminado nada. Ahora tengo, aunque no soy un cien por ciento feliz con el resultado.

Imágenes para que sepas lo que estamos discutiendo:

ingrese la descripción de la imagen aquí  Enter Descripción de la imagen aquí

Mis preocupaciones

  • de manejo de activos

Este es el primer proyecto en el que uso la clase AssetManager. He manejado activos al darle al astillero a cada objeto en su constructor, ¿es así como se supone que debo hacer esto? Además, utilizo algunos tamaños de fuente diferentes, así que tuve que hacer esto:

  FileHandleResolver resolver = new InternalFileHandleResolver();     assetManager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));     assetManager.setLoader(BitmapFont.class, ".ttf", new FreetypeFontLoader(resolver));      FreetypeFontLoader.FreeTypeFontLoaderParameter font = new FreetypeFontLoader.FreeTypeFontLoaderParameter();     font.fontFileName = "ClearSans-Bold.ttf";     font.fontParameters.size = HEIGHT / 10;     assetManager.load("mediumFont.ttf", BitmapFont.class, font);      FreetypeFontLoader.FreeTypeFontLoaderParameter font2 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();     font2.fontFileName = "ClearSans-Bold.ttf";     font2.fontParameters.size = HEIGHT / 6;     assetManager.load("bigFont.ttf", BitmapFont.class, font2);      FreetypeFontLoader.FreeTypeFontLoaderParameter font3 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();     font3.fontFileName = "ClearSans-Regular.ttf";     font3.fontParameters.size = HEIGHT / 30;     assetManager.load("smallFont.ttf", BitmapFont.class, font3);   

etc.

¿Hay una mejor manera de manejar muchos tamaños de fuente diferentes? Ya hago esto por el tamaño de los números en los azulejos:

  void setFontSize() {     BitmapFont tileFont = assetManager.get("tileFont.ttf", BitmapFont.class);     tileFont.getData().setScale(2.0f / BOARD_SIZE);      BitmapFont smallTileFont = assetManager.get("smallTileFont.ttf", BitmapFont.class);     smallTileFont.getData().setScale(2.0f / BOARD_SIZE); }   
  • constantes de manejo

Tengo algunas constantes como el color de ciertos elementos de texto, ¿debo crear una nueva clase y crear objetos de color en ellos?

  • Pregunta OOP

He creado nuevas clases para una pantalla de pérdida y ganar pantalla. Por supuesto, ambos solo necesitan ser insistidos una vez, y siempre se parecen iguales, ¿no debería haber usado OOP para esto? Aquí está la clase de pérdida y cómo lo muestro:

PERSCREEN.JAVA

  class LossScreen extends Group { private BitmapFont mediumFont, smallFont; private GlyphLayout layout = new GlyphLayout(); private Texture backgroundTexture, buttonTexture; private Rectangle tryAgainRect; private int zIndex = 0;  LossScreen (int x, int y, AssetManager assetManager) {     setBounds(x, y, TotallyNot2048.WIDTH, TotallyNot2048.HEIGHT);      smallFont = assetManager.get("smallBoltFont.ttf", BitmapFont.class);     mediumFont = assetManager.get("mediumFont.ttf", BitmapFont.class);      backgroundTexture = Helper.createRoundedRectangleTexture(getWidth(), getHeight(), new Color(200f / 256f, 200f / 256f, 200f / 256f, .8f), 15);      layout.setText(smallFont, "Try again");     buttonTexture = Helper.createRoundedRectangleTexture(layout.width * 1.5f, layout.height * 3f, new Color(143f / 256f, 122f / 256f, 102f / 256f, 1f), 3);     tryAgainRect = new Rectangle(getX() + getWidth() / 2 - buttonTexture.getWidth() / 2, getY() + getHeight() / 3, layout.width * 1.5f, layout.height * 3f);      listenForInput(); }  private void listenForInput() {     addListener(new InputListener() {          @Override         public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {             if (pointer == Input.Buttons.LEFT)                 if (tryAgainRect.contains(TotallyNot2048.getMousePosInGame().x, TotallyNot2048.getMousePosInGame().y))                     ((Board) getParent()).restartGame();             return true;         }     }); }  public void setZIndex(int zIndex) {     this.zIndex = zIndex; }  public int getZIndex() {     return zIndex; }  @Override public void draw(Batch batch, float parentAlpha) {     super.draw(batch, parentAlpha);      batch.setColor(getColor().r, getColor().g, getColor().b, getColor().a * parentAlpha);     batch.draw(backgroundTexture, 0, 0);     layout.setText(mediumFont, "Game over!");     mediumFont.setColor(119f / 256f, 110f / 256f, 101f/ 256f, mediumFont.getColor().a);     mediumFont.getColor().a = getColor().a;     mediumFont.draw(batch, "Game over!", getWidth() / 2 - layout.width / 2, getHeight() / 2 + layout.height / 2);     mediumFont.getColor().a = 1f;      batch.draw(buttonTexture, getWidth() / 2 - buttonTexture.getWidth() / 2, getHeight() / 3);     layout.setText(smallFont, "Try again");     smallFont.setColor(new Color(245f / 256f, 241f / 256f, 237f / 256f, 1f * getColor().a));     smallFont.draw(batch, "Try again", getWidth() / 2 - layout.width / 2, getHeight() / 3 + layout.height * 2);     batch.setColor(getColor().r, getColor().g, getColor().b, 1f); }  @Override public void act(float delta) {     super.act(delta); } }   

en "board.java", verifico si otros movimientos están disponibles, de forma no, se llama este código y una variable "islost" se establece en verdadero que evita la entrada del usuario, "PassScreen" es un actor que al momento de la configuración visible Llama automáticamente al método de sorteo en la clase de pérdida de pérdida:

  private void showLossScreen() {     lossScreen.setVisible(true);     lossScreen.addAction(Actions.fadeIn(.4f)); }   
  • lógica del juego

Esta es la lógica del juego que uso para mover las baldosas, "ISEXISTAS ()" comprueba si la baldosa no está a punto de ser eliminada (todavía está presente porque debe mostrar una animación), "SetMoveParameters ()" Da datos a un "azulejo" que será utilizado por el azulejo para mostrar una animación. "Azulejos []" es una matriz de objetos de azulejos (el azulejo extiende el actor). Además de GenericMove y GenericUpGrade, tengo cuatro métodos para cada dirección de movimiento:

      private boolean genericMove(int i, int j, int move_x, int move_y) {     if ((tiles[i + j] != null && !tiles[i + j].isExists()) || tiles[i + j] == null) {         tiles[i + j] = new Tile(tiles[i], this);         addActor(tiles[i + j]);         tiles[i + j].setMoveParameters(move_x, move_y, tiles[i].getValue(), false);         removeActor(tiles[i]);         tiles[i].dispose();         tiles[i] = null;         return true;     }     return false; }  private boolean genericUpgradeMove(int i, int j, int move_x, int move_y) {     if (tiles[i + j] != null && tiles[i + j].isExists() && tiles[i] != null) {         if (tiles[i + j].getValue() == tiles[i].getValue() && !tiles[i + j].isUpgradedThisTurn() && !tiles[i].isUpgradedThisTurn()) {             updateScore(tiles[i + j].getValue() * 2);             tiles[i + j].setMoveParameters(0, 0, tiles[i + j].getValue() * 2, false);             Tile tile = new Tile(tiles[i], this);             tempTiles.add(tile);             tile.setMoveParameters(move_x, move_y, tiles[i].getValue(), true);             addActor(tile);             removeActor(tiles[i]);             tiles[i].dispose();             tiles[i] = null;             return true;         }     }     return false; }  private boolean moveRight() {     boolean tileMoved = false;     for (int i = tiles.length - 2; i >= 0; i--) {         if (tiles[i] != null && tiles[i].isExists()) {             if ((i + 1) % BOARD_SIZE != 0) {                 tileMoved = genericMove(i, 1, MOVE_DISTANCE, 0);                 if (tileMoved) {                     moveRight();                     break;                 }                  tileMoved = genericUpgradeMove(i, 1, MOVE_DISTANCE, 0);                 if (tileMoved) {                     moveRight();                     break;                 }             }         }     }     return tileMoved; }   

Estos métodos están precedidos por uno de los cuatro métodos para cada dirección como:

  private boolean tryMoveRight() {     pause();     updateIndices();     if (moveRight()) {         updateTileGraphics();         addNewTile();         checkState();         return true;     } else {         resume();         return false;     } }   

"Pausa ()" no hace que se tome una nueva entrada de usuario, el curriculum vitae reanuda la entrada del usuario, los informes () se asegura de que todos los azulejos estén en su posición correcta, por lo que si lo hicieron, las instrucciones de mudanza son nuevas. , el azulejo se coloca por primera vez en la posición en la que debería haber sido para empezar. "UpdateTileLeGraphics ()" usa los datos dados por "setmoveParameters ()" para la mosaica de cada uno. "CheckState ()" comprueba si el juego se pierde o se gana. "Movimiento ()" devuelve verdadero o falso dependiendo de si una o más fichas pudieron moverse / deslizarse entre sí.

  • hacer una entrada más rápida posible

Actualmente, cuando las baldosas siguen mostrando animaciones, no se acepta una nueva entrada, esto resulta en no poder jugar el juego realmente rápido, no he descubierto una manera de arreglar esto sin hacer que las animaciones realmente sean rápidamente.

Estas son mis preguntas principales, por supuesto, sus son muchas más cosas que no estoy seguro de si usé las mejores prácticas, pero no sé si alguien está interesado en revisar el proyecto completo.

Original en ingles

I have created a 2048 clone, I wanted to take on a simple project since I have never actually finished anything. Now I have, though I'm not a hundred percent happy with the result.

Images so you know what we're discussing:

enter image description hereenter image description here

My concerns

  • Handling assets

This is the first project in which I use the AssetManager class. I have handled assets by giving the assetManager to each object in its constructor, is this how I'm supposed to do this? Also, I use a few different font sizes, so I had to do this:

FileHandleResolver resolver = new InternalFileHandleResolver();     assetManager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));     assetManager.setLoader(BitmapFont.class, ".ttf", new FreetypeFontLoader(resolver));      FreetypeFontLoader.FreeTypeFontLoaderParameter font = new FreetypeFontLoader.FreeTypeFontLoaderParameter();     font.fontFileName = "ClearSans-Bold.ttf";     font.fontParameters.size = HEIGHT / 10;     assetManager.load("mediumFont.ttf", BitmapFont.class, font);      FreetypeFontLoader.FreeTypeFontLoaderParameter font2 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();     font2.fontFileName = "ClearSans-Bold.ttf";     font2.fontParameters.size = HEIGHT / 6;     assetManager.load("bigFont.ttf", BitmapFont.class, font2);      FreetypeFontLoader.FreeTypeFontLoaderParameter font3 = new FreetypeFontLoader.FreeTypeFontLoaderParameter();     font3.fontFileName = "ClearSans-Regular.ttf";     font3.fontParameters.size = HEIGHT / 30;     assetManager.load("smallFont.ttf", BitmapFont.class, font3); 

etc.

Is there a better way to handle many different font sizes? I already do this for the size of the numbers on the tiles:

void setFontSize() {     BitmapFont tileFont = assetManager.get("tileFont.ttf", BitmapFont.class);     tileFont.getData().setScale(2.0f / BOARD_SIZE);      BitmapFont smallTileFont = assetManager.get("smallTileFont.ttf", BitmapFont.class);     smallTileFont.getData().setScale(2.0f / BOARD_SIZE); } 
  • Handling constants

I have a few constants like the color of certain text elements, should I create a new class and create color objects in them?

  • OOP question

I have created new classes for a loss screen and win screen. They of course both only need to be instanced once, and they always look the same, should I not have used OOP for this? Here is the loss class and how I show it:

LossScreen.java

class LossScreen extends Group { private BitmapFont mediumFont, smallFont; private GlyphLayout layout = new GlyphLayout(); private Texture backgroundTexture, buttonTexture; private Rectangle tryAgainRect; private int zIndex = 0;  LossScreen (int x, int y, AssetManager assetManager) {     setBounds(x, y, TotallyNot2048.WIDTH, TotallyNot2048.HEIGHT);      smallFont = assetManager.get("smallBoltFont.ttf", BitmapFont.class);     mediumFont = assetManager.get("mediumFont.ttf", BitmapFont.class);      backgroundTexture = Helper.createRoundedRectangleTexture(getWidth(), getHeight(), new Color(200f / 256f, 200f / 256f, 200f / 256f, .8f), 15);      layout.setText(smallFont, "Try again");     buttonTexture = Helper.createRoundedRectangleTexture(layout.width * 1.5f, layout.height * 3f, new Color(143f / 256f, 122f / 256f, 102f / 256f, 1f), 3);     tryAgainRect = new Rectangle(getX() + getWidth() / 2 - buttonTexture.getWidth() / 2, getY() + getHeight() / 3, layout.width * 1.5f, layout.height * 3f);      listenForInput(); }  private void listenForInput() {     addListener(new InputListener() {          @Override         public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {             if (pointer == Input.Buttons.LEFT)                 if (tryAgainRect.contains(TotallyNot2048.getMousePosInGame().x, TotallyNot2048.getMousePosInGame().y))                     ((Board) getParent()).restartGame();             return true;         }     }); }  public void setZIndex(int zIndex) {     this.zIndex = zIndex; }  public int getZIndex() {     return zIndex; }  @Override public void draw(Batch batch, float parentAlpha) {     super.draw(batch, parentAlpha);      batch.setColor(getColor().r, getColor().g, getColor().b, getColor().a * parentAlpha);     batch.draw(backgroundTexture, 0, 0);     layout.setText(mediumFont, "Game over!");     mediumFont.setColor(119f / 256f, 110f / 256f, 101f/ 256f, mediumFont.getColor().a);     mediumFont.getColor().a = getColor().a;     mediumFont.draw(batch, "Game over!", getWidth() / 2 - layout.width / 2, getHeight() / 2 + layout.height / 2);     mediumFont.getColor().a = 1f;      batch.draw(buttonTexture, getWidth() / 2 - buttonTexture.getWidth() / 2, getHeight() / 3);     layout.setText(smallFont, "Try again");     smallFont.setColor(new Color(245f / 256f, 241f / 256f, 237f / 256f, 1f * getColor().a));     smallFont.draw(batch, "Try again", getWidth() / 2 - layout.width / 2, getHeight() / 3 + layout.height * 2);     batch.setColor(getColor().r, getColor().g, getColor().b, 1f); }  @Override public void act(float delta) {     super.act(delta); } } 

In "board.java" I check whether other moves are available, if not, this code is called and an "isLost" variable is set to true which prevents user input, "lossScreen" is an actor that upon setting to visible automatically calls the draw method in the LossScreen class:

private void showLossScreen() {     lossScreen.setVisible(true);     lossScreen.addAction(Actions.fadeIn(.4f)); } 
  • Game logic

This is the game logic I use for moving the tiles, "isExists()" checks if the tile is not already about to be deleted (it is still present because it needs to show an animation), "setMoveParameters()" gives data to a "Tile" that will be used by the tile to show an animation. "tiles[]" is an array of Tile objects (Tile extends Actor). Besides genericMove and genericUpgrade, I have four methods for each move direction:

    private boolean genericMove(int i, int j, int move_x, int move_y) {     if ((tiles[i + j] != null && !tiles[i + j].isExists()) || tiles[i + j] == null) {         tiles[i + j] = new Tile(tiles[i], this);         addActor(tiles[i + j]);         tiles[i + j].setMoveParameters(move_x, move_y, tiles[i].getValue(), false);         removeActor(tiles[i]);         tiles[i].dispose();         tiles[i] = null;         return true;     }     return false; }  private boolean genericUpgradeMove(int i, int j, int move_x, int move_y) {     if (tiles[i + j] != null && tiles[i + j].isExists() && tiles[i] != null) {         if (tiles[i + j].getValue() == tiles[i].getValue() && !tiles[i + j].isUpgradedThisTurn() && !tiles[i].isUpgradedThisTurn()) {             updateScore(tiles[i + j].getValue() * 2);             tiles[i + j].setMoveParameters(0, 0, tiles[i + j].getValue() * 2, false);             Tile tile = new Tile(tiles[i], this);             tempTiles.add(tile);             tile.setMoveParameters(move_x, move_y, tiles[i].getValue(), true);             addActor(tile);             removeActor(tiles[i]);             tiles[i].dispose();             tiles[i] = null;             return true;         }     }     return false; }  private boolean moveRight() {     boolean tileMoved = false;     for (int i = tiles.length - 2; i >= 0; i--) {         if (tiles[i] != null && tiles[i].isExists()) {             if ((i + 1) % BOARD_SIZE != 0) {                 tileMoved = genericMove(i, 1, MOVE_DISTANCE, 0);                 if (tileMoved) {                     moveRight();                     break;                 }                  tileMoved = genericUpgradeMove(i, 1, MOVE_DISTANCE, 0);                 if (tileMoved) {                     moveRight();                     break;                 }             }         }     }     return tileMoved; } 

These methods are preceded by one of four methods for each direction like:

private boolean tryMoveRight() {     pause();     updateIndices();     if (moveRight()) {         updateTileGraphics();         addNewTile();         checkState();         return true;     } else {         resume();         return false;     } } 

"pause()" causes no new user input to be taken in, resume resumes user input, updateIndices() makes sure all tiles are in their right position, so if their did happen to be new move instructions given by the user, the tile is first put into the position it should have been to begin with. "updateTileGraphics()" uses the data given by "setMoveParameters()" for eachs Tile. "checkState()" checks if the game is lost or won. "moveUp()" returns true or false depending on whether one or more tiles were able to move / slide into each other.

  • Making faster input possible

Currently, when the tiles are still showing animations, no new input is accepted, this results in not being able to play the game really fast, I haven't figured out a way to fix this without making the animations really fast.

These are my main questions, of course their are much more things I'm not certain of whether I used best practices, but I don't know if anyone is interested in reviewing the entire project.

              

Lista de respuestas

3
 
vote
vote
La mejor respuesta
 
  • Pregunta OOP

    He creado nuevas clases para una pantalla de pérdida y ganar pantalla. Por supuesto, ambos solo necesitan ser insistidos una vez, y siempre se parecen iguales, ¿no debería haber usado OOP para esto?

Al codificar en Java, debe siempre usar OOP.

Pero OOP no significa dividir el código en clases aleatorias. Haciendo OOP significa que sigue ciertos principios que se encuentran (entre otros):

  • información de información / encapsulación
  • Responsabilidad única / Separación de preocupaciones
  • mismo nivel de abstracción
  • prefiero el polimorfismo sobre la ramificación
  • beso (Manténgalo simple (y) estúpido)
  • seco (no te repitas)
  • "¡Dígales! No preguntes".
  • Ley de Demeter ("¡No hables con los extraños!")

En Java, el concepto de clases admite estos principios, pero OOP no está vinculado a las clases (excepto el polimorfismo).


Usted crea clases cuando hay diferentes comportamientos , donde el comportamiento significa comunicación con dependencias (también conocido como métodos de llamada en otros objetos) o cálculos .

Sus clases para una pantalla de pérdida y una pantalla de ganar pueden tener un comportamiento diferente (es decir, agregar diferentes componentes a la pantalla). Luego, tener clases separadas estaría bien.

Si simplemente muestran diferentes cadenas en diferentes colores / tamaños, debe haber una sola clase con un constructor parametrizado.


  • lógica del juego

IMHO Hay un árbol Problemas principales con su código:

  1. naming pobre
  2. duplicación de código
  3. MÉTODOS CON EL RETURSO DE ESTADO Y Efectos secundarios.

Naming

Encontrar buenos nombres es la parte más difícil en la programación. Así que siempre tómese su tiempo para encontrar buenos nombres.

Aunque sigue al general convenciones de nombres de java sus nombres de identificadores Podría mejorarse de varias maneras. El hecho de que tenga que explicar sus métodos en prosa en su pregunta indica que.

  • Elija nombres del dominio de la solución de problemas, no del dominio de programación.

  • Los nombres de los métodos comienzan con un verbo .
    Los métodos son acciones en un objeto. Tener un verbo a medida que la primera parte mejora la legibilidad de su código en gran medida:
    toString()4 vs toString()5

  • toString()6 Variables y métodos que devuelven un booleano Debe estar prefijado con toString()7 o toString()8

  • No tememos ser verbosos.
    No hay penalización por nombres de identificadores largos. (Los IDES reales incluso le impiden escribirlos más de una vez a través de la finalización del código ...), así que resista a acortarlos artificialmente.

Duplicación de código

Usted tiene código similar para mover sus fichas a diferentes direcciones.

Al aplicar OOP, podría mover los cálculos diferentes en cada uno de los métodos en las clases que implementan la misma interfaz personalizada. Luego, solo tendrías un método haciendo los bucles que obtuvieran el tipo de interfaz como parámetro. Las diferencias para cada dirección se encapsularían en las clases de su propia implementación de esta interfaz (que podría llamarse toString()9 ).

Consulte esta respuesta para obtener más detalles: Versión simplificada de 2048 juego

De esta manera, usted lo haría a salvo 3/4 del "código similar".

Métodos con devolución de estado y Efectos secundarios

Los métodos deben devolver un valor (calculado) o cambiar el estado de un objeto. Tus métodos de "lógica" hacen ambos.

Parece que la razón principal de esto es que hace su lógica dos veces: primero para verificar la posibilidad y el segundo para hacer realmente el movimiento.

Podría elegir un enfoque diferente en el que hace el movimiento en una copia de la Junta y simplemente intercambie la placa "real" con la copia si el movimiento lo sucedió. En este escenario, el juego tendría una variable de miembro else0 :

  else1  

No hago cómo implementaría los diferentes para los bucles en un método parametrizado, algunos de los bucles son ascendiendo y otros descienden. â € "El wombat de codificación

La interfaz:

  else2  

Las direcciones como Clases interiores anónimos :

  else3  

Siguientes dos valores Acaba de adivinar, espero que vea la idea ...

  else4 < Código> 

  else5  

El método de bucle

  else6  

 
  • OOP question

    I have created new classes for a loss screen and win screen. They of course both only need to be instanced once, and they always look the same, should I not have used OOP for this?

When coding in Java you should always use OOP.

But OOP does not mean to split up code into random classes. Doing OOP means that you follow certain principles which are (among others):

  • information hiding / encapsulation
  • single responsibility / separation of concerns
  • same level of abstraction
  • prefer polymorphism over branching
  • KISS (Keep it simple (and) stupid)
  • DRY (Don't repeat yourself)
  • "Tell! Don't ask."
  • Law of demeter ("Don't talk to strangers!")

In Java the classes concept supports this principles but OOP is not bound to classes (except polymorphism).


You create classes when there is different behavior, where behavior means communication with dependencies (aka calling methods on other objects) or calculations.

Your classes for a loss screen and win screen might have different behavior (ie. adding different components to the screen). Then having separate classes would be OK.

If they just display different strings in different colors/sizes there should be only one class with a parametrized constructor.


  • Game logic

IMHO there a tree major problems with your code:

  1. poor naming
  2. code duplication
  3. methods with status return and side effects.

Naming

Finding good names is the hardest part in programming. So always take your time to find good names.

Although you follow the general Java Naming Conventions your identifier names could be improved in several ways. The fact that you have to explain your methods in prose in your question indicates that.

  • Choose names from the problem solution domain, not from the programming domain.

  • Let method names start with a verb.
    Methods are actions on an object. Having a verb as the first name part improves the readability of your code greatly:
    gameLogic.genericMove() vs gameLogic.move()

  • boolean variables and methods returning a boolean should be prefixed with is or has

  • Do not fear to be verbose.
    There is no penalty for long identifier names. (actual IDEs even prevent you from typing them more than once via code completion...) So resist to artificially shorten them.

code duplication

You have similar code to move your tiles into different directions.

By applying OOP you could move the differing calculations in each of that methods into classes implementing the same custom interface. Then you'd have only one method doing the loops getting the interface type as parameter. The differences for each direction would be encapsulated in classes of their own implementing this interface (which could be called Direction).

Please see this answer for more details: Simplified version of 2048 game

This way you would safe 3/4 of the "similar code".

Methods with status return and side effects

Methods should either return a (calculated) value or change the state of an object. Your "logic" methods do both.

Looks like the main reason for this is that you do your logic twice: first for checking the possibility and second for actually doing the move.

You could choose a different approach where you do the move in a copy of the board and simply exchange the "real" board with the copy if the move succeeded. In this scenario the game would have a member variable hasMoveSucceeded:

private boolean move(Direction direction) {     pause();     updateIndices();     moveInCopy(direction);     if (hasMoveSucceded) {         exchangeBoardWithCopy();         addNewTile();         checkState();         return true;     } else {         //  resume(); // maybe obsolete?         return false;     } } 

I do not how I would implement the different for loops in a parameterized method, some of the for loops are ascending and others are descending. xe2x80x93 The Coding Wombat

the interface:

interface Direction{   int startValue();   boolean hasNext(int loopIndex);   int next(int loopIndex);   int getX(int loopIndex);   int getY(int loopIndex); } 

the directions as anonymous inner classes:

Direction right = new Direction(){   @Override public int startValue(){     return tiles.length - 2;   }   @Override public int hasNext(int loopIndex){     return  loopIndex >= 0;       }   @Override public int next(int loopIndex){     return  loopIndex--;   }   @Override public int getX(int loopIndex){     return loopIndex;   }   @Override public int getY(int loopIndex){     return 1;   } } 

next two values just guessed, hope you see the idea...

Direction left = new Direction(){   @Override public int startValue(){     return 0;   }   @Override public int hasNext(int loopIndex){     return  loopIndex < tiles.length - 2;       }   @Override public int next(int loopIndex){     return  loopIndex++;   }   @Override public int getX(int loopIndex){     return loopIndex;   }   @Override public int getY(int loopIndex){     return 1;   } } 

Direction down = new Direction(){   @Override public int hasNext(int loopIndex){     return  loopIndex < tiles.length - 2;       }   @Override public int startValue(){     return 0;   }   @Override public int next(int loopIndex){     return  loopIndex++;   }   @Override public int getX(int loopIndex){     return 1;    }   @Override public int getY(int loopIndex){     return loopIndex;   } } 

the loop method

private void move(Direction direction) {     tileMoved = false;     for (int i = direction.startValue(); direction.hasNext(i); i=direction.next(i)) {         if (tiles[i] != null && tiles[i].isExists()) {             if ((i + 1) % BOARD_SIZE != 0) {                 tileMoved = genericMove(direction.getX(i), direction.getY(i), MOVE_DISTANCE, 0);                 if (tileMoved) {                     move(direction);                     break;                 }                  tileMoved = genericUpgradeMove(direction.getX(i), direction.getY(i), MOVE_DISTANCE, 0);                 if (tileMoved) {                     move(direction);                     break;                 }             }         }     } } 
 
 
       
       

Relacionados problema

2  2048 juego en Python 3  ( 2048 game in python 3 ) 
Escribí este juego de 2048 usando Python 3 y Pygame hace unas semanas. Ya que me estoy metiendo en CS este verano, me gustaría buscar alguna crítica sobre cóm...

9  Codefights: Juego 2048  ( Codefights game 2048 ) 
Descripción Es muy probable que esté familiarizado con el juego 2048. 2048 se juega en una cuadrícula gris 4 × 4 con azulejos numerados que se deslizan s...

4  C Función para mover y colapsar celdas en 2048 juego  ( C function to move and collapse cells in 2048 game ) 
Estoy implementando un clon de 2048 en C con SDL, y tengo la siguiente función para realizar un movimiento en un vector de columna / matriz help document2 ...

11  2048 juego escrito en Python  ( 2048 game written in python ) 
Esta es la primera vez que mi código se está revisando después de dos años de aprendizaje de Python como AutoDidact. Nunca he tomado un curso o una clase, ...

6  Implementación del juego de la consola 2048  ( 2048 console game implementation ) 
Así que me pidieron que hiciera un juego rápido de 2048 con C ++ pero usando solo cosas como matrices, estructuras, funciones, punteros sin usando vectore...

5  Juego de estilo 2048  ( 2048 style game ) 
He hecho una réplica del juego 2048. Funciona exactamente cómo debería funcionar el juego. Sin embargo, solo estaba buscando saber dónde podría haber limpiado...

8  2048 juego gráfico  ( 2048 graphic game ) 
Este juego está hecho para JFrame. Puedes controlar el uso de las flechas. Los números en las células son los grados del número 2. Es posible cambiar la posic...

5  2048 AI en Python 3  ( 2048 ai in python 3 ) 
Estoy trabajando en un AI 2048 y este es mi código hasta ahora. En el juego 2048, tiene una cuadrícula de 4x4 en ese aleatorio, así llamado Tiles Tiles Spew...

3  El juego 2048 usando c  ( The 2048 game using c ) 
He decidido hacer algo sobre mi codificación horrible y, por lo tanto, he hecho un caso de prueba para su revisión. No estoy seguro de cómo funciona este siti...

5  2048 juego de consola (C #)  ( 2048 console game c ) 
Así que hace una semana, decidí escribir una versión simple de consola de 2048 juego . Bueno, como verás, no salió tan simple ... y tomó mucho más tiempo y p...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos