Java贪吃蛇游戏
小黄小黄不再迷茫 人气:01.开发环境及游戏展示
贪吃蛇大作战在设计与实验的过程中需要在Window 10系统中进行,并使用开发工具IntelliJ IDEA Community Edition 2020.3.2(jdk-15)完成代码的编写、编译、调试、测试等工作。程序需要的组件如贪吃蛇的蛇头、蛇身及背景图片等均使用图像软件Adobe Photoshop 2020制作。
1.1 游戏主界面
1.2 移动界面
1.3 奖励界面
1.游戏达到50分:
2.游戏达到100分:
1.4 F加速功能界面
按下F时,速度提高到100。
1.5 死亡界面
2.需求分析
贪吃蛇游戏是一个小型益智游戏,可用于人们日常的休闲、娱乐等场景。本次游戏设计涉及一维数组、Swing中的常用组件、GUI中的事件处理(事件监听器、键盘监听器)、类的封装与继承、static静态变量、包装类、随机数等方面的知识。
具体需求概要如下:
1.初始化界面为800×800大小,且游戏界面应保持在屏幕中央,窗口应有“贪吃蛇”字样;
2.游戏界面应有必要的提示文字,提示游戏的得分和蛇的当前速度。开始界面应有操作提示文字(轻按空格游戏以开始/暂停游戏、按住F以加速),文字颜色应美观;
3.在程序中初始化蛇的位置(靠近面板左侧)、蛇的方向(默认向右)、蛇的长度(默认为3),并初始化食物的位置;
4.游戏的开始与暂停的实现:通过空格键控制游戏的开始与暂停;
5.蛇移动功能的实现:用W、S、A、D或↑、↓、←、→控制蛇的运动方向为上、下、左、右进行游戏;
6.当蛇头接触活动外围时:蛇头接触界面最右(左)端则从对称的左(右)端出现,以此类推,当蛇头接触界面最上(下)端则从对称的下(上)端出现;
7.蛇吃食物的功能的实现:游戏过程中,蛇头触碰到食物时,即为吃食物。此时蛇的长度应加1,速度应增加10,得分增加10,并刷新食物的位置(随机位置);
8.蛇加速功能的实现:当按住“F”键时,蛇加速运动,速度值达到100;当松开“F”键时,蛇恢复至默认速度10;
9.蛇的死亡判定:当蛇头触碰到蛇身时,蛇死亡,游戏结束,界面靠近中央提示:“你的小蛇已经死亡,按下空格重新开始!”;
10.游戏的奖励机制的实现:当得分为10时,游戏提示“Good!”字样;当得分为50时,游戏提示“Good!!!”字样;当得分为100时,游戏提示“Perfect!!!”字样;当得分达到200时,则游戏提示“无人能挡!!!”的字样;以上提示字样均位于蛇头;
基本操作表如下:
3.系统设计
3.1 系统总体功能设计
此贪吃蛇程序大方向上包含贪吃蛇的移动功能、贪吃蛇的主动加速功能、游戏规则判断功能、主界面控制功能、贪吃蛇吃食物功能。其中贪吃蛇的移动功能中包含移动方向的按键控制以及贪吃蛇的自动移动;游戏规则判断功能包括蛇的死亡判断、游戏开始与暂停判断以及越界规则;主界面控制功能包含蛇的初始化以及食物的初始化;贪吃蛇吃食物功能包括蛇身增长、蛇速度增加、积分增加和食物刷新位置。具体功能结构图如下:
3.2 系统总体流程设计
开始进入贪吃蛇程序时首先判断是否按下空格键,如果按下空格键则游戏开始。游戏开始后判断是否有按键输入,若有按键输入则根据按键设置改变蛇的移动方向或者进行加速操作;若没有按键操作则蛇继续保持原方向运动。在蛇运动过程中判断蛇是否吃到食物,若吃到食物则游戏得分加10,蛇的移动速度也相应增加;不管是否吃到食物都进行蛇是否死亡的判断,若死亡则游戏结束;若蛇没有死亡则继续判断是否有空格输入,若有则游戏暂停,反之则继续判断按键,根据按键改变蛇的运动状态,继续游戏,直到蛇死亡,游戏结束。
4.功能设计
4.1 贪吃蛇移动及加速功能设计
贪吃蛇的移动功能主要依赖于选择语句和键盘监听器来实现,用W、S、A、D或↑、↓、←、→控制蛇的运动方向为上、下、左、右进行游戏。当按下F键时,蛇加速运动。
4.2 贪吃蛇吃食物加速及死亡判定功能的设计
在贪吃蛇吃食物以及死亡判定的过程中,使用到了以下参数:
- Timer类的一个参数:int 类型的time,程序中调用timer.setDelay(time)方法更改定时器的参数(time越小,蛇运动越快);
- 记录蛇死亡与否的参数:boolean 类型的isDied(true则蛇死,false则蛇活),默认为false;
- 蛇的长度:int 类型的length(初始为3);
- 积分参数:int 类型的score(初始为0),程序中通过循环控制每吃一次食物加10分;
- 蛇头的坐标:int类型的snakeX[0]与snakeY[0],分别储存蛇头的横纵坐标;
- 蛇身的坐标:int[] 类型的snakeX[i]和snakeY[i],分别储存每一节蛇身的横纵坐标(i≠0);
- 食物的坐标:int 类型的foodX,foodY,分别储存食物的横纵坐标,foodX与foodY均为使用Random类中的nextInt()方法产生的随机数,故食物的坐标是随机的。
以下使用上述参数时便不再赘述。
4.2.1 贪吃蛇吃食物加速功能的设计
当蛇头的坐标snakeX[0]与snakeY[0]分别与食物的坐标foodX,foodY相等时,得分score增加10,蛇身长度length加1,Timer参数time减10,从而完成蛇吃到食物后的加分,蛇身加长及加速功能。该功能的流程设计图如下:
4.2.2 贪吃蛇死亡判定功能的设计
当贪吃蛇吃到自己的时候蛇死亡,即蛇头坐标snakeX[0]与snakeY[0]分别与蛇身坐标snakeX[i]和snakeY[i]相等时,即蛇死亡。此时isDied=!isDied。
4.3 贪吃蛇主动加速功能的设计
贪吃蛇的主动加速功能即在游戏过程中按住F键,蛇的移动速度加快,若按住后松开,则蛇的速度恢复默认值。这主要通过GUI中的键盘监听器来实现,实现了键盘监听器的两个方法,keyPressed()与keyReleased()分别监听按下F键的操作和松开F键的操作。其次用到了参数time,程序中改变time的值通过调用timer.setDelay(time)方法(timer为Timer类的一个对象)从而改变定时器的频率,达到蛇速度加快的效果。
4.4 贪吃蛇奖励机制功能的设计
贪吃蛇奖励机制功能主要使用了选择结构以及通过paintComponent(Graphics g)方法中的画笔工具来实现。当游戏达到一定的得分时,程序会调用画笔工具中的g.drawString(“XXX”,snakeX[0],snakeY[0])方法,在蛇头位置绘制出提示文字。
5.项目结构与项目实现
5.1 项目结构及类间关系
5.2 项目完整源码
5.2.1 Images类
该类为静态类,主要存储贪吃蛇组件的图片对象。
package snakegame; import javax.swing.*; import java.net.URL; //Images类用于封装图片路径以及获取图片(使用静态对象便于用类名直接访问) public class Images { //将图片的路径封装成一个对象 public static URL bodyURL=Images.class.getResource("/snake_images/body.jpg"); public static URL backURL=Images.class.getResource("/snake_images/back.jpg"); public static URL snakeDownURL=Images.class.getResource("/snake_images/snakeDown.jpg"); public static URL snakeLeftURL=Images.class.getResource("/snake_images/snakeLeft.jpg"); public static URL snakeRightURL=Images.class.getResource("/snake_images/snakeRight.jpg"); public static URL snakeUpURL=Images.class.getResource("/snake_images/snakeUp.png"); public static URL foodURL=Images.class.getResource("/snake_images/food.jpg"); //将图片封装为程序中的对象 public static ImageIcon bodyImg=new ImageIcon(bodyURL); public static ImageIcon backImg=new ImageIcon(backURL); public static ImageIcon snakeDownImg=new ImageIcon(snakeDownURL); public static ImageIcon snakeUpImg=new ImageIcon(snakeUpURL); public static ImageIcon snakeRightImg=new ImageIcon(snakeRightURL); public static ImageIcon snakeLeftImg=new ImageIcon(snakeLeftURL); public static ImageIcon foodImg=new ImageIcon(foodURL); }
5.2.2 HighestScore
该类实现历史最高分功能。
package snakegame; import org.w3c.dom.Text; import java.io.*; //该类用于给游戏添加历史最高分功能 public class HighestScore { static int score = 0;//最高分的存储 public void highest() throws IOException { //得分为最高分时存储 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(String.valueOf(Text.class.getResourceAsStream("贪吃蛇大作战/score.txt")))); if (GamePanel.score > score) { score = GamePanel.score; String s1 = String.valueOf(score); bos.write(s1.getBytes()); } bos.close(); } //用于游戏开始时从文件读取最高分的值 public void readHighest() throws IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(String.valueOf(Text.class.getResourceAsStream("贪吃蛇大作战/score.txt")))); byte[] bys = new byte[1024]; int len; while ((len = bis.read(bys)) != -1) { System.out.print(new String(bys, 0, len));//测试用 String s2 = new String(bys, 0, len); score = Integer.valueOf(s2); } bis.close(); } }
5.2.3 GamePanel类
该类用于绘制贪吃蛇游戏面板,以及实现游戏具体逻辑功能。
package snakegame; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.IOException; import java.util.Random; //贪吃蛇游戏面板的绘制 public class GamePanel extends JPanel { Timer timer; int time=100; static int score;//记录游戏分数 //蛇属性部分 boolean isDied; int length;//设置蛇长 String direction;//蛇头朝向 //分别储存蛇身的X,Y坐标 int[] snakeX=new int[300]; int[] snakeY=new int[300]; //判断游戏是否开始 boolean isStart=false; int foodX,foodY;//食物的X,Y坐标 //贪吃蛇初始化方法 public void init(){ length=3; snakeX[0]=175; snakeY[0]=275; snakeX[1]=150; snakeY[1]=275; snakeX[2]=125; snakeY[2]=275; foodX=300; foodY=400; score=0; isDied=false;//蛇默认状态为活着 direction="R";//U,D,L,R分别表示蛇头朝向上,下,左,右 } //构造方法 public GamePanel() throws IOException { init(); if(GamePanel.score>HighestScore.score){ new HighestScore().highest(); } //焦点定位到当前面板 this.setFocusable(true); //监听器的实现部分 this.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { super.keyPressed(e); int keyCode = e.getKeyCode(); //按下空格以开始游戏,以及方向键的控制 if (keyCode == KeyEvent.VK_SPACE) { if(isDied){ init(); }else { isStart = !isStart; repaint();//重绘 } } else if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_W) { direction = "U"; } else if (keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_S) { direction = "D"; } else if (keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_A) { direction = "L"; } else if (keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_D) { direction = "R"; } } }); //贪吃蛇加速功能的实现 this.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { int keyCode=e.getKeyCode(); if(keyCode==KeyEvent.VK_F){ time=10; timer.setDelay(time);//按下F时加速 } } @Override public void keyReleased(KeyEvent e) { int keyCode=e.getKeyCode(); if(keyCode==KeyEvent.VK_F){ time=100; timer.setDelay(time);//松开F键减速恢复默认速度 } } }); //对定时器进行初始化,并实现监听器 timer = new Timer(time, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { //当游戏处于开始状态且蛇活着时蛇活动,且后一节身子到前一节身子的位置上(每一节身子为25乘25像素) if (isStart && isDied == false) { //蛇身运动部分 for (int i = length - 1; i > 0; i--) { snakeX[i] = snakeX[i - 1]; snakeY[i] = snakeY[i - 1]; } //蛇头运动部分 if ("D".equals(direction)) { snakeY[0] += 25; } if ("U".equals(direction)) { snakeY[0] -= 25; } if ("L".equals(direction)) { snakeX[0] -= 25; } if ("R".equals(direction)) { snakeX[0] += 25; } //防止超出边界 if (snakeX[0] > 750) { snakeX[0] = 25; } if (snakeX[0] < 25) { snakeX[0] = 750; } if (snakeY[0] < 100) { snakeY[0] = 725; } if (snakeY[0] > 725) { snakeY[0] = 100; } //吃食物的动作,吃食物加10分且蛇身变长,蛇的速度提升10 if ((snakeX[0] == foodX && snakeY[0] == foodY)) { length++;//蛇身长度加1 foodX = (new Random().nextInt(30) + 1) * 25;//随机生成X坐标[25,750] foodY = (new Random().nextInt(26) + 4) * 25;//随机生成Y坐标[100,725] score += 10;//吃一次食物积分加10 try { new HighestScore().highest(); } catch (IOException ioException) { ioException.printStackTrace(); } if(time>10) timer.setDelay(time-=10);//吃一次食物速度增加10 } //蛇的死亡机制判定 for (int i = 1; i < length; i++) { if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) { isDied = !isDied; } } repaint(); } } }); timer.start();//定时器启动 } //绘制面板部分 @Override protected void paintComponent(Graphics g) { super.paintComponent(g); //填充背景颜色 this.setBackground(new Color(164, 219, 246)); //绘制初始蛇头的图片(默认右方朝向) Images.backImg.paintIcon(this,g,10,10); //调整画笔的颜色并绘制一个矩形面板 g.setColor(new Color(219,226,219)); g.fillRect(10,70,770,685); //蛇头的绘制;上下左右不同情况 if("L".equals(direction)){ Images.snakeLeftImg.paintIcon(this,g,snakeX[0],snakeY[0]); } if("R".equals(direction)){ Images.snakeRightImg.paintIcon(this,g,snakeX[0],snakeY[0]); } if("U".equals(direction)){ Images.snakeUpImg.paintIcon(this,g,snakeX[0],snakeY[0]); } if("D".equals(direction)){ Images.snakeDownImg.paintIcon(this,g,snakeX[0],snakeY[0]); } //蛇身的绘制 for(int i=1;i<length;i++) { Images.bodyImg.paintIcon(this, g, snakeX[i], snakeY[i]); } //食物绘制 Images.foodImg.paintIcon(this,g,foodX,foodY); //游戏的文字提示语 if(!isStart&&!isDied){ g.setColor(new Color(12, 191, 222)); g.setFont(new Font("楷体",Font.BOLD,40)); g.drawString("轻按空格以开始/暂停游戏!",150,330); g.setColor(new Color(246, 2, 31)); g.setFont(new Font("宋体",Font.CENTER_BASELINE,30)); g.drawString("按住F以加速",300,380); } //游戏的得分显示 g.setColor(new Color(248, 4, 74)); g.setFont(new Font("楷体",Font.BOLD,30)); g.drawString("得分:"+score,420,90); //游戏的最高分展示模块 try { new HighestScore().readHighest(); } catch (IOException e) { e.printStackTrace(); } g.setColor(new Color(183, 25, 238)); g.setFont(new Font("楷体",Font.BOLD,30)); g.drawString("历史最高分:"+HighestScore.score,10,90); //蛇的速度提示 g.setColor(new Color(248, 136, 36)); g.setFont(new Font("楷体",Font.BOLD,30)); g.drawString("当前速度:"+String.valueOf(110-time),550,90); //游戏的奖励鼓励机制 if(score==10){ g.setColor(new Color(127, 232, 20)); g.setFont(new Font("微软雅黑",Font.BOLD,50)); g.drawString("Good!",snakeX[0],snakeY[0]); } else if(score==50){ g.setColor(new Color(17, 231, 250)); g.setFont(new Font("微软雅黑",Font.BOLD,50)); g.drawString("Good!!!",snakeX[0],snakeY[0]); }else if(score==100){ g.setColor(new Color(248, 196, 6)); g.setFont(new Font("微软雅黑",Font.BOLD,50)); g.drawString("Perfect!!!",snakeX[0],snakeY[0]); }else if(score==200){ g.setColor(new Color(248, 245, 26)); g.setFont(new Font("微软雅黑",Font.BOLD,50)); g.drawString("无人能挡!!!",snakeX[0],snakeY[0]); } //小蛇死亡时面板的绘制 if(isDied){ g.setColor(new Color(6, 115, 248)); g.setFont(new Font("微软雅黑",Font.BOLD,30)); g.drawString("你的小蛇已经死亡,按下空格重新开始!",170,330); } } }
5.2.4 Start类
页面的总体布局与程序的入口。
package snakegame; import javax.swing.*; import java.awt.*; import java.io.IOException; //贪吃蛇总体界面布局以及程序的主方法 public class Start { public static void main(String[] args) throws IOException { JFrame jf=new JFrame("贪吃蛇"); //获取屏幕的大小,并使用数据使窗口位置居中 int width= Toolkit.getDefaultToolkit().getScreenSize().width; int height= Toolkit.getDefaultToolkit().getScreenSize().height; jf.setBounds((width-800)/2,(height-800)/2,800,800); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.setResizable(false);//设置窗口大小不能更改 //添加GamePanel面板 GamePanel panel=new GamePanel(); jf.add(panel); jf.setVisible(true); } }
加载全部内容