贪吃蛇和俄罗斯方块

贪吃蛇

一、创建新项目

创建一个新的项目,并命名。

创建一个名为images的文件夹用来存放游戏相关图片。

然后再在项目的src文件下创建一个com.xxx.view的包用来存放所有的图形界面类,

创建一个com.xxx.controller的包用来存放启动的入口类(控制类)

二、游戏界面 

 
  1. package com.snake.view;

  2. import java.awt.Color;

  3. import java.awt.EventQueue;

  4. import java.awt.Font;

  5. import java.awt.Frame;

  6. import java.awt.Graphics;

  7. import java.awt.Image;

  8. import java.util.ArrayList;

  9. import java.util.List;

  10. import java.util.Random;

  11. import javax.swing.ImageIcon;

  12. import javax.swing.JFrame;

  13. import javax.swing.JOptionPane;

  14. import javax.swing.JPanel;

  15. import javax.swing.Timer;

  16. import javax.swing.border.EmptyBorder;

  17. import java.awt.event.ActionEvent;

  18. import java.awt.event.ActionListener;

  19. import java.awt.event.KeyAdapter;

  20. import java.awt.event.KeyEvent;

  21. public class SnakeJPanel extends JPanel implements ActionListener{

  22. private boolean start;//当前游戏状态

  23. private int speed;//速度

  24. private boolean exist;//当前是否存在食物

  25. private int foodType;//食物种类

  26. private int x;//豆子的横坐标

  27. private int y;//豆子的纵坐标

  28. private ArrayList<int[]> localList;//蛇

  29. public String direction;//方向

  30. private String direction2;//引导方向

  31. public boolean flag;

  32. Random rand = new Random();

  33. private ImageIcon up;

  34. private ImageIcon down;

  35. private ImageIcon right;

  36. private ImageIcon left;

  37. private ImageIcon body;

  38. private ImageIcon food;

  39. private ImageIcon title;

  40. Timer time;

  41. private int score;//当前得分情况

  42. private int num;//吃到的食物个数

  43. // private Image offScreenImage; //图形缓存

  44. //图片绘制

  45. @Override

  46. public void paint(Graphics g) {

  47. direction = direction2;

  48. g.setColor(Color.WHITE);

  49. g.fillRect(0, 0, 900, 700);

  50. //绘制游戏框

  51. //标题框

  52. // g.drawRect(25, 30, 800, 75);

  53. title.paintIcon(this, g, 25, 10);

  54. //内容框

  55. g.setColor(Color.black);

  56. g.fillRect(25, 75, 850, 600);

  57. //绘制食物的坐标位置

  58. if(!exist) {//如果当前不存在豆子,随机绘制一个豆子

  59. if(num % 5 == 0) {

  60. foodType = 1;

  61. }else {

  62. foodType = 0;

  63. }

  64. boolean isProduce = true;

  65. while(isProduce) {

  66. isProduce = false;

  67. x = rand.nextInt(33) * 25 + 25;

  68. y = rand.nextInt(23) * 25 + 75;

  69. for (int[] arr:localList) {

  70. if(x == arr[0] && y == arr[1]) {

  71. isProduce = true;

  72. break;

  73. }

  74. }

  75. }

  76. System.out.println(x + "---" + y);

  77. }

  78. if(eat()) {

  79. exist = false;

  80. }else {

  81. exist = true;

  82. }

  83. if(foodType == 0) {

  84. //绘制食物

  85. g.setColor(Color.blue);

  86. // g.fillRect(x, y, 25, 25);

  87. g.drawImage(food.getImage(),x, y, 25, 25,null);

  88. }else {

  89. //绘制食物

  90. g.setColor(Color.WHITE);

  91. g.fillRect(x, y, 25, 25);

  92. // g.drawImage(food.getImage(),x, y, 25, 25,null);

  93. }

  94. //绘制头

  95. g.setColor(Color.red);

  96. // g.fillRect(localList.get(0)[0], localList.get(0)[1], 25, 25);

  97. ImageIcon head = null;

  98. //判断当前方向

  99. if(direction.equals("R")) {

  100. head = right;

  101. }else if(direction.equals("L")) {

  102. head = left;

  103. }else if(direction.equals("U")) {

  104. head = up;

  105. }else if(direction.equals("D")) {

  106. head = down;

  107. }

  108. // g.drawImage(head.getImage(), localList.get(0)[0], localList.get(0)[1], 25, 25,null);

  109. head.paintIcon(this, g,localList.get(0)[0], localList.get(0)[1]);

  110. //绘制身体

  111. g.setColor(Color.white);

  112. for (int i = 1; i < localList.size(); i++) {

  113. // g.fillRect(localList.get(i)[0], localList.get(i)[1], 25, 25);

  114. // g.drawImage(body.getImage(), localList.get(i)[0], localList.get(i)[1], 25, 25,null);

  115. body.paintIcon(this, g, localList.get(i)[0], localList.get(i)[1]);

  116. }

  117. // g.fillRect(localList.get(1)[0], localList.get(1)[1], 25, 25);

  118. // g.fillRect(localList.get(2)[0], localList.get(2)[1], 25, 25);

  119. //绘制分数和长度

  120. //长度

  121. g.setColor(Color.GREEN);

  122. g.setFont(new Font("宋体", Font.BOLD, 18));

  123. g.drawString("长度:" + (localList.size() - 1), 25, 30);

  124. //分数

  125. g.drawString("分数:" + score, 25, 48);

  126. if(!start) {//如果游戏未启动,结束移动和重绘

  127. g.setColor(Color.white);

  128. g.setFont(new Font("宋体", Font.BOLD, 30));

  129. g.drawString("暂停/开始(请按任意键开始,空格键暂停)", 150, 300);

  130. time.stop();

  131. }else {

  132. time.start();

  133. }

  134. // speed();

  135. //移动后进行下一次绘制

  136. // move();//移动

  137. // repaint();//重新绘制

  138. }

  139. // //解决闪烁问题

  140. // //如果为JFrame 为重量级 程序不会调用update()方法

  141. // //如果为Frame 为轻量级 重写update()方法 做双缓冲

  142. // //如果为JPanel 不会闪烁

  143. // @Override

  144. // public void update(Graphics g)

  145. // {

  146. // System.out.println("update");

  147. // if(offScreenImage == null)

  148. // offScreenImage = this.createImage(900, 700); //新建一个图像缓存空间,这里图像大小为800*600

  149. // Graphics gImage = offScreenImage.getGraphics(); //把它的画笔拿过来,给gImage保存着

  150. // paint(gImage); //将要画的东西画到图像缓存空间去

  151. // g.drawImage(offScreenImage, 0, 0, null); //然后一次性显示出来

  152. // }

  153. @Override

  154. public void actionPerformed(ActionEvent e) {

  155. //移动后进行下一次绘制

  156. move();//移动

  157. repaint();//重新绘制

  158. }

  159. /**

  160. * 绘制速度

  161. */

  162. // private void speed() {

  163. // try {//按一定速度进行移动

  164. // Thread.sleep(speed);//控制移动速度

  165. // } catch (InterruptedException e) {

  166. // // TODO 自动生成的 catch 块

  167. // e.printStackTrace();

  168. // }

  169. // }

  170. /**

  171. * 初始化图片

  172. */

  173. private void drawImage() {

  174. up = new ImageIcon("images/up.png");

  175. down = new ImageIcon("images/down.png");

  176. right = new ImageIcon("images/right.png");

  177. left = new ImageIcon("images/left.png");

  178. body = new ImageIcon("images/body.png");

  179. food = new ImageIcon("images/food.png");

  180. title = new ImageIcon("images/title.jpg");

  181. }

  182. private boolean eat() {

  183. if(localList.get(0)[0] == x && localList.get(0)[1] == y) {//如果当前蛇头吃到了豆子

  184. System.out.println("eat");

  185. num++;

  186. if(foodType == 0) {

  187. score += 10;

  188. }else {

  189. score += (rand.nextInt(5) * 10 + 10);

  190. }

  191. int last = localList.size() - 1;//蛇尾

  192. //在蛇尾后面添加一节身体

  193. localList.add(new int[] {localList.get(last)[0],localList.get(last)[1]});

  194. return true;

  195. }

  196. return false;

  197. }

  198. //移动方法

  199. public void move() {

  200. //判断是否游戏结束

  201. if(isbody()) {

  202. System.out.println("game over");

  203. start = false;//结束游戏移动

  204. JOptionPane.showMessageDialog(null,"游戏已结束!");

  205. time.stop();

  206. init();

  207. }

  208. if(flag && localList != null) {//如果长度不为空且游戏未结束

  209. int last = localList.size() - 1;//记录蛇尾

  210. for (int i = last; i > 0; i--) {//从蛇尾开始,每节身体移动到前一节身体的位置上

  211. localList.set(i,new int[] {localList.get(i - 1)[0],localList.get(i - 1)[1]});

  212. }

  213. //记录头位置

  214. int[] local = localList.get(0);

  215. //判断当前方向,并进行模拟移动,判断是否与边界重合

  216. if(direction.equals("R")) {

  217. if(local[0] >= 850) {

  218. local[0] = 25;

  219. }else {

  220. local[0] += 25;

  221. }

  222. }else if(direction.equals("L")) {

  223. if(local[0] <= 25) {

  224. local[0] = 850;

  225. }else {

  226. local[0] -= 25;

  227. }

  228. }else if(direction.equals("U")) {

  229. if(local[1] <= 75) {

  230. local[1] = 650;

  231. }else {

  232. local[1] -= 25;

  233. }

  234. }else if(direction.equals("D")) {

  235. if(local[1] >= 650) {

  236. local[1] = 75;

  237. }else {

  238. local[1] += 25;

  239. }

  240. }

  241. //更改头的位置

  242. localList.set(0, local);

  243. }

  244. }

  245. //判断下一步是否为蛇身

  246. private boolean isbody() {

  247. // TODO 自动生成的方法存根

  248. //记录头位置

  249. int x = localList.get(0)[0];

  250. int y = localList.get(0)[1];

  251. //判断当前方向,并进行模拟移动,判断是否与边界重合

  252. if(direction.equals("R")) {

  253. x += 25;

  254. }else if(direction.equals("L")) {

  255. x -= 25;

  256. }else if(direction.equals("U")) {

  257. y -= 25;

  258. }else if(direction.equals("D")) {

  259. y += 25;

  260. }

  261. for (int i = 1; i < localList.size(); i++) {

  262. if(localList.get(i)[0] == x && localList.get(i)[1] == y) {

  263. return true;

  264. }

  265. }

  266. return false;

  267. }

  268. // //判断下一步是否为边界

  269. // private boolean isborder() {

  270. // // TODO 自动生成的方法存根

  271. // //记录头位置

  272. // // TODO 自动生成的方法存根

  273. // //记录头位置

  274. // int x = localList.get(0)[0];

  275. // int y = localList.get(0)[1];

  276. //

  277. // //判断当前方向,并进行模拟移动,判断是否与边界重合

  278. // if(direction.equals("R")) {

  279. // x += 25;

  280. // }else if(direction.equals("L")) {

  281. // x -= 25;

  282. // }else if(direction.equals("U")) {

  283. // y -= 25;

  284. // }else if(direction.equals("D")) {

  285. // y += 25;

  286. // }

  287. //

  288. // if(x < 25 || x > (33 * 25 + 25)) {

  289. // return true;//当x坐标超出边界,则返回true

  290. // }

  291. // if(y < 105 || y > (23 * 25 + 105)) {

  292. // return true;//当y坐标超出边界,则返回true

  293. // }

  294. // return false;//蛇头移动后未超出边界,返回false

  295. //

  296. // }

  297. /**

  298. * Create the frame.

  299. */

  300. public SnakeJPanel(int speed) {

  301. this.speed = speed; //初始化速度

  302. //初始化游戏面板的基本信息

  303. this.setSize(900, 700);

  304. this.setLocation(0, 30);

  305. this.setFocusable(true);

  306. init();//初始化界面

  307. drawImage();//绘制图片

  308. moveByKey();//给界面添加一个键盘监听

  309. }

  310. /*

  311. * 键盘监听

  312. * 通过键盘输入上下左右来控制当前蛇头移动的方向

  313. * 先判断当前蛇头方向,再来改变引导方向

  314. * 当进行绘制时再修改蛇的方向

  315. * 保证不会因为在短时间内快速变换方向导致蛇头逆向转向

  316. */

  317. private void moveByKey() {

  318. addKeyListener(new KeyAdapter() {

  319. @Override

  320. public void keyPressed(KeyEvent e) {

  321. int key = e.getKeyCode();

  322. //边界值判断

  323. switch(key) {

  324. case 65:

  325. case 37:{//向左走

  326. if(!direction.equals("R")) {

  327. direction2 = "L";

  328. }

  329. break;

  330. }

  331. case 87:

  332. case 38:{//向上走

  333. if(!direction.equals("D")) {

  334. direction2 = "U";

  335. }

  336. break;

  337. }

  338. case 68:

  339. case 39:{//向右走

  340. if(!direction.equals("L")) {

  341. direction2 = "R";

  342. }

  343. break;

  344. }

  345. case 83:

  346. case 40:{//向下走

  347. if(!direction.equals("U")) {

  348. direction2 = "D";

  349. }

  350. break;

  351. }

  352. case KeyEvent.VK_SPACE:{//如果当前键盘输入为空格

  353. start = !start;//调整游戏状态

  354. System.out.println("暂停/开始");

  355. repaint();//重绘

  356. }

  357. }

  358. //任意键开始

  359. if(!start && key != KeyEvent.VK_SPACE) {//如果当前状态为暂停状态,且键盘输入不是空格

  360. start = true;

  361. repaint();//重绘

  362. }

  363. }

  364. });

  365. }

  366. /**

  367. * 初始化游戏基本信息

  368. */

  369. private void init() {

  370. start = false;

  371. exist = true;

  372. direction2 = "U";

  373. flag = true;

  374. localList = new ArrayList<int[]>();

  375. localList.add(0,new int[] {75,125});//蛇头

  376. localList.add(1,new int[] {75,150});//蛇身1

  377. localList.add(2,new int[] {75,175});//蛇身2

  378. //创建第一个食物的位置

  379. //通过循环保证当前生成的食物不在身体所在的坐标上

  380. boolean isProduce = true;

  381. while(isProduce) {//循环生成食物坐标

  382. isProduce = false;//结束本次循环

  383. x = rand.nextInt(33) * 25 + 25;

  384. y = rand.nextInt(23) * 25 + 75;

  385. for (int[] arr:localList) {//循环遍历蛇头及蛇身的坐标

  386. if(x == arr[0] && y == arr[1]) {//如果食物坐标和蛇的某一节坐标重合

  387. isProduce = true;//跳转循环状态,继续下一次食物生成

  388. break;

  389. }

  390. }

  391. //蛇身遍历完成,没有重合坐标,结束食物坐标生成

  392. }

  393. time = new Timer(speed, this);

  394. setLayout(null);

  395. score = 0;

  396. num = 0;

  397. foodType = 0;

  398. // repaint();

  399. }

  400. }

