Grundlegendes Java Tetris Spiel

Aug 17 2020

Ich lerne wirklich einfaches Java in der Schule, aber ich lerne alleine zu Hause. Ich habe einige Erfahrungen mit einfachen Swing-Spielen gemacht, aber dieses übertrifft alles. Ich möchte jede Meinung und jeden Rat, den jemand beitragen kann.

public class Display {

    private JFrame frame;
    private Canvas canvas;

    private String title;
    private int width, height;

    public Display(String title, int width, int height) {
        this.title = title;
        this.width = width;
        this.height = height;

        createDisplay();
    }
    private void createDisplay() {
        frame = new JFrame(title);
        frame.setSize(width, height);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        canvas = new Canvas();
        canvas.setPreferredSize(new Dimension(width, height));
        canvas.setMaximumSize(new Dimension(width, height));
        canvas.setMinimumSize(new Dimension(width, height));

        frame.add(canvas);
        frame.pack();
    }
    public Canvas getCanvas() {
        return canvas;
    }
    public void addKeyListner(KeyAdapter ka) {
        canvas.addKeyListener(ka);
        canvas.requestFocus();
    }
}

public class Shape {

    private int[] coords;
    private int color;
    private int pos;

    public Shape(Shape shape) {
        this(shape.coords, shape.color, shape.pos);
    }
    public Shape(int[] coords, int color) {
        this(coords, color, 0);
    }
    public Shape(int[] coords, int color, int pos) {
        this.coords = coords;
        this.color = color;
        this.pos = pos;
    }
    public void rotate() {
        pos++;
        if (pos == 4) pos = 0;
    }
    public int color() {
        return color;
    }
    public int position() {
        return pos;
    }
    public int[] coordinates() {
        return coords;
    }
}

public class Game implements Runnable {

    private Display display;
    private Board board;

    private int width, height;
    private String title;

    private boolean running = false;
    private Thread gameThread;
    private int tickTime = 400;

    private BufferStrategy bs;
    private Graphics g;

    private KeyKeeper keyKeeper;

    public Game(String title, int width, int height) {
        this.width = width;
        this.height = height;
        this.title = title;
    }
    
    private void initTick() {
        while (running) {
                try {
                    gameThread.sleep(tickTime);
                } catch (InterruptedException ie) {}

                tick();

            }
    }

    private void init() {
        display = new Display(title, width, height);
        board = new Board(width - 100, height);
        keyKeeper = new KeyKeeper();
        display.addKeyListner(keyKeeper);
    }

    private void tick() {
        board.tick();
    }

    private void render() {
        bs = display.getCanvas().getBufferStrategy();
        if (bs == null) {
            display.getCanvas().createBufferStrategy(3);
            return;
        }
        g = bs.getDrawGraphics();
        //Draw Here!

        //background
        Tetris.drawBackground(g, board, 0, 0);

        // board
        Tetris.drawBoard(g, board, 0, 0);

        //shape
        Tetris.drawShape(g, board);

        //End Drawing!
        bs.show();
        g.dispose();
    }

    public void run() {
            init();

            while (running) {
                render();
            }
            stop();
    }

    public synchronized void start() {
        if (running) {
            return;
        }
        running = true;
        gameThread = new Thread(this);
        gameThread.start();
        
        initTick();
    }

