在B站看视频的时候,看到下面这个视频就比较感兴趣,跟着手动做了一遍,写下这篇博客,
【狂神说Java】一小时开发贪吃蛇游戏 https://www.bilibili.com/video/BV1HE41127CV?p=1
最终游戏的界面长这个(都是画图做的图片,能力有限)
具体的步骤,可以参照上面的视频听一下,简单易懂,
游戏规则:小蛇初始长度为3,可以上下左右四个方向移动,吃到蓝色的食物,身体长度加一,分数加10,
可以穿过自己的身体,也可以穿过墙壁,(没有死亡,因为触碰身体就死掉的话很不爽,也有这个功能,只不过注释掉了)
玩了一会,之后的界面
之前使用IDEA写的代码,但是一直说的激活码失效了,一直自动退出,就很气,改用eclipse了
下面开始说一下,编写的步骤:
1. 首先创建一个简单的java项目 File -> 右键 - > New -> Project
2.创建以下目录
3,把相关的图片添加到此文件夹下
4. 创建一个 StartGames 类,用来绘制窗口
1 package com.wang.snake; 2 3 import javax.swing.*; 4 5 public class StartGames { 6 public static void main(String[] args) { 7 // 1.绘制一个静态窗口 8 JFrame frame = new JFrame("贪吃蛇小游戏"); 9 // 2.设置界面的大小 10 frame.setBounds(100,10,910,720); 11 // 3.设置窗口不可调整大小 12 frame.setResizable(false); 13 // 4.“X”的关闭事件,关闭窗口 14 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 15 // 5.把面板添加到窗口里 16 frame.add(new GamePanel()); 17 // 6.让窗口可以展示 18 frame.setVisible(true); 19 } 20 }
5.创建Data类,获取添加的图片信息
1 package com.wang.snake; 2 3 import javax.swing.*; 4 import java.net.URL; 5 6 /** 7 * 存放数据 8 */ 9 public class Data { 10 11 // 获取标题栏图片的相对路径 12 public static URL headerURL = Data.class.getResource("/statics/header.png"); 13 // 转换成图片 14 public static ImageIcon header = new ImageIcon(headerURL); 15 16 // 获取小蛇的头部图片 17 public static URL upURL = Data.class.getResource("/statics/up.png"); 18 public static URL rightURL = Data.class.getResource("/statics/right.png"); 19 public static URL downURL = Data.class.getResource("/statics/down.png"); 20 public static URL leftURL = Data.class.getResource("/statics/left.png"); 21 public static ImageIcon upHeader = new ImageIcon(upURL); 22 public static ImageIcon rightHeader = new ImageIcon(rightURL); 23 public static ImageIcon downHeader = new ImageIcon(downURL); 24 public static ImageIcon leftHeader = new ImageIcon(leftURL); 25 26 // 获取小蛇的身体图片 27 public static URL bodyURL = Data.class.getResource("/statics/body.png"); 28 public static ImageIcon body = new ImageIcon(bodyURL); 29 30 // 获取食物的图片 31 public static URL foodURL = Data.class.getResource("/statics/food.png"); 32 public static ImageIcon food = new ImageIcon(foodURL); 33 34 }
6.创建GamePanel类,绘制界面,包含了标题块,游戏块,以及小蛇的移动等功能的实现类
1 package com.wang.snake; 2 3 import javax.swing.*; 4 import java.awt.*; 5 import java.awt.event.ActionEvent; 6 import java.awt.event.ActionListener; 7 import java.awt.event.KeyEvent; 8 import java.awt.event.KeyListener; 9 import java.util.Iterator; 10 import java.util.Random; 11 12 public class GamePanel extends JPanel implements KeyListener, ActionListener { 13 14 // 游戏是否开始flag 15 boolean isStart = false; 16 // 游戏是否失败flag 17 boolean isFail = false; 18 19 //定义一个记分系统 20 int score; 21 22 // 定义一个小蛇 23 String fx; // 定义小蛇头部的朝向 24 int length; // 小蛇的长度 25 int[] snakeX = new int[600]; // 小蛇的X坐标 26 int[] snakeY = new int[500]; // 小蛇的Y坐标 27 28 // 定义一个食物 29 int foodX; // 食物的X坐标 30 int foodY; // 食物的Y坐标 31 // 随机数的对象 32 Random random = new Random(); 33 34 // 定义一个定时器 35 Timer timer = new Timer(100,this); 36 37 //创建构造器,调用init方法, 38 public GamePanel(){ 39 // 小蛇的初始化 40 init(); 41 42 // 获取键盘的监听事件 43 // 获取焦点,默认是不获取焦点的 44 this.setFocusable(true); 45 // 添加监听 46 this.addKeyListener(this); 47 // 启动定时器 48 timer.start(); 49 } 50 51 // 小蛇和食物的初始化的长度及位置 52 public void init(){ 53 // 记分的初期值设置为0 54 score = 0; 55 56 // 小蛇的长度是3个,头部位置在X:100,Y:100的坐标处,朝向右 57 fx = "R"; 58 length = 3; 59 snakeX[0] = 100; 60 snakeY[0] = 100; 61 // 身体部分的坐标,第一部分的位置在X:75,Y:100的坐标处, 62 snakeX[1] = 75; 63 snakeY[1] = 100; 64 // 第二部分的位置在X:50,Y:100的坐标处, 65 snakeX[2] = 50; 66 snakeY[2] = 100; 67 68 // 食物的初期的位置随机的坐标, 69 foodX = 25 + 25 * random.nextInt(34); 70 foodY = 75 + 25 * random.nextInt(24); 71 } 72 73 // Graphics 画笔 74 // 面板:画界面,画小蛇 75 @Override 76 protected void paintComponent(Graphics g) { 77 // 调用父类方法,清屏 78 super.paintComponent(g); 79 80 // 设置面板背景颜色(黑色) 81 this.setBackground(Color.white); 82 83 // 绘制头部的标题栏 84 Data.header.paintIcon(this,g,25,10); 85 86 // 绘制游戏区域 87 g.fillRect(25,75,850,600); 88 89 // 创建一个静态的小蛇 90 if (fx.equals("R")){ 91 Data.rightHeader.paintIcon(this,g,snakeX[0], snakeY[0]); 92 } else if (fx.equals("L")){ 93 Data.leftHeader.paintIcon(this,g,snakeX[0], snakeY[0]); 94 } else if (fx.equals("U")){ 95 Data.upHeader.paintIcon(this,g,snakeX[0], snakeY[0]); 96 } else if (fx.equals("D")){ 97 Data.downHeader.paintIcon(this,g,snakeX[0], snakeY[0]); 98 } 99 100 // 通过for循环,创建小蛇的身体部分 101 for (int i = 1; i < length; i++){ 102 Data.body.paintIcon(this,g,snakeX[i], snakeY[i]); 103 } 104 105 // 把食物添加到窗口里 106 Data.food.paintIcon(this,g,foodX, foodY); 107 108 // 游戏开始提示:是否开始游戏 109 if (isStart == false){ 110 // 提示文字的属性, 111 g.setColor(Color.white); 112 g.setFont(new Font("微软雅黑",Font.BOLD,40)); 113 g.drawString("按下空格开始游戏",300,300); 114 } 115 116 // 游戏失败提示:游戏失败 117 if (isFail){ 118 // 提示文字的属性, 119 g.setColor(Color.red); 120 g.setFont(new Font("微软雅黑",Font.BOLD,40)); 121 g.drawString("游戏失败,按下空格重新开始游戏",200,300); 122 } 123 124 // 记分提示文字的属性, 125 g.setColor(Color.white); 126 g.setFont(new Font("微软雅黑",Font.BOLD,18)); 127 g.drawString("长度:" + length,750,33); 128 g.drawString("分数:" + score,750,55); 129 } 130 131 /** 132 * 接受键盘的输入,进行监听 133 * @param e 134 */ 135 @Override 136 public void keyPressed(KeyEvent e) { 137 // 获取键盘按下的是哪一个键 138 int keyCode = e.getKeyCode(); 139 140 // 如果按下的是空格键,就启动或者暂停 141 if (keyCode == KeyEvent.VK_SPACE){ 142 // 如果游戏失败 143 if (isFail) { 144 // 游戏重新开始 145 isFail = false; 146 // 重新初始化游戏 147 init(); 148 } else { 149 // 暂停游戏 150 isStart = !isStart; 151 } 152 153 // 刷新界面 154 repaint(); 155 } 156 157 // 判断键盘按下的上下左右键,控制小蛇的走向 158 if (keyCode == KeyEvent.VK_LEFT) { 159 fx = "L"; 160 } else if (keyCode == KeyEvent.VK_RIGHT){ 161 fx = "R"; 162 }else if (keyCode == KeyEvent.VK_UP) { 163 fx = "U"; 164 }else if (keyCode == KeyEvent.VK_DOWN) { 165 fx = "D"; 166 } 167 168 } 169 170 /** 171 * 定时器,监听事件,执行定时操作 172 * @param e 173 */ 174 @Override 175 public void actionPerformed(ActionEvent e) { 176 177 // 如果游戏处于开始状态,并且游戏没有结束,小蛇就移动 178 if (isStart && isFail == false){ 179 180 // 除了头部,身体都跟着往右移动 181 for (int i = length - 1; i > 0; i--) { 182 snakeX[i] = snakeX[i-1]; 183 snakeY[i] = snakeY[i-1]; 184 } 185 186 // 通过控制方向,移动头部 187 if (fx.equals("R")){ 188 // 头部向右移动一步 189 snakeX[0] = snakeX[0] + 25; 190 // 如果已经到达边界,则从最右侧重新开始 191 if (snakeX[0] > 850){ 192 snakeX[0] = 25; 193 } 194 } else if (fx.equals("L")){ 195 // 头部向左移动一步 196 snakeX[0] = snakeX[0] - 25; 197 // 如果已经到达边界,则从最右侧重新开始 198 if (snakeX[0] < 25){ 199 snakeX[0] = 850; 200 } 201 } else if (fx.equals("U")){ 202 // 头部向左移动一步 203 snakeY[0] = snakeY[0] - 25; 204 // 如果已经到达边界,则从最右侧重新开始 205 if (snakeY[0] < 75){ 206 snakeY[0] = 650; 207 } 208 } else if (fx.equals("D")){ 209 // 头部向左移动一步 210 snakeY[0] = snakeY[0] + 25; 211 // 如果已经到达边界,则从最右侧重新开始 212 if (snakeY[0] > 650){ 213 snakeY[0] = 75; 214 } 215 } 216 217 //System.out.println("snakeX = "+snakeX[0]+" foodX="+foodX +"////snakeY = "+snakeY[0]+" foodY="+foodY); 218 // 判断小蛇的头的坐标和食物的坐标重合的情况下 219 if (snakeX[0] == foodX && snakeY[0] == foodY) 220 { 221 // 小蛇的身体长度加1 222 length ++ ;
snakeX[length-1] = snakeX[length-2] * 2 - snakeX[length-3]; snakeY[length-1] = snakeY[length-2] * 2 - snakeY[length-3]; 223 224 // 每当吃掉一个食物,积分+10 225 score = score + 10; 226 227 // 生成新的食物 228 foodX = 25 + 25 * random.nextInt(34); 229 foodY = 75 + 25 * random.nextInt(24); 230 } 231 232 // 结束判断:头和身体有接触,则游戏失败 233 //for (int i = 1; i < length; i++) { 234 // if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) { 235 // // isFail = true; 236 // 237 // } 238 //}
239 // 刷新界面 240 repaint(); 241 } 242 // 定时器启动 243 timer.start(); 244 } 245 246 /** 247 * 键盘按下, 248 * @param e 249 */ 250 @Override 251 public void keyTyped(KeyEvent e) { 252 253 } 254 255 /** 256 * 257 * @param e 258 */ 259 @Override 260 public void keyReleased(KeyEvent e) { 261 262 } 263 264 265 }