三、构造启动类

 
  1. package com.snake.controller;

  2. import javax.swing.JFrame;

  3. import javax.swing.JOptionPane;

  4. import com.snake.view.SnakeJPanel;

  5. public class SnakeStart {

  6. public static void main(String[] args) {

  7. int speed = 0;

  8. String showInputDialog = null;//初始化时间

  9. //得到速度

  10. while(true) {

  11. showInputDialog = JOptionPane.showInputDialog("蛇移动速度(1 - 5)","3");

  12. if(showInputDialog == null) {

  13. showInputDialog = "3";//默认速度

  14. break;

  15. }

  16. if(showInputDialog.length() > 1) {

  17. continue;

  18. }

  19. char[] a = showInputDialog.toCharArray();

  20. if(a[0] >= '1' && a[0] <= '5') {

  21. break;

  22. }

  23. }

  24. speed = Integer.parseInt(showInputDialog) * 50;

  25. SnakeJPanel snakeJPanel = new SnakeJPanel(speed);

  26. //创建一个JFrame窗口,将游戏面板添加进行窗口中

  27. JFrame jFrame = new JFrame();

  28. //设置窗口的某些属性

  29. jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  30. jFrame.setSize(920, 750);

  31. jFrame.add(snakeJPanel);

  32. jFrame.setLocationRelativeTo(null);

  33. jFrame.setVisible(true);

  34. }

  35. }

