【问题标题】:Java Tetris Game: Having trouble visualizing how to move an object Piece of 4 distinct Tile objectsJava 俄罗斯方块游戏:无法可视化如何移动对象 4 个不同的 Tile 对象
【发布时间】:2013-03-09 00:27:07
【问题描述】:

我正在将俄罗斯方块作为一个有趣的 Java 副项目。

我正试图移动一个完整的俄罗斯方块片,这是一个由 4 个 Tile 对象组成的对象。

当我将棋子向下移动时,所有未被其他瓷砖之一阻挡的块
将毫无问题地向下移动。由于这两项检查,棋子的任何部分被其中一个图块(例如,带有蓝色 x 的图块)阻挡的部分都不会移动:

public boolean isValidCoordinate(int x, int y) {
    return x >= 0 && y >= 0 && x < width && y < height-1; 
}

public boolean isOpen(int x, int y) {
        return isValidCoordinate(x, y) && getTileAt(x, y) == null;
}

从 Board 类中,我这样移动 Piece 对象:

if(keyCode == KeyEvent.VK_DOWN) {

newPiece.movePieceDown();

然后在 Piece 类中:

    public void movePieceDown() {
    tile1.setLocation(tile1.getX(), tile1.getY()+1);
    tile2.setLocation(tile2.getX(), tile2.getY()+1);
    tile3.setLocation(tile3.getX(), tile3.getY()+1);
    tile4.setLocation(tile4.getX(), tile4.getY()+1);        
}

有没有办法让俄罗斯方块的所有图块一起移动?

谢谢!


编辑:

EXE:

https://www.dropbox.com/s/mg9dtf2sr0sazwq/my_tetris_test2.jar

来源:

https://www.dropbox.com/s/9kt3sl6qqo54amk/Tetris%20Two.rar


代码编辑:

反向 Z 形测试:

起始坐标:Piece "RevZ" Coordinates: (1, 1) (2, 1) (0, 2) (1, 2)

按下键给我:

Board.java 类: 将按键事件改成这样:

public void keyPressed(KeyEvent e) {
    int keyCode = e.getKeyCode();
        newPiece.movePieceCheck(keyCode);
        if (newPiece.movePieceValid()) {
            newPiece.move();
        }
        repaint();  
}

Piece.java 类:我现在在移动之前检查...

public void calcNewPosition(int newX, int newY, int currTile) {     
    newPositionX[currTile] = newX;
    newPositionY[currTile] = newY;
    System.out.println("New Position: " + newPositionX[currTile] + ", " + newPositionY[currTile]);  
}

public void clearCurrPosition(int currX, int currY, int currTile) { 
        currPositionX[currTile] = currX;
        currPositionY[currTile] = currY;    
        System.out.println("Current Position: " + currPositionX[currTile] + ", " + currPositionY[currTile]);
        board.setTileAt(null, currPositionX[currTile], currPositionY[currTile]);
        if (board.getTileAt(currPositionX[currTile], currPositionY[currTile]) == null) {
            System.out.println("curr block set to null...");
    }       
}       

public void movePieceCheck(int keycode) {
    if (keycode == KeyEvent.VK_DOWN) {
        for (int i = 0; i < tile.length; i++) {
            calcNewPosition(tile[i].getX(), tile[i].getY()+1, i);
            clearCurrPosition(tile[i].getX(), tile[i].getY(), i);
        }       
    }
    if (keycode == KeyEvent.VK_RIGHT) {
        for (int i = 0; i < tile.length; i++) {
            calcNewPosition(tile[i].getX()+1, tile[i].getY(), i);
            clearCurrPosition(tile[i].getX(), tile[i].getY(), i);
        }       
    }
    if (keycode == KeyEvent.VK_LEFT) {
        for (int i = 0; i < tile.length; i++) {
            calcNewPosition(tile[i].getX()-1, tile[i].getY(), i);
            clearCurrPosition(tile[i].getX(), tile[i].getY(), i);
        }       
    }
}

public boolean movePieceValid() {   
    boolean valid = false;
    for (int i = 0; i < tile.length; i++) {
        if(tile[i].checkNewLocation(newPositionX[i], newPositionY[i])) {
            valid = true;
        }
    }       
    return valid;
}       

public void move()   {
    for (int i = 0; i < tile.length; i++) {
        tile[i].setLocation(newPositionX[i], newPositionY[i]);              
    }       
}

Println 输出:

Piece "RevZ" Coordinates: (1, 1) (2, 1) (0, 2) (1, 2) 

New Position: 1, 2
Current Position: 1, 1
curr block set to null...
New Position: 2, 2
Current Position: 2, 1
curr block set to null...
New Position: 0, 3
Current Position: 0, 2
curr block set to null...
New Position: 1, 3
Current Position: 1, 2
curr block set to null...

我按照你说的先检查所有,然后设置位置。

现在的问题是,它正在移动除了在检查发生时无法移动的块,因为原始块在那里。但这很奇怪,因为我在移动碎片之前将那个旧位置设置为空,所以它应该是空的。

【问题讨论】:

  • 创建一个包含多个俄罗斯方块块的俄罗斯方块类,并让第一个类检查障碍物并控制其组成块的移动。我认为这只不过是基本的 OOP 设计。
  • 只需添加一个检查以查看getTileAt(x,y) == tile[1-4]
  • @HovercraftFullOfEels 这就是我所做的。 Piece 是包含多个俄罗斯方块(瓦片)的类。我有 4 个类:Tetris.java 创建 JPanel,Board.java 实例化瓷砖网格,并进行碰撞/结束游戏,getTile 位置检查等...,Tile.java 这是俄罗斯方块,@987654337 @ 是 4 个 Tile 对象....它得到一个 randomPiece 参数,该参数设置初始形状(通过设置 4 个瓷砖的坐标)
  • @AlexGittemeier 我正在考虑这样做,但不确定如何从 Board 类中遍历俄罗斯方块的 4 个图块
  • 我的意思是 Collection 准确地说,Collection 库的类之一。 Piece 是如何保持它的 Tiles 的?同样,它应该将它们保存在 Collection 中,例如 ArrayList&lt;Tile&gt;。你不应该把代码弄得太死板,以至于只能容纳 4 块且只有 4 块。

标签: java swing


【解决方案1】:

这并不奇怪,因为你说检查位置是否是valid,也就是说,据我了解,没有其他部分存在。

你的问题是这样的:

tile3.setLocation(tile3.getX(), tile3.getY()+1);
tile4.setLocation(tile4.getX(), tile4.getY()+1);  

我敢打赌,卡住的是第 3 部分。所以要解决这个问题,只需改变这两个的顺序。但是现在问题来了,当你旋转这块时会发生什么,然后它们应该按什么顺序移动?

我认为最好先计算每个方格的新位置,然后将它们从当前位置中删除。然后将它们全部拉到新位置:

calculateNewPositions();
clearCurrentPositions();
drawSquares();

这应该可以在没有太多麻烦的情况下变得非常通用,因此它可以处理不同的情况(即当您旋转对象时)而不会有太多麻烦。

【讨论】:

  • @Daniel,谢谢你的回答。我试过你说的(请见上文),但它仍然给我类似的问题。打印语句显示:1)新位置正在准确计算和存储,2)旧位置设置为空......然后我将块的位置设置为新位置。我做错了什么?