    public synchronized void stop() {
        if (!running) {
            return;
        }
        running = false;
        try {
            gameThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    class KeyKeeper extends KeyAdapter {

        @Override
        public void keyPressed(KeyEvent ke) {
            if (ke.getKeyCode() == KeyEvent.VK_LEFT) {
                board.moveShape(-1, 0);
            } else if (ke.getKeyCode() == KeyEvent.VK_RIGHT) {
                board.moveShape(1, 0);
            } else if (ke.getKeyCode() == KeyEvent.VK_DOWN) {
                board.moveShape(0, 1);
            } else if (ke.getKeyCode() == KeyEvent.VK_UP) {
                board.hardDown();
            }
            else if (ke.getKeyCode() == KeyEvent.VK_SPACE) 
                board.rotateShape();
        }
    }
}

public class Board {

    public static int width, height;
    public static int tx, ty;
    public static int xts;
    public static int yts;

    private int[][] boardCoor;

    private int[][] coords;

    private Shape noShape;
    private Point shapeCoorPoint;
    private boolean[] shapeUsed;
    private int shapeCounter;

    public Board(int width, int height) {
        this.width = width;
        this.height = height;

        init();
    }

    private void init() {
        tx = 12;
        ty = 24;
        xts = width / tx;
        yts = height / ty;

        boardCoor = new int[tx][ty];
        for (int i = 0; i < ty; i++) {
            for (int j = 0; j < tx; j++) {
                boardCoor[j][i] = 7;
            }
        }

        coords = new int[][]{
            {0, 2, 4, 6},// I
            {1, 2, 3, 4},// Z
            {0, 2, 3, 5},// S
            {0, 2, 3, 4},// T
            {0, 2, 4, 5},// L
            {1, 3, 5, 4},// J
            {2, 3, 4, 5} // O
        };

        shapeCoorPoint = new Point();
        shapeUsed = new boolean[]{false, false, false, false, false, false, false};
        shapeCounter = 0;

        initShape();
    }

    public int[][] getBoard() {
        return boardCoor;
    }

    public Shape getShape() {
        return noShape;
    }

    public Point getShapeCoorPoint() {
        return shapeCoorPoint;
    }

    private void initShape() {
        boolean changeShape = true;
        int n;
        while (changeShape) {
            n = (int) (Math.random() * 7);
            if (!shapeUsed[n]) {
                noShape = new Shape(coords[n], n);
                shapeUsed[n] = true;
                shapeCounter++;
                changeShape = false;
            }
        }

        if (shapeCounter == 7) {
            shapeUsed = new boolean[]{false, false, false, false, false, false, false};
            shapeCounter = 0;
        }
        shapeCoorPoint.move(tx / 2 - 1, 0);
    }

    public void tick() {
        if (Tetris.canFall(this)) {
            shapeCoorPoint.translate(0, 1);
        } else {
            Tetris.update(this);

            clearLines();
            initShape();
        }
    }

    public boolean moveShape(int dx, int dy) {
        //dy=1 - down
        //dx=-1 - right
        //dx=1 - left
        // ~~~ strategy ~~~
        // create an instance point, then, check -
        //if legal, translate the shape point.
        Point instancePoint = new Point(shapeCoorPoint);
        instancePoint.translate(dx, dy);
        if (Tetris.isLegal(boardCoor, noShape, instancePoint)) {
            shapeCoorPoint.translate(dx, dy);
            return true;
        }
        return false;
    }

    public void hardDown() {
        boolean stop;
        do {
            stop = moveShape(0, 1);
        } while (stop);
    }

    public boolean rotateShape() {
        //~~~ strategy ~~~
        //create an instance shape, then, check -
        //if legal, rotate
        Shape instanceShape = new Shape(noShape);
        instanceShape.rotate();
        if (Tetris.isLegal(boardCoor, instanceShape, shapeCoorPoint)) {
            noShape.rotate();
            return true;
        }
        return false;
    }

    private void clearLines() {
        boolean isFilled;
        for (int row = 0; row < ty; row++) {
            isFilled = true;
            //check the first tile of the each rank
            for (int col = 0; col < tx; col++) {
                if (boardCoor[col][row] == 7) {
                    isFilled = false;
                    col = tx;
                }
            }

            if (isFilled) {

                for (int i = 0; i < tx; i++) {
                    for (int j = row; j > 0; j--) {
                        boardCoor[i][j] = boardCoor[i][j - 1];
                        boardCoor[i][j - 1] = 7;
                    }
                }
            }
        }
    }
}

public class Tetris {

        //~~~graphic drawings~~~
    public static void drawBackground(Graphics g, Board board, int x, int y) {
        g.setColor(Color.black);
        g.fillRect(x, y, board.width, board.height);

        g.setColor(Color.white);
        g.drawRect(x, y, board.width, board.height);

        g.setColor(Color.gray);
        for (int i = 1; i < board.ty; i++) {
            g.drawLine(x, y + i * board.yts,
                    x + board.width, y + i * board.yts);
        }
        for (int i = 1; i < board.tx; i++) {
            g.drawLine(x + i * board.xts, y,
                    x + i * board.xts, y + board.height);
        }
    }

    public static void drawBoard(Graphics g, Board board, int x, int y) {
        int[][] boardCoor = board.getBoard();
        int c;
        Color[] colors = new Color[]{
            Color.red, Color.blue, Color.orange, Color.magenta,
            Color.cyan, Color.green, Color.yellow, Color.black};

        for (int i = 0; i < board.ty; i++) {
            for (int j = 0; j < board.tx; j++) {
                c = boardCoor[j][i];
                g.setColor(colors[c]);
                g.fillRect(x + j * board.xts + 1, y + i * board.yts + 1,
                        board.xts - 1, board.yts - 1);
            }
        }
    }

    public static void drawShape(Graphics g, Board board) {
        Point point = board.getShapeCoorPoint();
        Shape shape = board.getShape();
        int[] coords = shape.coordinates();
        int pos = shape.position();
        int c = shape.color();
        Color[] colors = new Color[]{
            Color.red, Color.blue, Color.orange, Color.magenta,
            Color.cyan, Color.green, Color.yellow, Color.black};
        g.setColor(colors[c]);

        int[] arr;

            for (int i = 0; i < coords.length; i++) {
                arr = getXY(coords[i], pos, point);
                g.fillRect(
                        (arr[0]) * board.xts + 1, (arr[1]) * board.yts + 1,
                        board.xts - 1, board.yts - 1);
            }
    }

            // ~~~game rules~~~
    public static boolean canFall(Board board) {
        return canFall(board.getBoard(), board.getShape(), board.getShapeCoorPoint());
    }

    public static boolean canFall(int[][] boardCoor, Shape shape, Point point) {
        return canFall(boardCoor, shape.coordinates(), shape.position(), point);
    }

    public static boolean canFall(int[][] boardCoor, int[] coords, int pos, Point point) {
        int[] arr;
            for (int i = 0; i < coords.length; i++) {
                arr = getXY(coords[i], pos, point);
                if (arr[1] == Board.ty - 1 || boardCoor[arr[0]][arr[1] + 1] != 7) {
                    return false;
                }
            }
        return true;
    }

    public static boolean isLegal(Board board) {
        return isLegal(board.getBoard(), board.getShape(), board.getShapeCoorPoint());
    }

    public static boolean isLegal(int[][] boardCoor, Shape shape, Point point) {
        return isLegal(boardCoor, shape.coordinates(), shape.position(), point);
    }

    public static boolean isLegal(int[][] boardCoor, int[] coords, int pos, Point point) {
        int[] arr;
        for (int i = 0; i < coords.length; i++) {
            arr = getXY(coords[i], pos, point);
            if (arr[1] >= Board.ty || arr[1] < 0  ||
                arr[0] < 0        || arr[0] >= Board.tx || 
                boardCoor[arr[0]][arr[1]] != 7) {
                return false;
            }
        }
        return true;
    }

                //~~~technical functions~~~
    public static void update(Board board) {
        update(board.getBoard(), board.getShape(), board.getShapeCoorPoint());
    }

    public static void update(int[][] boardCoor, Shape shape, Point point) {
        update(boardCoor, shape.coordinates(), shape.color(), shape.position(), shape, point);
    }

    public static void update(int[][] boardCoor, int[] coords, int color, int pos, Shape shape, Point point) {
        int[] arr;
        for (int i = 0; i < coords.length; i++) {
            arr = getXY(coords[i], pos, point);
            boardCoor[arr[0]][arr[1]] = color;
        }
    }

    private static int[] getXY(int value, int pos, Point point) {
        int[] arr = new int[2];
        if (pos == 0) {
            arr[0] = value % 2 + point.x;
            arr[1] = value / 2 + point.y;
            return arr;

        } else if (pos == 1) {
            arr[0] = 2 - value / 2 + point.x;
            arr[1] = 1 + value % 2 + point.y;
            return arr;

        } else if (pos == 2) {
            arr[0] = 1 - value % 2 + point.x;
            arr[1] = 3 - value / 2 + point.y;
            return arr;

        } else {
            arr[0] = value / 2 - 1 + point.x;
            arr[1] = 2 - value % 2 + point.y;
            return arr;
        }
    }
}

Antworten

2 Doi9t Aug 17 2020 at 07:27

Ich habe einige Vorschläge für Ihren Code.

Fügen Sie loop& immer geschweifte Klammern hinzuif

Meiner Meinung nach ist es eine schlechte Praxis, einen Codeblock zu haben, der nicht von geschweiften Klammern umgeben ist. Ich habe in meiner Karriere so viele Fehler gesehen, die damit zusammenhängen. Wenn Sie vergessen, beim Hinzufügen von Code die geschweiften Klammern hinzuzufügen, brechen Sie die Logik / Semantik des Codes.

Extrahieren Sie den Ausdruck bei mehrfacher Verwendung in Variablen.

In Ihrem Code können Sie den Ausdruck in Variablen extrahieren. Dadurch wird der Code kürzer und leichter lesbar.

Vor

if (ke.getKeyCode() == KeyEvent.VK_LEFT) {
   board.moveShape(-1, 0);
} else if (ke.getKeyCode() == KeyEvent.VK_RIGHT) {
   board.moveShape(1, 0);
} else if (ke.getKeyCode() == KeyEvent.VK_DOWN) {
   board.moveShape(0, 1);
} else if (ke.getKeyCode() == KeyEvent.VK_UP) {
   board.hardDown();
} else if (ke.getKeyCode() == KeyEvent.VK_SPACE)
   board.rotateShape();

Nach

int keyCode = ke.getKeyCode();
if (keyCode == KeyEvent.VK_LEFT) {
   board.moveShape(-1, 0);
} else if (keyCode == KeyEvent.VK_RIGHT) {
   board.moveShape(1, 0);
} else if (keyCode == KeyEvent.VK_DOWN) {
   board.moveShape(0, 1);
} else if (keyCode == KeyEvent.VK_UP) {
   board.hardDown();
} else if (keyCode == KeyEvent.VK_SPACE) {
   board.rotateShape();
}

Es gibt andere Fälle wie diesen in Ihrem Code. Ich schlage vor, Sie tun dasselbe für sie ( new Dimension(width, height), ect).

Verwendet immer eine Kopie des Arrays, wenn Sie es zurückgeben oder empfangen

Die meisten Container (Map, List, Arrays) in Java sind veränderbar (Ausnahme für einige Implementierungen). Wenn Sie eine Instanz in einem Getter zurückgeben, kann jede Klasse, die Zugriff darauf hat, die Auflistung ändern. Auf diese Weise verlieren Sie die Kontrolle über Ihre eigenen Daten. Um dies zu überwinden, müssen Sie eine neue Kopie des Arrays erstellen / die Sammlung in eine Implementierung konvertieren, die nicht geändert werden kann, und dann den Wert zurückgeben.

Versuchen Sie immer, die Kontrolle über Ihre eigenen Daten zu behalten, geben Sie die Sammlungen niemals direkt an die anderen weiter und kopieren Sie die Daten beim Empfang einer Sammlung / eines Arrays in Ihre internen Sammlungen.

Vor

public int[] coordinates() {
   return coords;
}

Nach

public int[] coordinates() {
   return Arrays.copyOf(coords, coords.length);
}

Es gibt mehrere Möglichkeiten, ein Array zu kopieren .

Verwendet Getter anstelle statischer Variablen

In der BoardKlasse verwenden Sie statische Variablen, um die Werte gemeinsam zu nutzen. Dies ist ein Fehler und eine schlechte Angewohnheit, da die statischen Variablen von Instanzen gemeinsam genutzt werden (wenn Sie mehrere Instanzen der Karte erstellen, werden die Werte in allen Instanzen geändert). Stattdessen schlage ich vor, dass Sie die Daten ausblenden und Getter für sie erstellen.

Ersetzen Sie die forSchleife durch eine erweiterte 'for'-Schleife

In Ihrem Code benötigen Sie nicht den von der Schleife bereitgestellten Index, sondern die erweiterte Version.

Vor

 for (int i = 0; i < coords.length; i++) {
    //[...]
 }

Nach

for (int coord : coords) {
}