四、游戏启动

设置游戏速度

游戏界面

 俄罗斯方块

游戏规则:

由小方块组成的不同形状的板块陆续从屏幕上方落下来,玩家通过调整板块的位置和方向,使它们在屏幕底部拼出完整的一条或几条。这些完整的横条会随即消失,给新落下来的板块腾出空间,与此同时,玩家得到分数奖励。没有被消除掉的方块不断堆积起来,一旦堆到屏幕顶端,玩家便告输,游戏结束。

整体代码分为三个模块:方格模块,七种图形模块,俄罗斯方块主模块。

小方块类:Cell

 
  1. package com.zhao.demo.block;

  2. import java.awt.image.BufferedImage;

  3. import java.util.Objects;

  4. /**

  5. * @author xiaoZhao

  6. * @date 2022/5/7

  7. * @describe

  8. * 小方块类

  9. * 方法: 左移、右移、下落

  10. */

  11. public class Cell {

  12. // 行

  13. private int row;

  14. // 列

  15. private int col;

  16. private BufferedImage image;

  17. public Cell() {

  18. }

  19. public Cell(int row, int col, BufferedImage image) {

  20. this.row = row;

  21. this.col = col;

  22. this.image = image;

  23. }

  24. public int getRow() {

  25. return row;

  26. }

  27. public void setRow(int row) {

  28. this.row = row;

  29. }

  30. public int getCol() {

  31. return col;

  32. }

  33. public void setCol(int col) {

  34. this.col = col;

  35. }

  36. public BufferedImage getImage() {

  37. return image;

  38. }

  39. public void setImage(BufferedImage image) {

  40. this.image = image;

  41. }

  42. @Override

  43. public String toString() {

  44. return "Cell{" +

  45. "row=" + row +

  46. ", col=" + col +

  47. ", image=" + image +

  48. '}';

  49. }

  50. @Override

  51. public boolean equals(Object o) {

  52. if (this == o) {

  53. return true;

  54. }

  55. if (!(o instanceof Cell)) {

  56. return false;

  57. }

  58. Cell cell = (Cell) o;

  59. return getRow() == cell.getRow() &&

  60. getCol() == cell.getCol() &&

  61. Objects.equals(getImage(), cell.getImage());

  62. }

  63. @Override

  64. public int hashCode() {

  65. return Objects.hash(getRow(), getCol(), getImage());

  66. }

  67. //左移动一格

  68. public void left(){

  69. col--;

  70. }

  71. //右移动一格

  72. public void right(){

  73. col++;

  74. }

  75. //下移动一格

  76. public void down(){

  77. row++;

  78. }

  79. }

 四方格图形的父类:Tetromino

 
  1. package com.zhao.demo.block;

  2. import com.zhao.demo.shape.*;

  3. /**

  4. * @author xiaoZhao

  5. * @date 2022/5/11

  6. * @describe 编写四方格父类

  7. */

  8. public class Tetromino {

  9. public Cell[] cells = new Cell[4];

  10. //旋转的状态

  11. protected State[] states;

  12. //声明旋转次数

  13. protected int count = 10000;

  14. //左移方法

  15. public void moveLeft() {

  16. for (Cell cell : cells) {

  17. cell.left();

  18. }

  19. }

  20. //右移方法

  21. public void moveRight() {

  22. for (Cell cell : cells) {

  23. cell.right();

  24. }

  25. }

  26. //单元格下落

  27. public void moveDrop() {

  28. for (Cell cell : cells) {

  29. cell.down();

  30. }

  31. }

  32. //编写随机生成四方格

  33. public static Tetromino randomOne() {

  34. int num = (int) (Math.random() * 7);

  35. Tetromino tetromino = null;

  36. switch (num) {

  37. case 0:

  38. tetromino = new I();

  39. break;

  40. case 1:

  41. tetromino = new J();

  42. break;

  43. case 2:

  44. tetromino = new L();

  45. break;

  46. case 3:

  47. tetromino = new O();

  48. break;

  49. case 4:

  50. tetromino = new S();

  51. break;

  52. case 5:

  53. tetromino = new T();

  54. break;

  55. case 6:

  56. tetromino = new Z();

  57. break;

  58. }

  59. return tetromino;

  60. }

  61. //顺时针旋转的方法

  62. public void rotateRight() {

  63. if (states.length == 0) {

  64. return;

  65. }

  66. //旋转次数+1

  67. count++;

  68. State s = states[count % states.length];

  69. Cell cell = cells[0];

  70. int row = cell.getRow();

  71. int col = cell.getCol();

  72. cells[1].setRow(row + s.row1);

  73. cells[1].setCol(col + s.col1);

  74. cells[2].setRow(row + s.row2);

  75. cells[2].setCol(col + s.col2);

  76. cells[3].setRow(row + s.row3);

  77. cells[3].setCol(col + s.col3);

  78. }

  79. //逆时针旋转的方法

  80. public void rotateLeft() {

  81. if (states.length == 0) {

  82. return;

  83. }

  84. //旋转次数+1

  85. count--;

  86. State s = states[count % states.length];

  87. Cell cell = cells[0];

  88. int row = cell.getRow();

  89. int col = cell.getCol();

  90. cells[1].setRow(row + s.row1);

  91. cells[1].setCol(col + s.col1);

  92. cells[2].setRow(row + s.row2);

  93. cells[2].setCol(col + s.col2);

  94. cells[3].setRow(row + s.row3);

  95. cells[3].setCol(col + s.col3);

  96. }

  97. //四方格旋转状态的内部类

  98. protected class State {

  99. //存储四方格各元素的位置

  100. int row0, col0, row1, col1, row2, col2, row3, col3;

  101. public State() {

  102. }

  103. public State(int row0, int col0, int row1, int col1, int row2, int col2, int row3, int col3) {

  104. this.row0 = row0;

  105. this.col0 = col0;

  106. this.row1 = row1;

  107. this.col1 = col1;

  108. this.row2 = row2;

  109. this.col2 = col2;

  110. this.row3 = row3;

  111. this.col3 = col3;

  112. }

  113. public int getRow0() {

  114. return row0;

  115. }

  116. public void setRow0(int row0) {

  117. this.row0 = row0;

  118. }

  119. public int getCol0() {

  120. return col0;

  121. }

  122. public void setCol0(int col0) {

  123. this.col0 = col0;

  124. }

  125. public int getRow1() {

  126. return row1;

  127. }

  128. public void setRow1(int row1) {

  129. this.row1 = row1;

  130. }

  131. public int getCol1() {

  132. return col1;

  133. }

  134. public void setCol1(int col1) {

  135. this.col1 = col1;

  136. }

  137. public int getRow2() {

  138. return row2;

  139. }

  140. public void setRow2(int row2) {

  141. this.row2 = row2;

  142. }

  143. public int getCol2() {

  144. return col2;

  145. }

  146. public void setCol2(int col2) {

  147. this.col2 = col2;

  148. }

  149. public int getRow3() {

  150. return row3;

  151. }

  152. public void setRow3(int row3) {

  153. this.row3 = row3;

  154. }

  155. public int getCol3() {

  156. return col3;

  157. }

  158. public void setCol3(int col3) {

  159. this.col3 = col3;

  160. }

  161. @Override

  162. public String toString() {

  163. return "State{" +

  164. "row0=" + row0 +

  165. ", col0=" + col0 +

  166. ", row1=" + row1 +

  167. ", col1=" + col1 +

  168. ", row2=" + row2 +

  169. ", col2=" + col2 +

  170. ", row3=" + row3 +

  171. ", col3=" + col3 +

  172. '}';

  173. }

  174. }

  175. }