【解决方案2】:

我认为您的问题是每个 Tile 决定其是否被阻塞和移动的逻辑之一是错误的。建议:

  • Tile 类不应该决定它是否应该移动。
  • 相反,它应该有一个方法来检查它是否未被阻塞,仅此而已。
  • Piece 类应遍历其 Tile 集合,对其组成的 Tile 对象调用此方法。
  • 如果一个 Tile 被阻挡,Piece 应该确保它没有被它自己的 Tiles 阻挡。
  • 如果棋子被非棋子棋子挡住,棋子应取消移动。
  • 如果所有的瓷砖都没有被阻挡,或者如果任何瓷砖被自己的碎片中的瓷砖阻挡,那么碎片应该遍历瓷砖 ArrayList&lt;Tile&gt; 告诉每个瓷砖移动。

为了其他人的利益,这是您在单个文件中的完整程序:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class TetrisSscce extends JPanel {
   static TetrisSscce runMe;
   BoardSscce board;

   public TetrisSscce() { // creates a new Panel window and sets properties
      JFrame f = new JFrame("Tetris");
      // width (height), length, tilesize
      board = new BoardSscce(4, 6, 30);
      f.add(board);
      f.setSize(board.getWidth(), board.getHeight());
      f.setVisible(true);
      f.setResizable(false);
      f.setVisible(true);
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
      f.setLocation((screensize.width - f.getWidth()) / 2,
            (screensize.height - f.getHeight()) / 2 - 100);
   }

   public static void main(String[] args) {
      runMe = new TetrisSscce();
   }

}

