贪吃蛇
一、创建新项目
创建一个新的项目,并命名。
创建一个名为images的文件夹用来存放游戏相关图片。
然后再在项目的src文件下创建一个com.xxx.view的包用来存放所有的图形界面类,
创建一个com.xxx.controller的包用来存放启动的入口类(控制类)
二、游戏界面
-
package com.snake.view; import java.awt.Color; import java.awt.EventQueue; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.border.EmptyBorder; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; public class SnakeJPanel extends JPanel implements ActionListener{ private boolean start;//当前游戏状态 private int speed;//速度 private boolean exist;//当前是否存在食物 private int foodType;//食物种类 private int x;//豆子的横坐标 private int y;//豆子的纵坐标 private ArrayList<int[]> localList;//蛇 public String direction;//方向 private String direction2;//引导方向 public boolean flag; Random rand = new Random(); private ImageIcon up; private ImageIcon down; private ImageIcon right; private ImageIcon left; private ImageIcon body; private ImageIcon food; private ImageIcon title; Timer time; private int score;//当前得分情况 private int num;//吃到的食物个数 // private Image offScreenImage; //图形缓存 //图片绘制 @Override public void paint(Graphics g) { direction = direction2; g.setColor(Color.WHITE); g.fillRect(0, 0, 900, 700); //绘制游戏框 //标题框 // g.drawRect(25, 30, 800, 75); title.paintIcon(this, g, 25, 10); //内容框 g.setColor(Color.black); g.fillRect(25, 75, 850, 600); //绘制食物的坐标位置 if(!exist) {//如果当前不存在豆子,随机绘制一个豆子 if(num % 5 == 0) { foodType = 1; }else { foodType = 0; } boolean isProduce = true; while(isProduce) { isProduce = false; x = rand.nextInt(33) * 25 + 25; y = rand.nextInt(23) * 25 + 75; for (int[] arr:localList) { if(x == arr[0] && y == arr[1]) { isProduce = true; break; } } } System.out.println(x + "---" + y); } if(eat()) { exist = false; }else { exist = true; } if(foodType == 0) { //绘制食物 g.setColor(Color.blue); // g.fillRect(x, y, 25, 25); g.drawImage(food.getImage(),x, y, 25, 25,null); }else { //绘制食物 g.setColor(Color.WHITE); g.fillRect(x, y, 25, 25); // g.drawImage(food.getImage(),x, y, 25, 25,null); } //绘制头 g.setColor(Color.red); // g.fillRect(localList.get(0)[0], localList.get(0)[1], 25, 25); ImageIcon head = null; //判断当前方向 if(direction.equals("R")) { head = right; }else if(direction.equals("L")) { head = left; }else if(direction.equals("U")) { head = up; }else if(direction.equals("D")) { head = down; } // g.drawImage(head.getImage(), localList.get(0)[0], localList.get(0)[1], 25, 25,null); head.paintIcon(this, g,localList.get(0)[0], localList.get(0)[1]); //绘制身体 g.setColor(Color.white); for (int i = 1; i < localList.size(); i++) { // g.fillRect(localList.get(i)[0], localList.get(i)[1], 25, 25); // g.drawImage(body.getImage(), localList.get(i)[0], localList.get(i)[1], 25, 25,null); body.paintIcon(this, g, localList.get(i)[0], localList.get(i)[1]); } // g.fillRect(localList.get(1)[0], localList.get(1)[1], 25, 25); // g.fillRect(localList.get(2)[0], localList.get(2)[1], 25, 25); //绘制分数和长度 //长度 g.setColor(Color.GREEN); g.setFont(new Font("宋体", Font.BOLD, 18)); g.drawString("长度:" + (localList.size() - 1), 25, 30); //分数 g.drawString("分数:" + score, 25, 48); if(!start) {//如果游戏未启动,结束移动和重绘 g.setColor(Color.white); g.setFont(new Font("宋体", Font.BOLD, 30)); g.drawString("暂停/开始(请按任意键开始,空格键暂停)", 150, 300); time.stop(); }else { time.start(); } // speed(); //移动后进行下一次绘制 // move();//移动 // repaint();//重新绘制 } // //解决闪烁问题 // //如果为JFrame 为重量级 程序不会调用update()方法 // //如果为Frame 为轻量级 重写update()方法 做双缓冲 // //如果为JPanel 不会闪烁 // @Override // public void update(Graphics g) // { // System.out.println("update"); // if(offScreenImage == null) // offScreenImage = this.createImage(900, 700); //新建一个图像缓存空间,这里图像大小为800*600 // Graphics gImage = offScreenImage.getGraphics(); //把它的画笔拿过来,给gImage保存着 // paint(gImage); //将要画的东西画到图像缓存空间去 // g.drawImage(offScreenImage, 0, 0, null); //然后一次性显示出来 // } @Override public void actionPerformed(ActionEvent e) { //移动后进行下一次绘制 move();//移动 repaint();//重新绘制 } /** * 绘制速度 */ // private void speed() { // try {//按一定速度进行移动 // Thread.sleep(speed);//控制移动速度 // } catch (InterruptedException e) { // // TODO 自动生成的 catch 块 // e.printStackTrace(); // } // } /** * 初始化图片 */ private void drawImage() { up = new ImageIcon("images/up.png"); down = new ImageIcon("images/down.png"); right = new ImageIcon("images/right.png"); left = new ImageIcon("images/left.png"); body = new ImageIcon("images/body.png"); food = new ImageIcon("images/food.png"); title = new ImageIcon("images/title.jpg"); } private boolean eat() { if(localList.get(0)[0] == x && localList.get(0)[1] == y) {//如果当前蛇头吃到了豆子 System.out.println("eat"); num++; if(foodType == 0) { score += 10; }else { score += (rand.nextInt(5) * 10 + 10); } int last = localList.size() - 1;//蛇尾 //在蛇尾后面添加一节身体 localList.add(new int[] {localList.get(last)[0],localList.get(last)[1]}); return true; } return false; } //移动方法 public void move() { //判断是否游戏结束 if(isbody()) { System.out.println("game over"); start = false;//结束游戏移动 JOptionPane.showMessageDialog(null,"游戏已结束!"); time.stop(); init(); } if(flag && localList != null) {//如果长度不为空且游戏未结束 int last = localList.size() - 1;//记录蛇尾 for (int i = last; i > 0; i--) {//从蛇尾开始,每节身体移动到前一节身体的位置上 localList.set(i,new int[] {localList.get(i - 1)[0],localList.get(i - 1)[1]}); } //记录头位置 int[] local = localList.get(0); //判断当前方向,并进行模拟移动,判断是否与边界重合 if(direction.equals("R")) { if(local[0] >= 850) { local[0] = 25; }else { local[0] += 25; } }else if(direction.equals("L")) { if(local[0] <= 25) { local[0] = 850; }else { local[0] -= 25; } }else if(direction.equals("U")) { if(local[1] <= 75) { local[1] = 650; }else { local[1] -= 25; } }else if(direction.equals("D")) { if(local[1] >= 650) { local[1] = 75; }else { local[1] += 25; } } //更改头的位置 localList.set(0, local); } } //判断下一步是否为蛇身 private boolean isbody() { // TODO 自动生成的方法存根 //记录头位置 int x = localList.get(0)[0]; int y = localList.get(0)[1]; //判断当前方向,并进行模拟移动,判断是否与边界重合 if(direction.equals("R")) { x += 25; }else if(direction.equals("L")) { x -= 25; }else if(direction.equals("U")) { y -= 25; }else if(direction.equals("D")) { y += 25; } for (int i = 1; i < localList.size(); i++) { if(localList.get(i)[0] == x && localList.get(i)[1] == y) { return true; } } return false; } // //判断下一步是否为边界 // private boolean isborder() { // // TODO 自动生成的方法存根 // //记录头位置 // // TODO 自动生成的方法存根 // //记录头位置 // int x = localList.get(0)[0]; // int y = localList.get(0)[1]; // // //判断当前方向,并进行模拟移动,判断是否与边界重合 // if(direction.equals("R")) { // x += 25; // }else if(direction.equals("L")) { // x -= 25; // }else if(direction.equals("U")) { // y -= 25; // }else if(direction.equals("D")) { // y += 25; // } // // if(x < 25 || x > (33 * 25 + 25)) { // return true;//当x坐标超出边界,则返回true // } // if(y < 105 || y > (23 * 25 + 105)) { // return true;//当y坐标超出边界,则返回true // } // return false;//蛇头移动后未超出边界,返回false // // } /** * Create the frame. */ public SnakeJPanel(int speed) { this.speed = speed; //初始化速度 //初始化游戏面板的基本信息 this.setSize(900, 700); this.setLocation(0, 30); this.setFocusable(true); init();//初始化界面 drawImage();//绘制图片 moveByKey();//给界面添加一个键盘监听 } /* * 键盘监听 * 通过键盘输入上下左右来控制当前蛇头移动的方向 * 先判断当前蛇头方向,再来改变引导方向 * 当进行绘制时再修改蛇的方向 * 保证不会因为在短时间内快速变换方向导致蛇头逆向转向 */ private void moveByKey() { addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); //边界值判断 switch(key) { case 65: case 37:{//向左走 if(!direction.equals("R")) { direction2 = "L"; } break; } case 87: case 38:{//向上走 if(!direction.equals("D")) { direction2 = "U"; } break; } case 68: case 39:{//向右走 if(!direction.equals("L")) { direction2 = "R"; } break; } case 83: case 40:{//向下走 if(!direction.equals("U")) { direction2 = "D"; } break; } case KeyEvent.VK_SPACE:{//如果当前键盘输入为空格 start = !start;//调整游戏状态 System.out.println("暂停/开始"); repaint();//重绘 } } //任意键开始 if(!start && key != KeyEvent.VK_SPACE) {//如果当前状态为暂停状态,且键盘输入不是空格 start = true; repaint();//重绘 } } }); } /** * 初始化游戏基本信息 */ private void init() { start = false; exist = true; direction2 = "U"; flag = true; localList = new ArrayList<int[]>(); localList.add(0,new int[] {75,125});//蛇头 localList.add(1,new int[] {75,150});//蛇身1 localList.add(2,new int[] {75,175});//蛇身2 //创建第一个食物的位置 //通过循环保证当前生成的食物不在身体所在的坐标上 boolean isProduce = true; while(isProduce) {//循环生成食物坐标 isProduce = false;//结束本次循环 x = rand.nextInt(33) * 25 + 25; y = rand.nextInt(23) * 25 + 75; for (int[] arr:localList) {//循环遍历蛇头及蛇身的坐标 if(x == arr[0] && y == arr[1]) {//如果食物坐标和蛇的某一节坐标重合 isProduce = true;//跳转循环状态,继续下一次食物生成 break; } } //蛇身遍历完成,没有重合坐标,结束食物坐标生成 } time = new Timer(speed, this); setLayout(null); score = 0; num = 0; foodType = 0; // repaint(); } }
三、构造启动类
-
p
ackage com.snake.controller; import javax.swing.JFrame; import javax.swing.JOptionPane; import com.snake.view.SnakeJPanel; public class SnakeStart { public static void main(String[] args) { int speed = 0; String showInputDialog = null;//初始化时间 //得到速度 while(true) { showInputDialog = JOptionPane.showInputDialog("蛇移动速度(1 - 5)","3"); if(showInputDialog == null) { showInputDialog = "3";//默认速度 break; } if(showInputDialog.length() > 1) { continue; } char[] a = showInputDialog.toCharArray(); if(a[0] >= '1' && a[0] <= '5') { break; } } speed = Integer.parseInt(showInputDialog) * 50; SnakeJPanel snakeJPanel = new SnakeJPanel(speed); //创建一个JFrame窗口,将游戏面板添加进行窗口中 JFrame jFrame = new JFrame(); //设置窗口的某些属性 jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jFrame.setSize(920, 750); jFrame.add(snakeJPanel); jFrame.setLocationRelativeTo(null); jFrame.setVisible(true); } }
四、游戏启动
设置游戏速度
游戏界面
俄罗斯方块
游戏规则:
由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。
整体代码分为三个模块:方格模块,七种图形模块,俄罗斯方块主模块。
小方块类:Cell
-
package com.zhao.demo.block; import java.awt.image.BufferedImage; import java.util.Objects; /** * @author xiaoZhao * @date 2022/5/7 * @describe * 小方块类 * 方法: 左移、右移、下落 */ public class Cell { // 行 private int row; // 列 private int col; private BufferedImage image; public Cell() { } public Cell(int row, int col, BufferedImage image) { this.row = row; this.col = col; this.image = image; } public int getRow() { return row; } public void setRow(int row) { this.row = row; } public int getCol() { return col; } public void setCol(int col) { this.col = col; } public BufferedImage getImage() { return image; } public void setImage(BufferedImage image) { this.image = image; } @Override public String toString() { return "Cell{" + "row=" + row + ", col=" + col + ", image=" + image + '}'; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Cell)) { return false; } Cell cell = (Cell) o; return getRow() == cell.getRow() && getCol() == cell.getCol() && Objects.equals(getImage(), cell.getImage()); } @Override public int hashCode() { return Objects.hash(getRow(), getCol(), getImage()); } //左移动一格 public void left(){ col--; } //右移动一格 public void right(){ col++; } //下移动一格 public void down(){ row++; } }
四方格图形的父类:Tetromino
-
package com.zhao.demo.block; import com.zhao.demo.shape.*; /** * @author xiaoZhao * @date 2022/5/11 * @describe 编写四方格父类 */ public class Tetromino { public Cell[] cells = new Cell[4]; //旋转的状态 protected State[] states; //声明旋转次数 protected int count = 10000; //左移方法 public void moveLeft() { for (Cell cell : cells) { cell.left(); } } //右移方法 public void moveRight() { for (Cell cell : cells) { cell.right(); } } //单元格下落 public void moveDrop() { for (Cell cell : cells) { cell.down(); } } //编写随机生成四方格 public static Tetromino randomOne() { int num = (int) (Math.random() * 7); Tetromino tetromino = null; switch (num) { case 0: tetromino = new I(); break; case 1: tetromino = new J(); break; case 2: tetromino = new L(); break; case 3: tetromino = new O(); break; case 4: tetromino = new S(); break; case 5: tetromino = new T(); break; case 6: tetromino = new Z(); break; } return tetromino; } //顺时针旋转的方法 public void rotateRight() { if (states.length == 0) { return; } //旋转次数+1 count++; State s = states[count % states.length]; Cell cell = cells[0]; int row = cell.getRow(); int col = cell.getCol(); cells[1].setRow(row + s.row1); cells[1].setCol(col + s.col1); cells[2].setRow(row + s.row2); cells[2].setCol(col + s.col2); cells[3].setRow(row + s.row3); cells[3].setCol(col + s.col3); } //逆时针旋转的方法 public void rotateLeft() { if (states.length == 0) { return; } //旋转次数+1 count--; State s = states[count % states.length]; Cell cell = cells[0]; int row = cell.getRow(); int col = cell.getCol(); cells[1].setRow(row + s.row1); cells[1].setCol(col + s.col1); cells[2].setRow(row + s.row2); cells[2].setCol(col + s.col2); cells[3].setRow(row + s.row3); cells[3].setCol(col + s.col3); } //四方格旋转状态的内部类 protected class State { //存储四方格各元素的位置 int row0, col0, row1, col1, row2, col2, row3, col3; public State() { } public State(int row0, int col0, int row1, int col1, int row2, int col2, int row3, int col3) { this.row0 = row0; this.col0 = col0; this.row1 = row1; this.col1 = col1; this.row2 = row2; this.col2 = col2; this.row3 = row3; this.col3 = col3; } public int getRow0() { return row0; } public void setRow0(int row0) { this.row0 = row0; } public int getCol0() { return col0; } public void setCol0(int col0) { this.col0 = col0; } public int getRow1() { return row1; } public void setRow1(int row1) { this.row1 = row1; } public int getCol1() { return col1; } public void setCol1(int col1) { this.col1 = col1; } public int getRow2() { return row2; } public void setRow2(int row2) { this.row2 = row2; } public int getCol2() { return col2; } public void setCol2(int col2) { this.col2 = col2; } public int getRow3() { return row3; } public void setRow3(int row3) { this.row3 = row3; } public int getCol3() { return col3; } public void setCol3(int col3) { this.col3 = col3; } @Override public String toString() { return "State{" + "row0=" + row0 + ", col0=" + col0 + ", row1=" + row1 + ", col1=" + col1 + ", row2=" + row2 + ", col2=" + col2 + ", row3=" + row3 + ", col3=" + col3 + '}'; } } }
七种图形类:I、J、L、O、S、T、Z
I
-
package com.zhao.demo.shape; import com.zhao.demo.App.Tetris; import com.zhao.demo.block.Cell; import com.zhao.demo.block.Tetromino; /** * @author xiaoZhao * @date 2022/5/11 * @describe */ public class I extends Tetromino { public I() { cells[0] = new Cell(0,4, Tetris.I); cells[1] = new Cell(0,3, Tetris.I); cells[2] = new Cell(0,5, Tetris.I); cells[3] = new Cell(0,6, Tetris.I); //共有两种旋转状态 states =new State[2]; //初始化两种状态的相对坐标 states[0]=new State(0,0,0,-1,0,1,0,2); states[1]=new State(0,0,-1,0,1,0,2,0); } }
J
-
package com.zhao.demo.shape; import com.zhao.demo.App.Tetris; import com.zhao.demo.block.Cell; import com.zhao.demo.block.Tetromino; /** * @author xiaoZhao * @date 2022/5/11 * @describe */ public class J extends Tetromino { public J() { cells[0] = new Cell(0,4, Tetris.J); cells[1] = new Cell(0,3, Tetris.J); cells[2] = new Cell(0,5, Tetris.J); cells[3] = new Cell(1,5, Tetris.J); states=new State[4]; states[0]=new State(0,0,0,-1,0,1,1,1); states[1]=new State(0,0,-1,0,1,0,1,-1); states[2]=new State(0,0,0,1,0,-1,-1,-1); states[3]=new State(0,0,1,0,-1,0,-1,1); } }
L
-
package com.zhao.demo.shape; import com.zhao.demo.App.Tetris; import com.zhao.demo.block.Cell; import com.zhao.demo.block.Tetromino; /** * @author xiaoZhao * @date 2022/5/11 * @describe */ public class L extends Tetromino { public L() { cells[0] = new Cell(0,4, Tetris.L); cells[1] = new Cell(0,3, Tetris.L); cells[2] = new Cell(0,5, Tetris.L); cells[3] = new Cell(1,3, Tetris.L); states=new State[4]; states[0]=new State(0,0,0,-1,0,1,1,-1); states[1]=new State(0,0,-1,0,1,0,-1,-1); states[2]=new State(0,0,0,1,0,-1,-1,1); states[3]=new State(0,0,1,0,-1,0,1,1); } }
O
-
package com.zhao.demo.shape; import com.zhao.demo.App.Tetris; import com.zhao.demo.block.Cell; import com.zhao.demo.block.Tetromino; /** * @author xiaoZhao * @date 2022/5/11 * @describe */ public class O extends Tetromino { public O() { cells[0] = new Cell(0, 4, Tetris.O); cells[1] = new Cell(0, 5, Tetris.O); cells[2] = new Cell(1, 4, Tetris.O); cells[3] = new Cell(1, 5, Tetris.O); //无旋转状态 states = new State[0]; } }
S
-
package com.zhao.demo.shape; import com.zhao.demo.App.Tetris; import com.zhao.demo.block.Cell; import com.zhao.demo.block.Tetromino; /** * @author xiaoZhao * @date 2022/5/11 * @describe */ public class S extends Tetromino { public S() { cells[0] = new Cell(0,4, Tetris.S); cells[1] = new Cell(0,5, Tetris.S); cells[2] = new Cell(1,3, Tetris.S); cells[3] = new Cell(1,4, Tetris.S); //共有两种旋转状态 states =new State[2]; //初始化两种状态的相对坐标 states[0]=new State(0,0,0,1,1,-1,1,0); states[1]=new State(0,0,1,0,-1,-1,0,-1); } }
T
-
package com.zhao.demo.shape; import com.zhao.demo.App.Tetris; import com.zhao.demo.block.Cell; import com.zhao.demo.block.Tetromino; /** * @author xiaoZhao * @date 2022/5/11 * @describe */ public class T extends Tetromino { public T() { cells[0] = new Cell(0,4, Tetris.T); cells[1] = new Cell(0,3, Tetris.T); cells[2] = new Cell(0,5, Tetris.T); cells[3] = new Cell(1,4, Tetris.T); states=new State[4]; states[0]=new State(0,0,0,-1,0,1,1,0); states[1]=new State(0,0,-1,0,1,0,0,-1); states[2]=new State(0,0,0,1,0,-1,-1,0); states[3]=new State(0,0,1,0,-1,0,0,1); } }
Z
-
package com.zhao.demo.shape; import com.zhao.demo.App.Tetris; import com.zhao.demo.block.Cell; import com.zhao.demo.block.Tetromino; /** * @author xiaoZhao * @date 2022/5/11 * @describe */ public class Z extends Tetromino { public Z() { cells[0] = new Cell(1,4, Tetris.Z); cells[1] = new Cell(0,3, Tetris.Z); cells[2] = new Cell(0,4, Tetris.Z); cells[3] = new Cell(1,5, Tetris.Z); //共有两种旋转状态 states =new State[2]; //初始化两种状态的相对坐标 states[0]=new State(0,0,-1,-1,-1,0,0,1); states[1]=new State(0,0,-1,1,0,1,1,0); } }
俄罗斯方块游戏主类:Tetris
-
package com.zhao.demo.App; import com.zhao.demo.block.Cell; import com.zhao.demo.block.Tetromino; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.security.cert.Certificate; /** * @author xiaoZhao * @date 2022/5/11 * @describe 俄罗斯方块游戏主类 */ public class Tetris extends JPanel { //正在下落的方块 private Tetromino currentOne = Tetromino.randomOne(); //将要下落的方块 private Tetromino nextOne = Tetromino.randomOne(); //游戏主区域 private Cell[][] wall = new Cell[18][9]; //声明单元格的值 private static final int CELL_SIZE = 48; //游戏分数池 int[] scores_pool = {0, 1, 2, 5, 10}; //当前游戏的分数 private int totalScore = 0; //当前消除的行数 private int totalLine = 0; //游戏三种状态 游戏中、暂停、结束 public static final int PLING = 0; public static final int STOP = 1; public static final int OVER = 2; //当前游戏状态值 private int game_state; //显示游戏状态 String[] show_state = {"P[pause]", "C[continue]", "S[replay]"}; //载入方块图片 public static BufferedImage I; public static BufferedImage J; public static BufferedImage L; public static BufferedImage O; public static BufferedImage S; public static BufferedImage T; public static BufferedImage Z; public static BufferedImage background; static { try { I = ImageIO.read(new File("images/I.png")); J = ImageIO.read(new File("images/J.png")); L = ImageIO.read(new File("images/L.png")); O = ImageIO.read(new File("images/O.png")); S = ImageIO.read(new File("images/S.png")); T = ImageIO.read(new File("images/T.png")); Z = ImageIO.read(new File("images/Z.png")); background = ImageIO.read(new File("images/background.png")); } catch (IOException e) { e.printStackTrace(); } } @Override public void paint(Graphics g) { g.drawImage(background, 0, 0, null); //平移坐标轴 g.translate(22, 15); //绘制游戏主区域 paintWall(g); //绘制正在下落的四方格 paintCurrentOne(g); //绘制下一个将要下落的四方格 paintNextOne(g); //绘制游戏得分 paintSource(g); //绘制当前游戏状态 paintState(g); } public void start() { game_state = PLING; KeyListener l = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { int code = e.getKeyCode(); switch (code) { case KeyEvent.VK_DOWN: sortDropActive(); break; case KeyEvent.VK_LEFT: moveleftActive(); break; case KeyEvent.VK_RIGHT: moveRightActive(); break; case KeyEvent.VK_UP: rotateRightActive(); break; case KeyEvent.VK_SPACE: hadnDropActive(); break; case KeyEvent.VK_P: //判断当前游戏状态 if (game_state == PLING) { game_state = STOP; } break; case KeyEvent.VK_C: if (game_state == STOP) { game_state = PLING; } break; case KeyEvent.VK_S: //重新开始 game_state = PLING; wall = new Cell[18][9]; currentOne = Tetromino.randomOne(); nextOne = Tetromino.randomOne(); totalScore = 0; totalLine = 0; break; } } }; //将窗口设置为焦点 this.addKeyListener(l); this.requestFocus(); while (true) { if (game_state == PLING) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (camDrop()) { currentOne.moveDrop(); } else { landToWall(); destroyLine(); if (isGameOver()) { game_state = OVER; } else { //游戏没有结束 currentOne = nextOne; nextOne = Tetromino.randomOne(); } } } repaint(); } } //创建顺时针旋转 public void rotateRightActive() { currentOne.rotateRight(); if (outOFBounds() || coincide()) { currentOne.rotateLeft(); } } //瞬间下落 public void hadnDropActive() { while (true) { //判断能否下落 if (camDrop()) { currentOne.moveDrop(); } else { break; } } //嵌入到墙中 landToWall(); destroyLine(); if (isGameOver()) { game_state = OVER; } else { //游戏没有结束 currentOne = nextOne; nextOne = Tetromino.randomOne(); } } //按键一次,下落一格 public void sortDropActive() { if (camDrop()) { //当前四方格下落一格 currentOne.moveDrop(); } else { landToWall(); destroyLine(); if (isGameOver()) { game_state = OVER; } else { //游戏没有结束 currentOne = nextOne; nextOne = Tetromino.randomOne(); } } } //单元格嵌入墙中 private void landToWall() { Cell[] cells = currentOne.cells; for (Cell cell : cells) { int row = cell.getRow(); int col = cell.getCol(); wall[row][col] = cell; } } //判断四方格能否下落 public boolean camDrop() { Cell[] cells = currentOne.cells; for (Cell cell : cells) { int row = cell.getRow(); int col = cell.getCol(); //判断是否到达底部 if (row == wall.length - 1) { return false; } else if (wall[row + 1][col] != null) { return false; } } return true; } //消除行 public void destroyLine() { int line = 0; Cell[] cells = currentOne.cells; for (Cell cell : cells) { int row = cell.getRow(); if (isFullLine(row)) { line++; for (int i = row; i > 0; i--) { System.arraycopy(wall[i - 1], 0, wall[i], 0, wall[0].length); } wall[0] = new Cell[9]; } } //分数池获取分数,累加到总分 totalScore += scores_pool[line]; //总行数 totalLine += line; } //判断当前行是否已经满了 public boolean isFullLine(int row) { Cell[] cells = wall[row]; for (Cell cell : cells) { if (cell == null) { return false; } } return true; } //判断游戏是否结束 public boolean isGameOver() { Cell[] cells = nextOne.cells; for (Cell cell : cells) { int row = cell.getRow(); int col = cell.getCol(); if (wall[row][col] != null) { return true; } } return false; } private void paintState(Graphics g) { if (game_state == PLING) { g.drawString(show_state[PLING], 500, 660); } else if (game_state == STOP) { g.drawString(show_state[STOP], 500, 660); } else { g.drawString(show_state[OVER], 500, 660); g.setColor(Color.RED); g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 60)); g.drawString("GAME OVER!", 30, 400); } } private void paintSource(Graphics g) { g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 30)); g.drawString("分数: " + totalScore, 500, 250); g.drawString("行数: " + totalLine, 500, 430); } private void paintNextOne(Graphics g) { Cell[] cells = nextOne.cells; for (Cell cell : cells) { int x = cell.getCol() * CELL_SIZE + 370; int y = cell.getRow() * CELL_SIZE + 25; g.drawImage(cell.getImage(), x, y, null); } } private void paintCurrentOne(Graphics g) { Cell[] cells = currentOne.cells; for (Cell cell : cells) { int x = cell.getCol() * CELL_SIZE; int y = cell.getRow() * CELL_SIZE; g.drawImage(cell.getImage(), x, y, null); } } private void paintWall(Graphics g) { for (int i = 0; i < wall.length; i++) { for (int j = 0; j < wall[i].length; j++) { int x = j * CELL_SIZE; int y = i * CELL_SIZE; Cell cell = wall[i][j]; //判断是否有小方块 if (cell == null) { g.drawRect(x, y, CELL_SIZE, CELL_SIZE); } else { g.drawImage(cell.getImage(), x, y, null); } } } } //判断是否出界 public boolean outOFBounds() { Cell[] cells = currentOne.cells; for (Cell cell : cells) { int col = cell.getCol(); int row = cell.getRow(); if (row < 0 || row > wall.length - 1 || col < 0 || col > wall[0].length-1) { return true; } } return false; } //按键一次,左移一次 public void moveleftActive() { currentOne.moveLeft(); //判断是否越界或重合 if (outOFBounds() || coincide()) { currentOne.moveRight(); } } //按键一次,右移一次 public void moveRightActive() { currentOne.moveRight(); //判断是否越界或重合 if (outOFBounds() || coincide()) { currentOne.moveLeft(); } } //判断是否重合 public boolean coincide() { Cell[] cells = currentOne.cells; for (Cell cell : cells) { int row = cell.getRow(); int col = cell.getCol(); if (wall[row][col] != null) { return true; } } return false; } public static void main(String[] args) { JFrame jFrame = new JFrame("俄罗斯方块"); //创建游戏界面 Tetris panel = new Tetris(); jFrame.add(panel); //设置可见 jFrame.setVisible(true); //设置窗口大小 jFrame.setSize(810, 940); //设置剧中 jFrame.setLocationRelativeTo(null); //设置窗口关闭时停止 jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //游戏主要开始逻辑 panel.start(); } }
三、效果展示
游戏开始,方快下落,右边区域展示即将下落的方块图、分数、消除的行数以及游戏切换的状态。
按下空格键,方块瞬间下落, 按下P键游戏暂停,消除一行分数为1(此处由分数池进行控制)
按下C键游戏继续。
方块占满,游戏结束,此时可以按下S键重新开始游戏。