Jeu de base Java Tetris
J'apprends java vraiment basique à l'école mais j'apprends par moi-même à la maison. J'ai eu une certaine expérience avec des jeux de swing simples, mais celui-ci dépasse tout. Je veux toute opinion et tout conseil que quelqu'un peut apporter.
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;
}
}
}
Réponses
J'ai quelques suggestions pour votre code.
Ajoutez toujours des accolades à loop
&if
À mon avis, c'est une mauvaise pratique d'avoir un bloc de code non entouré d'accolades; J'ai vu tellement de bugs dans ma carrière liés à ça, si vous oubliez d'ajouter les accolades lors de l'ajout de code, vous cassez la logique / sémantique du code.
Extrayez l'expression en variables lorsqu'elle est utilisée plusieurs fois.
Dans votre code, vous pouvez extraire l'expression en variables; cela rendra le code plus court et plus facile à lire.
Avant
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();
Après
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();
}
Il y a d'autres cas comme celui-ci dans votre code, je vous suggère de faire la même chose pour eux ( new Dimension(width, height)
, ect).
Utilise toujours une copie du tableau lors du retour ou de la réception
La plupart des conteneurs (Map, List, Arrays) en java sont modifiables (exception pour certaines implémentations). Si vous renvoyez une instance dans un getter, toute classe qui y a accès peut modifier la collection; vous perdez ainsi le contrôle de vos propres données. Pour surmonter cela, vous devez créer une nouvelle copie du tableau / convertir la collection en une implémentation qui ne peut pas être modifiée, puis renvoyer la valeur.
Essayez toujours de garder le contrôle sur vos propres données, ne partagez jamais les collections directement avec les autres et lors de la réception d'une collection / tableau, copiez les données dans vos collections internes.
Avant
public int[] coordinates() {
return coords;
}
Après
public int[] coordinates() {
return Arrays.copyOf(coords, coords.length);
}
Il existe plusieurs façons de copier un tableau .
Utilise des getters au lieu de variables statiques
Dans la Board
classe, vous utilisez des variables statiques pour partager les valeurs; c'est un bogue et une mauvaise habitude, puisque les variables statiques seront partagées entre les instances (si vous créez plusieurs instances du tableau, les valeurs seront modifiées dans toutes les instances). Au lieu de cela, je vous suggère de masquer les données et de créer des getters pour eux.
Remplacez la for
boucle par une boucle «for» améliorée
Dans votre code, vous n'avez pas réellement besoin de l'index fourni par la boucle, vous pouvez la version améliorée.
Avant
for (int i = 0; i < coords.length; i++) {
//[...]
}
Après
for (int coord : coords) {
}