class BoardSscce extends JPanel implements KeyListener {
   private TileSscce grid[][];
   private TileSscce activetile;
   private int width, height, tilesize;
   private String piece;
   private PieceSscce newPiece;

   public BoardSscce(int w, int h, int ts) { // add elements to the Tetris panel
      width = w;
      height = h;
      tilesize = ts;
      grid = new TileSscce[height][width];

      // activetile = new TileSscce(this,0, 0); //add new tile to "this" board

      newPiece = new PieceSscce(this, randomPiece());

      addKeyListener(this);
      setFocusable(true);

   }

   public String randomPiece() {
      String[] Pieces = { "L", "O", "Z", "RevZ", "Bar", "T", "RevL" };
      String randomShape;

      int rand = (int) (Math.random() * Pieces.length);
      randomShape = Pieces[rand];

      return randomShape;
   }

   // dimensions of board = width * tilesize
   public int getWidth() {
      // 4 * 30 = 120 width
      return width * tilesize;
   }

   public int getHeight() {
      // 6 * 30 = 180 height
      return height * tilesize;
   }

   public void paintComponent(Graphics g) {
      g.setColor(Color.black);
      g.fillRect(0, 0, getWidth(), getHeight());
      for (int row = 0; row < grid.length; row++) {
         for (int col = 0; col < grid[row].length; col++) {
            if (grid[row][col] != null) {
               // if there is a non-null space, that is a tetris piece.. fill it
               // red
               if (grid[row][col].getColor() != null) {
                  g.setColor(grid[row][col].getColor());
                  g.fillRect(col * tilesize, row * tilesize, tilesize, tilesize);
               }
            }
         }
      }
   }

   public void keyPressed(KeyEvent e) {
      int keyCode = e.getKeyCode();
      if (keyCode == KeyEvent.VK_DOWN) {
         // checkBottomFull(0,4);
         // collisionCheck(activetile.getX(),activetile.getY());
         // checkEndGame(activetile.getX(), activetile.getY());

         // activetile.setLocation(activetile.getX(), activetile.getY()+1);

         // need to move Object newPiece down as a unit of 4 tiles
         newPiece.movePieceDown();

         // System.out.println("coordinates: " + activetile.getX() + ", " +
         // activetile.getY());

         repaint();
      }
      if (keyCode == KeyEvent.VK_RIGHT) {
         // activetile.setLocation(activetile.getX()+1, activetile.getY());
         // System.out.println("coordinates: " + activetile.getX() + ", " +
         // activetile.getY());
         newPiece.movePieceRight();
         repaint();
      }
      if (keyCode == KeyEvent.VK_LEFT) {
         // activetile.setLocation(activetile.getX()-1, activetile.getY());
         // System.out.println("coordinates: " + activetile.getX() + ", " +
         // activetile.getY());
         newPiece.movePieceLeft();
         repaint();
      }

   }

   public void keyReleased(KeyEvent e) {
   }

   public void keyTyped(KeyEvent e) {
   }

   // check if (x, y) is a safe coordinate, ie, within the grid array bounds
   public boolean isValidCoordinate(int x, int y) {
      return x >= 0 && y >= 0 && x < width && y < height - 1;
   }

   // returns the tile at (x, y) or null if empty
   public TileSscce getTileAt(int x, int y) {
      if (isValidCoordinate(x, y))
         return grid[y][x];
      return null;
   }

   // sets the tile at (x, y) to tile
   public void setTileAt(TileSscce tile, int x, int y) {
      if (isValidCoordinate(x, y))
         grid[y][x] = tile;
   }

   public boolean isOpen(int x, int y) {
      return isValidCoordinate(x, y) && getTileAt(x, y) == null;
   }

   public void collisionCheck(int x, int y) {
      /*
       * if (activetile.getY() == this.height-2 || getTileAt(x, y+1) != null) {
       * //activetile = new TileSscce(this, 0, 0); newPiece = new
       * PieceSscce(this, randomPiece()); }
       */

   }