七种图形类:I、J、L、O、S、T、Z

 I

 
  1. package com.zhao.demo.shape;

  2. import com.zhao.demo.App.Tetris;

  3. import com.zhao.demo.block.Cell;

  4. import com.zhao.demo.block.Tetromino;

  5. /**

  6. * @author xiaoZhao

  7. * @date 2022/5/11

  8. * @describe

  9. */

  10. public class I extends Tetromino {

  11. public I() {

  12. cells[0] = new Cell(0,4, Tetris.I);

  13. cells[1] = new Cell(0,3, Tetris.I);

  14. cells[2] = new Cell(0,5, Tetris.I);

  15. cells[3] = new Cell(0,6, Tetris.I);

  16. //共有两种旋转状态

  17. states =new State[2];

  18. //初始化两种状态的相对坐标

  19. states[0]=new State(0,0,0,-1,0,1,0,2);

  20. states[1]=new State(0,0,-1,0,1,0,2,0);

  21. }

  22. }

J

 
  1. package com.zhao.demo.shape;

  2. import com.zhao.demo.App.Tetris;

  3. import com.zhao.demo.block.Cell;

  4. import com.zhao.demo.block.Tetromino;

  5. /**

  6. * @author xiaoZhao

  7. * @date 2022/5/11

  8. * @describe

  9. */

  10. public class J extends Tetromino {

  11. public J() {

  12. cells[0] = new Cell(0,4, Tetris.J);

  13. cells[1] = new Cell(0,3, Tetris.J);

  14. cells[2] = new Cell(0,5, Tetris.J);

  15. cells[3] = new Cell(1,5, Tetris.J);

  16. states=new State[4];

  17. states[0]=new State(0,0,0,-1,0,1,1,1);

  18. states[1]=new State(0,0,-1,0,1,0,1,-1);

  19. states[2]=new State(0,0,0,1,0,-1,-1,-1);

  20. states[3]=new State(0,0,1,0,-1,0,-1,1);

  21. }

  22. }

 L

 
  1. package com.zhao.demo.shape;

  2. import com.zhao.demo.App.Tetris;

  3. import com.zhao.demo.block.Cell;

  4. import com.zhao.demo.block.Tetromino;

  5. /**

  6. * @author xiaoZhao

  7. * @date 2022/5/11

  8. * @describe

  9. */

  10. public class L extends Tetromino {

  11. public L() {

  12. cells[0] = new Cell(0,4, Tetris.L);

  13. cells[1] = new Cell(0,3, Tetris.L);

  14. cells[2] = new Cell(0,5, Tetris.L);

  15. cells[3] = new Cell(1,3, Tetris.L);

  16. states=new State[4];

  17. states[0]=new State(0,0,0,-1,0,1,1,-1);

  18. states[1]=new State(0,0,-1,0,1,0,-1,-1);

  19. states[2]=new State(0,0,0,1,0,-1,-1,1);

  20. states[3]=new State(0,0,1,0,-1,0,1,1);

  21. }

  22. }

