• 贪吃蛇游戏和俄罗斯方块


    一、创建新项目

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

    创建一个名为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. }

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

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

    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

    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. }

    S

    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. }

    Z

    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键游戏继续。

    方块占满,游戏结束,此时可以按下S键重新开始游戏

  • 相关阅读:
    vue filters过滤器分别在template和script中使用
    ABAP学习笔记之——第七章:ABAP数据字典
    Leetcode刷题day2|数组二|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
    VUE day_07(7.25)学子商城项目简略版
    硬件知识积累 PCIE 接口
    EasyX图形库实现贪吃蛇游戏
    IBM MQ 连接属性-示例
    稻草材料优选养牛户制作优质青贮饲料 国稻种芯现代饲料规划
    百度之星第二场T1
    由浅入深,从掌握Promise的基本使用到手写Promise
  • 原文地址:https://blog.csdn.net/2302_76551439/article/details/134426440