   public void checkBottomFull(int x, int y) {
      while (getTileAt(x, y) != null) {
         say("(" + x + ", " + y + ")");
         if (x == 3) {
            say("row is full");
            // replace full row with tiles from above
            for (int i = 0; i < 4; i++) {
               for (int j = 5; j > 0; j--) {
                  grid[j][i] = getTileAt(i, j - 1);
               }
            }
            break;
         }
         x++;
      }
   }

   public void checkEndGame(int x, int y) {
      if (y == 0 && !isOpen(x, y + 1)) {
         say("Game over");
         for (int i = 0; i < 4; i++) {
            for (int j = 5; j > 0; j--) {
               grid[j][i] = null;
            }
         }
      }
   }

   public void say(String word) {
      System.out.println(word);
   }

   public void sayint(int number) {
      System.out.println(number);
   }
}

class PieceSscce {
   public int[] pieceCoordinates;
   public String shape;
   public BoardSscce board;

   public TileSscce tile[];

   public TileSscce tile1;
   public TileSscce tile2;
   public TileSscce tile3;
   public TileSscce tile4;

   // don't need to pass in board because I'm already utilizing the Tiles class,
   // which knows about the board
   public PieceSscce(BoardSscce b, String randomPiece) {
      shape = randomPiece;
      board = b;
      // set what the shape coordinates should be based on what "shape" is

      pieceCoordinates = new int[8];
      setInitialShapeCoordinates(shape);

      tile = new TileSscce[4];

      tile1 = new TileSscce(board, pieceCoordinates[0], pieceCoordinates[1]);
      tile2 = new TileSscce(board, pieceCoordinates[2], pieceCoordinates[3]);
      tile3 = new TileSscce(board, pieceCoordinates[4], pieceCoordinates[5]);
      tile4 = new TileSscce(board, pieceCoordinates[6], pieceCoordinates[7]);

   }

   public void movePieceDown() {
      for (int i = 0; i < 4; i++) {

      }

      tile1.setLocation(tile1.getX(), tile1.getY() + 1);
      tile2.setLocation(tile2.getX(), tile2.getY() + 1);
      tile3.setLocation(tile3.getX(), tile3.getY() + 1);
      tile4.setLocation(tile4.getX(), tile4.getY() + 1);
   }

   public void movePieceRight() {

      tile1.setLocation(tile1.getX() + 1, tile1.getY());
      tile2.setLocation(tile2.getX() + 1, tile2.getY());
      tile3.setLocation(tile3.getX() + 1, tile3.getY());
      tile4.setLocation(tile4.getX() + 1, tile4.getY());
   }

   public void movePieceLeft() {

      tile1.setLocation(tile1.getX() - 1, tile1.getY());
      tile2.setLocation(tile2.getX() - 1, tile2.getY());
      tile3.setLocation(tile3.getX() - 1, tile3.getY());
      tile4.setLocation(tile4.getX() - 1, tile4.getY());
   }