O

 
  1. package com.zhao.demo.shape;

  2. import com.zhao.demo.App.Tetris;

  3. import com.zhao.demo.block.Cell;

  4. import com.zhao.demo.block.Tetromino;

  5. /**

  6. * @author xiaoZhao

  7. * @date 2022/5/11

  8. * @describe

  9. */

  10. public class O extends Tetromino {

  11. public O() {

  12. cells[0] = new Cell(0, 4, Tetris.O);

  13. cells[1] = new Cell(0, 5, Tetris.O);

  14. cells[2] = new Cell(1, 4, Tetris.O);

  15. cells[3] = new Cell(1, 5, Tetris.O);

  16. //无旋转状态

  17. states = new State[0];

  18. }

  19. }

 
  1. package com.zhao.demo.shape;

  2. import com.zhao.demo.App.Tetris;

  3. import com.zhao.demo.block.Cell;

  4. import com.zhao.demo.block.Tetromino;

  5. /**

  6. * @author xiaoZhao

  7. * @date 2022/5/11

  8. * @describe

  9. */

  10. public class S extends Tetromino {

  11. public S() {

  12. cells[0] = new Cell(0,4, Tetris.S);

  13. cells[1] = new Cell(0,5, Tetris.S);

  14. cells[2] = new Cell(1,3, Tetris.S);

  15. cells[3] = new Cell(1,4, Tetris.S);

  16. //共有两种旋转状态

  17. states =new State[2];

  18. //初始化两种状态的相对坐标

  19. states[0]=new State(0,0,0,1,1,-1,1,0);

  20. states[1]=new State(0,0,1,0,-1,-1,0,-1);

  21. }

  22. }

 
  1. package com.zhao.demo.shape;

  2. import com.zhao.demo.App.Tetris;

  3. import com.zhao.demo.block.Cell;

  4. import com.zhao.demo.block.Tetromino;

  5. /**

  6. * @author xiaoZhao

  7. * @date 2022/5/11

  8. * @describe

  9. */

  10. public class T extends Tetromino {

  11. public T() {

  12. cells[0] = new Cell(0,4, Tetris.T);

  13. cells[1] = new Cell(0,3, Tetris.T);

  14. cells[2] = new Cell(0,5, Tetris.T);

  15. cells[3] = new Cell(1,4, Tetris.T);

  16. states=new State[4];

  17. states[0]=new State(0,0,0,-1,0,1,1,0);

  18. states[1]=new State(0,0,-1,0,1,0,0,-1);

  19. states[2]=new State(0,0,0,1,0,-1,-1,0);

  20. states[3]=new State(0,0,1,0,-1,0,0,1);

  21. }

  22. }

 
  1. package com.zhao.demo.shape;

  2. import com.zhao.demo.App.Tetris;

  3. import com.zhao.demo.block.Cell;

  4. import com.zhao.demo.block.Tetromino;

  5. /**

  6. * @author xiaoZhao

  7. * @date 2022/5/11

  8. * @describe

  9. */

  10. public class Z extends Tetromino {

  11. public Z() {

  12. cells[0] = new Cell(1,4, Tetris.Z);

  13. cells[1] = new Cell(0,3, Tetris.Z);

  14. cells[2] = new Cell(0,4, Tetris.Z);

  15. cells[3] = new Cell(1,5, Tetris.Z);

  16. //共有两种旋转状态

  17. states =new State[2];

  18. //初始化两种状态的相对坐标

  19. states[0]=new State(0,0,-1,-1,-1,0,0,1);

  20. states[1]=new State(0,0,-1,1,0,1,1,0);

  21. }

  22. }

