贪吃蛇
一、创建新项目
创建一个新的项目,并命名。
创建一个名为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();
-
}
-
}
三、构造启动类
-
package 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键游戏继续。