   public void setInitialShapeCoordinates(String shape) {
      System.out.println(shape);
      if (shape == "L") {
         // piece 1
         pieceCoordinates[0] = 0;
         pieceCoordinates[1] = 1;

         // piece2
         pieceCoordinates[2] = 1;
         pieceCoordinates[3] = 1;

         // piece3
         pieceCoordinates[4] = 2;
         pieceCoordinates[5] = 1;

         // piece4
         pieceCoordinates[6] = 2;
         pieceCoordinates[7] = 2;

      } else if (shape == "O") {
         // piece 1
         pieceCoordinates[0] = 0;
         pieceCoordinates[1] = 1;

         // piece2
         pieceCoordinates[2] = 1;
         pieceCoordinates[3] = 1;

         // piece3
         pieceCoordinates[4] = 0;
         pieceCoordinates[5] = 2;

         // piece4
         pieceCoordinates[6] = 1;
         pieceCoordinates[7] = 2;

      } else if (shape == "Z") {
         // piece 1
         pieceCoordinates[0] = 0;
         pieceCoordinates[1] = 1;

         // piece2
         pieceCoordinates[2] = 1;
         pieceCoordinates[3] = 1;

         // piece3
         pieceCoordinates[4] = 1;
         pieceCoordinates[5] = 2;

         // piece4
         pieceCoordinates[6] = 2;
         pieceCoordinates[7] = 2;

      } else if (shape == "RevZ") {
         // piece 1
         pieceCoordinates[0] = 1;
         pieceCoordinates[1] = 1;

         // piece2
         pieceCoordinates[2] = 2;
         pieceCoordinates[3] = 1;

         // piece3
         pieceCoordinates[4] = 0;
         pieceCoordinates[5] = 2;

         // piece4
         pieceCoordinates[6] = 1;
         pieceCoordinates[7] = 2;

      } else if (shape == "Bar") {
         // piece 1
         pieceCoordinates[0] = 0;
         pieceCoordinates[1] = 1;

         // piece2
         pieceCoordinates[2] = 1;
         pieceCoordinates[3] = 1;

         // piece3
         pieceCoordinates[4] = 2;
         pieceCoordinates[5] = 1;

         // piece4
         pieceCoordinates[6] = 3;
         pieceCoordinates[7] = 1;

      } else if (shape == "T") {
         // piece 1
         pieceCoordinates[0] = 1;
         pieceCoordinates[1] = 1;

         // piece2
         pieceCoordinates[2] = 0;
         pieceCoordinates[3] = 2;

         // piece3
         pieceCoordinates[4] = 1;
         pieceCoordinates[5] = 2;

         // piece4
         pieceCoordinates[6] = 2;
         pieceCoordinates[7] = 2;

      } else if (shape == "RevL") {
         // piece 1
         pieceCoordinates[0] = 0;
         pieceCoordinates[1] = 2;

         // piece2
         pieceCoordinates[2] = 1;
         pieceCoordinates[3] = 2;

         // piece3
         pieceCoordinates[4] = 2;
         pieceCoordinates[5] = 2;

         // piece4
         pieceCoordinates[6] = 2;
         pieceCoordinates[7] = 1;

      }
   }
}

class TileSscce {
   private BoardSscce board;
   private int currX, currY;

   public TileSscce(BoardSscce b, int x, int y) {
      board = b;

      // when TileSscce is instantiated, set its position
      setLocation(x, y);
   }

   public int getX() {
      return currX;
   }

   public int getY() {
      return currY;
   }

   public void setLocation(int newX, int newY) {
      if (board.isValidCoordinate(newX, newY) && board.isOpen(newX, newY)) {

         board.setTileAt(null, currX, currY);
         currX = newX;
         currY = newY;
         board.setTileAt(this, currX, currY);

      }
   }

   public Color getColor() {

      double rand = Math.random() * (2 - 0);

      /*
       * Random rand = new Random(); float r = rand.nextFloat(); float g =
       * rand.nextFloat(); float b = rand.nextFloat();
       */
      // Color randomColor = new Color(r, g, b);

      if (rand == 1) {
         return Color.GRAY;
      } else {
         return Color.RED;
      }
   }

   public Color setColor() {
      return Color.GREEN;
   }
}

正如 Daniel 指出的那样,您需要在如何检查被阻塞的棋子以及如何移动上拆分逻辑。你应该首先检查一个棋子持有的所有棋子上的块,然后让棋子告诉所有棋子移动而不是你在做什么——检查每个棋子是否有一个块,如果没有被阻止就移动它。

例如,给 Tile 这样的东西:

// method to check if position OK
public boolean checkNewLocation(int newX, int newY) {
  boolean newLocationOK = board.isValidCoordinate(newX, newY)
        && board.isOpen(newX, newY);

  return newLocationOK;
}

// method to move to new position.
public void setLocation(int newX, int newY) {
  board.setTileAt(null, currX, currY);
  currX = newX;
  currY = newY;
  board.setTileAt(this, currX, currY);
}

所以 Piece 将遍历其 Tiles 调用第一个方法,然后如果一切正常,移动 Tiles 调用第二个方法。

【讨论】:

  • 感谢您的回答。我试过你所说的(请参见上文),但它仍然给我类似的问题。打印语句显示:1)新位置正在准确计算和存储,2)旧位置设置为空......然后我将块的位置设置为新位置。我做错了什么?
  • @Growler:你仍然在以错误的顺序做事。同样,您的 Piece 类必须首先检查 所有 Tiles 以查看它们是否可移动。此时不应移动 任何 块。如果一切正常,那么它应该移动 all 块。同样,这不过是简单的逻辑。仔细想想这个问题,你会发现这是有道理的。
  • 气垫船有什么想法吗?我在上面做了一些更新。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-29
  • 2013-03-17
  • 1970-01-01
  • 1970-01-01
  • 2022-01-24
相关资源
最近更新 更多