俄罗斯方块游戏主类:Tetris

 
  1. package com.zhao.demo.App;

  2. import com.zhao.demo.block.Cell;

  3. import com.zhao.demo.block.Tetromino;

  4. import javax.imageio.ImageIO;

  5. import javax.swing.*;

  6. import java.awt.*;

  7. import java.awt.event.KeyAdapter;

  8. import java.awt.event.KeyEvent;

  9. import java.awt.event.KeyListener;

  10. import java.awt.image.BufferedImage;

  11. import java.io.File;

  12. import java.io.IOException;

  13. import java.security.cert.Certificate;

  14. /**

  15. * @author xiaoZhao

  16. * @date 2022/5/11

  17. * @describe 俄罗斯方块游戏主类

  18. */

  19. public class Tetris extends JPanel {

  20. //正在下落的方块

  21. private Tetromino currentOne = Tetromino.randomOne();

  22. //将要下落的方块

  23. private Tetromino nextOne = Tetromino.randomOne();

  24. //游戏主区域

  25. private Cell[][] wall = new Cell[18][9];

  26. //声明单元格的值

  27. private static final int CELL_SIZE = 48;

  28. //游戏分数池

  29. int[] scores_pool = {0, 1, 2, 5, 10};

  30. //当前游戏的分数

  31. private int totalScore = 0;

  32. //当前消除的行数

  33. private int totalLine = 0;

  34. //游戏三种状态 游戏中、暂停、结束

  35. public static final int PLING = 0;

  36. public static final int STOP = 1;

  37. public static final int OVER = 2;

  38. //当前游戏状态值

  39. private int game_state;

  40. //显示游戏状态

  41. String[] show_state = {"P[pause]", "C[continue]", "S[replay]"};

  42. //载入方块图片

  43. public static BufferedImage I;

  44. public static BufferedImage J;

  45. public static BufferedImage L;

  46. public static BufferedImage O;

  47. public static BufferedImage S;

  48. public static BufferedImage T;

  49. public static BufferedImage Z;

  50. public static BufferedImage background;

  51. static {

  52. try {

  53. I = ImageIO.read(new File("images/I.png"));

  54. J = ImageIO.read(new File("images/J.png"));

  55. L = ImageIO.read(new File("images/L.png"));

  56. O = ImageIO.read(new File("images/O.png"));

  57. S = ImageIO.read(new File("images/S.png"));

  58. T = ImageIO.read(new File("images/T.png"));

  59. Z = ImageIO.read(new File("images/Z.png"));

  60. background = ImageIO.read(new File("images/background.png"));

  61. } catch (IOException e) {

  62. e.printStackTrace();

  63. }

  64. }

  65. @Override

  66. public void paint(Graphics g) {

  67. g.drawImage(background, 0, 0, null);

  68. //平移坐标轴

  69. g.translate(22, 15);

  70. //绘制游戏主区域

  71. paintWall(g);

  72. //绘制正在下落的四方格

  73. paintCurrentOne(g);

  74. //绘制下一个将要下落的四方格

  75. paintNextOne(g);

  76. //绘制游戏得分

  77. paintSource(g);

  78. //绘制当前游戏状态

  79. paintState(g);

  80. }

  81. public void start() {

  82. game_state = PLING;

  83. KeyListener l = new KeyAdapter() {

  84. @Override

  85. public void keyPressed(KeyEvent e) {

  86. int code = e.getKeyCode();

  87. switch (code) {

  88. case KeyEvent.VK_DOWN:

  89. sortDropActive();

  90. break;

  91. case KeyEvent.VK_LEFT:

  92. moveleftActive();

  93. break;

  94. case KeyEvent.VK_RIGHT:

  95. moveRightActive();

  96. break;

  97. case KeyEvent.VK_UP:

  98. rotateRightActive();

  99. break;

  100. case KeyEvent.VK_SPACE:

  101. hadnDropActive();

  102. break;

  103. case KeyEvent.VK_P:

  104. //判断当前游戏状态

  105. if (game_state == PLING) {

  106. game_state = STOP;

  107. }

  108. break;

  109. case KeyEvent.VK_C:

  110. if (game_state == STOP) {

  111. game_state = PLING;

  112. }

  113. break;

  114. case KeyEvent.VK_S:

  115. //重新开始

  116. game_state = PLING;

  117. wall = new Cell[18][9];

  118. currentOne = Tetromino.randomOne();

  119. nextOne = Tetromino.randomOne();

  120. totalScore = 0;

  121. totalLine = 0;

  122. break;

  123. }

  124. }

  125. };

  126. //将窗口设置为焦点

  127. this.addKeyListener(l);

  128. this.requestFocus();

  129. while (true) {

  130. if (game_state == PLING) {

  131. try {

  132. Thread.sleep(500);

  133. } catch (InterruptedException e) {

  134. e.printStackTrace();

  135. }

  136. if (camDrop()) {

  137. currentOne.moveDrop();

  138. } else {

  139. landToWall();

  140. destroyLine();

  141. if (isGameOver()) {

  142. game_state = OVER;

  143. } else {

  144. //游戏没有结束

  145. currentOne = nextOne;

  146. nextOne = Tetromino.randomOne();

  147. }

  148. }

  149. }

  150. repaint();

  151. }

  152. }

  153. //创建顺时针旋转

  154. public void rotateRightActive() {

  155. currentOne.rotateRight();

  156. if (outOFBounds() || coincide()) {

  157. currentOne.rotateLeft();

  158. }

  159. }

  160. //瞬间下落

  161. public void hadnDropActive() {

  162. while (true) {

  163. //判断能否下落

  164. if (camDrop()) {

  165. currentOne.moveDrop();

  166. } else {

  167. break;

  168. }

  169. }

  170. //嵌入到墙中

  171. landToWall();

  172. destroyLine();

  173. if (isGameOver()) {

  174. game_state = OVER;

  175. } else {

  176. //游戏没有结束

  177. currentOne = nextOne;

  178. nextOne = Tetromino.randomOne();

  179. }

  180. }

  181. //按键一次,下落一格

  182. public void sortDropActive() {

  183. if (camDrop()) {

  184. //当前四方格下落一格

  185. currentOne.moveDrop();

  186. } else {

  187. landToWall();

  188. destroyLine();

  189. if (isGameOver()) {

  190. game_state = OVER;

  191. } else {

  192. //游戏没有结束

  193. currentOne = nextOne;

  194. nextOne = Tetromino.randomOne();

  195. }

  196. }

  197. }

  198. //单元格嵌入墙中

  199. private void landToWall() {

  200. Cell[] cells = currentOne.cells;

  201. for (Cell cell : cells) {

  202. int row = cell.getRow();

  203. int col = cell.getCol();

  204. wall[row][col] = cell;

  205. }

  206. }

  207. //判断四方格能否下落

  208. public boolean camDrop() {

  209. Cell[] cells = currentOne.cells;

  210. for (Cell cell : cells) {

  211. int row = cell.getRow();

  212. int col = cell.getCol();

  213. //判断是否到达底部

  214. if (row == wall.length - 1) {

  215. return false;

  216. } else if (wall[row + 1][col] != null) {

  217. return false;

  218. }

  219. }

  220. return true;

  221. }

  222. //消除行

  223. public void destroyLine() {

  224. int line = 0;

  225. Cell[] cells = currentOne.cells;

  226. for (Cell cell : cells) {

  227. int row = cell.getRow();

  228. if (isFullLine(row)) {

  229. line++;

  230. for (int i = row; i > 0; i--) {

  231. System.arraycopy(wall[i - 1], 0, wall[i], 0, wall[0].length);

  232. }

  233. wall[0] = new Cell[9];

  234. }

  235. }

  236. //分数池获取分数,累加到总分

  237. totalScore += scores_pool[line];

  238. //总行数

  239. totalLine += line;

  240. }

  241. //判断当前行是否已经满了

  242. public boolean isFullLine(int row) {

  243. Cell[] cells = wall[row];

  244. for (Cell cell : cells) {

  245. if (cell == null) {

  246. return false;

  247. }

  248. }

  249. return true;

  250. }

  251. //判断游戏是否结束

  252. public boolean isGameOver() {

  253. Cell[] cells = nextOne.cells;

  254. for (Cell cell : cells) {

  255. int row = cell.getRow();

  256. int col = cell.getCol();

  257. if (wall[row][col] != null) {

  258. return true;

  259. }

  260. }

  261. return false;

  262. }

  263. private void paintState(Graphics g) {

  264. if (game_state == PLING) {

  265. g.drawString(show_state[PLING], 500, 660);

  266. } else if (game_state == STOP) {

  267. g.drawString(show_state[STOP], 500, 660);

  268. } else {

  269. g.drawString(show_state[OVER], 500, 660);

  270. g.setColor(Color.RED);

  271. g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 60));

  272. g.drawString("GAME OVER!", 30, 400);

  273. }

  274. }

  275. private void paintSource(Graphics g) {

  276. g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 30));

  277. g.drawString("分数: " + totalScore, 500, 250);

  278. g.drawString("行数: " + totalLine, 500, 430);

  279. }

  280. private void paintNextOne(Graphics g) {

  281. Cell[] cells = nextOne.cells;

  282. for (Cell cell : cells) {

  283. int x = cell.getCol() * CELL_SIZE + 370;

  284. int y = cell.getRow() * CELL_SIZE + 25;

  285. g.drawImage(cell.getImage(), x, y, null);

  286. }

  287. }

  288. private void paintCurrentOne(Graphics g) {

  289. Cell[] cells = currentOne.cells;

  290. for (Cell cell : cells) {

  291. int x = cell.getCol() * CELL_SIZE;

  292. int y = cell.getRow() * CELL_SIZE;

  293. g.drawImage(cell.getImage(), x, y, null);

  294. }

  295. }

  296. private void paintWall(Graphics g) {

  297. for (int i = 0; i < wall.length; i++) {

  298. for (int j = 0; j < wall[i].length; j++) {

  299. int x = j * CELL_SIZE;

  300. int y = i * CELL_SIZE;

  301. Cell cell = wall[i][j];

  302. //判断是否有小方块

  303. if (cell == null) {

  304. g.drawRect(x, y, CELL_SIZE, CELL_SIZE);

  305. } else {

  306. g.drawImage(cell.getImage(), x, y, null);

  307. }

  308. }

  309. }

  310. }

  311. //判断是否出界

  312. public boolean outOFBounds() {

  313. Cell[] cells = currentOne.cells;

  314. for (Cell cell : cells) {

  315. int col = cell.getCol();

  316. int row = cell.getRow();

  317. if (row < 0 || row > wall.length - 1 || col < 0 || col > wall[0].length-1) {

  318. return true;

  319. }

  320. }

  321. return false;

  322. }

  323. //按键一次,左移一次

  324. public void moveleftActive() {

  325. currentOne.moveLeft();

  326. //判断是否越界或重合

  327. if (outOFBounds() || coincide()) {

  328. currentOne.moveRight();

  329. }

  330. }

  331. //按键一次,右移一次

  332. public void moveRightActive() {

  333. currentOne.moveRight();

  334. //判断是否越界或重合

  335. if (outOFBounds() || coincide()) {

  336. currentOne.moveLeft();

  337. }

  338. }

  339. //判断是否重合

  340. public boolean coincide() {

  341. Cell[] cells = currentOne.cells;

  342. for (Cell cell : cells) {

  343. int row = cell.getRow();

  344. int col = cell.getCol();

  345. if (wall[row][col] != null) {

  346. return true;

  347. }

  348. }

  349. return false;

  350. }

  351. public static void main(String[] args) {

  352. JFrame jFrame = new JFrame("俄罗斯方块");

  353. //创建游戏界面

  354. Tetris panel = new Tetris();

  355. jFrame.add(panel);

  356. //设置可见

  357. jFrame.setVisible(true);

  358. //设置窗口大小

  359. jFrame.setSize(810, 940);

  360. //设置剧中

  361. jFrame.setLocationRelativeTo(null);

  362. //设置窗口关闭时停止

  363. jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  364. //游戏主要开始逻辑

  365. panel.start();

  366. }

  367. }

三、效果展示

游戏开始,方快下落,右边区域展示即将下落的方块图、分数、消除的行数以及游戏切换的状态。

按下空格键,方块瞬间下落, 按下P键游戏暂停,消除一行分数为1(此处由分数池进行控制)

  按下C键游戏继续。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值