实验二 贪吃蛇游戏开发
一、实验要求
1)、实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子,上下左右控制“蛇”的移动,吃到“豆子”以后“蛇”的身体加长一点。
2)、“蛇”碰到边界或蛇头与蛇身相撞,蛇死亡,游戏结束。
3)、为游戏设计友好的交互界面;例如欢迎界面,游戏界面,游戏结束界面。要有开始键、暂停键和停止退出的选项。
4)、对蛇吃到豆子进行分值计算,可以设置游戏速度,游戏音乐等拓展元素。
二、实验步骤
1、首先是搭建游戏基本界面框架
搭建窗口:创建一个JFrame的继承类Frame,完成窗口的基本设置(窗口尺寸,名称,窗口可视化等等)
this.setTitle("贪吃蛇");
this.setSize(500, 600);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
//窗体居中
this.setLocationRelativeTo(null);this.setVisible(true);
创建一个JPanel的继承类GamePanel,来装游戏的基本界面
首先考虑游戏过程中需要的按钮
基本的游戏按钮需要设置开始,暂停,退出,在这里为了简洁,我把开始和暂停设置在同一个按钮上,根据不同的情况触发不同的功能除此之外,还需要设置一个JLable用于显示当前分数。并对这些组件进行基本设置,如背景色,字体大小等。
设置好后,全部添加到Frame的NORTH位置。
String str = new String("0");
JLabel score = new JLabel(str);
JPanel panel = new JPanel();JButton start = new Button("暂停");JButton esc = new Button("退出");score.setFont(new Font("", Font.PLAIN, 25));score.setForeground(new Color(121, 135, 162));System.out.println(score);JPanel button = new JPanel();button.setLayout(new GridLayout(1, 3));button.add(start);button.add(esc);button.add(score);panel.setBorder(new EmptyBorder(20, 0, 7, 0));panel.setLayout(new BorderLayout());panel.setOpaque(false);panel.add(button);panel.setVisible(true);frame.add(panel, BorderLayout.NORTH);
2、贪吃蛇算法实现
1)、首先要实现的是蛇头和食物的随机生成
创建一个关于蛇的类Snake,并在其中设置两个列表,一个snake列表,存放除蛇头以外的所有蛇身的坐标,一个snakeHead列表 ,存放蛇头坐标。并且设置两个全局变量x,y记录蛇头坐标。
并分别给蛇头和蛇身设置图片,以及游戏界面背景图片
//蛇身坐标;List<List<Integer>> snake = new ArrayList<>();//蛇头坐标;List<Integer> snakeHead = new ArrayList<>();public int x;public int y;
public ImageIcon head = new ImageIcon("C:\\Users\\miracle\\OneDrive\\图片\\贪吃蛇素材\\3.png");public ImageIcon head1 = new ImageIcon("C:\\Users\\miracle\\OneDrive\\图片\\贪吃蛇素材\\1.png");public ImageIcon head2 = new ImageIcon("C:\\Users\\miracle\\OneDrive\\图片\\贪吃蛇素材\\2.png");public ImageIcon body = new ImageIcon("C:\\Users\\miracle\\OneDrive\\图片\\贪吃蛇素材\\水母2.png");public ImageIcon backGround = new ImageIcon("C:\\Users\\miracle\\OneDrive\\图片\\贪吃蛇素材\\比奇宝.jpg");
同样的,创建一个豆子的类Food,设置全局变量x,y和列表food来存放豆子坐标。
public class Food {public int x;public int y;List<Integer> bean = new ArrayList<>();public ImageIcon food = new ImageIcon("C:\\Users\\miracle\\OneDrive\\图片\\贪吃蛇素材\\水母王.png");}
注意:在上面两个类中,设置列表来存放坐标是为了之后方便用存放列表的列表snake进行判断是否包含相关列表。而又用x,y来存放坐标是为了方便改动坐标。
在主游戏界面GamePanel中分别创建这两个类的对象,s和f。
Snake s = new Snake();
Food f = new Food();
给s中的蛇头坐标和f中的食物坐标分别附一个随机初始值。
do {s.x = random.nextInt(0, 460);} while (s.x % 25 != 0);do {s.y = random.nextInt(20, 460);} while (s.y % 25 != 0);do {do {f.x = random.nextInt(0, 460);} while (f.x % 25 != 0);do {f.y = random.nextInt(20, 460);} while (f.y % 25 != 0);} while (s.x == f.x && s.y == f.y);
注意:蛇头和食物坐标不能重合!并且要确保移动后能够重合坐标。
2)、贪吃蛇移动实现
首先给开始和暂停按钮设置键盘监听。在这里,我用键盘按键的code值来进行判断并设置相应动作。
start.addKeyListener(new KeyListener() {@Overridepublic void keyTyped(KeyEvent e) {}@Overridepublic void keyPressed(KeyEvent e) {//int x = 0, y = 0;switch (e.getKeyCode()) {case 38://str = "up";y = -25;x = 0;img=s.head;break;case 40://str = "down";y = 25;x = 0;img=s.head;break;case 37://str = "left";x = -25;y = 0;img=s.head1;break;case 39://str = "right";x = 25;y = 0;img=s.head2;break;default:break;}}@Overridepublic void keyReleased(KeyEvent e) {}});
当按键为“上”时,则蛇头纵坐标需要移动-25,横坐标不变。其中x,y是全局变量,用于存储每次蛇的坐标的变化情况。
但 仅仅这样还不能让蛇动起来,需要面板不断进行刷新,才能实现蛇的移动。
新建一个Thread的子类ReThread,用于面板的刷新,重绘。
public class Rethread extends Thread {GamePanel gamePanel;int speed;public Rethread(GamePanel gamePanel, int speed) {this.gamePanel = gamePanel;this.speed = speed;}@Overridepublic void run() {while (true) {try {Thread.sleep(speed);} catch (Exception e) {e.printStackTrace();}gamePanel.repaint();}}
}
并在Game Panel主游戏界面中生成对象调用
Rethread rt = new Rethread(this, speed);rt.start();
然后重写paintComponent方法,用于重绘面板
@Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);g.drawImage(backGround.getImage(), 0, 0, this.getWidth(), this.getHeight(), this);
将蛇和食物设置的图片画在界面中
ImageIcon bean = f.food;ImageIcon backGround = s.backGround;ImageIcon b = s.body;ImageIcon img = s.head;
//画出蛇和食物;
g.drawImage(img.getImage(), s.x, s.y, 25, 25, this);
g.drawImage(bean.getImage(), f.x, f.y, 25, 25, this);
根据之前通过键盘按键得到的蛇的坐标变化情况来改变蛇头的坐标。
s.x = s.x + x;s.y = s.y + y;s.snakeHead.set(0, s.x);s.snakeHead.set(1, s.y);
蛇头坐标改变后,需要判断是否越界,也就是离开游戏界面,若出界,则蛇身清空,游戏结束,打开新的提示界面
if (s.x > 460 || s.x < 0 || s.y > 460 || s.y < 0) {if (best < sc) {best = sc;}s.snake.removeAll(s.snake);stop = 1;OverFrame of = new OverFrame();of.init(frame, best, speed);}
此时需要判断,是否只有蛇头,也就是snake列表是否为空。
当蛇身长度不为空时,判断蛇头是否与蛇身的某一坐标重合,若相撞,则打开新的提示界面,结束游戏
if (s.snake.size() >= 1 && s.snake.contains(s.snakeHead)) {s.snake.removeAll(s.snake);if (best < sc) {best = sc;}stop = 1;OverFrame of = new OverFrame();of.init(frame, best, speed);}
当snake列表为空时,再判断蛇头是否与食物的坐标重合,若重合,则蛇身长度加1,并将蛇头的上一坐标作为蛇尾的坐标,食物重新随机获取坐标(仍然不能与蛇身重合,且不能越界)
if (s.x == f.x && s.y == f.y && s.snake.size() == 0) {List<Integer> list = new ArrayList<>();list.add(s.x - x);list.add(s.y - y);//s.snake.set(0, s.snakeHead);s.snake.add(list);sc = 10;do {do {f.x = random.nextInt(0, 460);} while (f.x % 25 != 0);do {f.y = random.nextInt(20, 460);} while (f.y % 25 != 0);f.bean.set(0, f.x);f.bean.set(1, f.y);} while (s.snake.contains(f.bean) || s.snakeHead.equals(f.bean));}
注意:这里要先判断snake是否不为空,再判断是否为空,否则,当snake为空且蛇头与食物 坐标重合时,snake会从空变成不为空,若再进行不为空的判断就会出错。
如若游戏还没结束,则蛇身坐标接着移动,这里我是新建一个类来专门进行蛇身移动的计算。主要思想是,每个身体段的坐标都变成上一段身体的坐标,第0段的身体坐标则是蛇头上一个坐标。
public class Move {public List<List<Integer>> move(List<List<Integer>> snake, int x, int y, List<Integer> food, List<Integer> head) {List<Integer> list = new ArrayList<>();Iterator<Integer> iterator = snake.get(snake.size() - 1).iterator();int m = iterator.next();int n = iterator.next();//蛇尾坐标;list.add(m);list.add(n);for (int i = snake.size() - 1; i > 0; i--) {snake.get(i).set(0, snake.get(i - 1).get(0));snake.get(i).set(1, snake.get(i - 1).get(1));}snake.get(0).set(0, x);snake.get(0).set(1, y);if (head.equals(food)) {snake.add(list);}return snake;}
}
注意:由于存储列表的列表的特殊性:会随着内部列表的变化而变化,因此,我使用了两个整型变量来存放原有蛇尾坐标。如果吃到豆子,则蛇尾增加原有蛇尾坐标。
在Game Panel判断snake不为空后,进行调用
if (s.snake.size() >= 1) {s.snake = m.move(s.snake, s.x - x, s.y - y, f.bean, s.snakeHead);//吃掉豆子后,豆子重新获取;if (s.snakeHead.equals(f.bean)) {sc = sc + 10;do {do {f.x = random.nextInt(0, 460);} while (f.x % 25 != 0);do {f.y = random.nextInt(20, 460);} while (f.y % 25 != 0);f.bean.set(0, f.x);f.bean.set(1, f.y);} while (s.snake.contains(f.bean) || s.snakeHead.equals(f.bean));}}
实现了整条蛇的移动后,还要画出移动后的蛇的图案。在此之前,已经将蛇头和食物画在了面板上,接下来就是画出移动后的蛇身。在这里,我调用循环来依次画出每一段蛇身。
for (int i = 0; i < s.snake.size(); i++) {List list = new ArrayList();list.add(s.snake.get(i).get(0));list.add(s.snake.get(i).get(1));Iterator<Integer> iterator = list.iterator();int c = iterator.next();int d = iterator.next();g.drawImage(b.getImage(), c, d, 25, 25, this);s.snake.get(i).set(0, s.snake.get(i).get(0));s.snake.get(i).set(1, s.snake.get(i).get(1));}
最后再在label上显示出分数,我这里设置的是吃掉一颗豆子,得10分。
score.setText("分数:" + sc);
至此,贪吃蛇的主要游戏界面和主要算法基本实现
3、开始界面和游戏结束界面的搭建
1)、创建一个JPanel继承类StartPanel用于设置开始界面的组件内容。
在此需要包含的组件有游戏名称(JLabel)、开始按钮、速度按钮、历史最高纪录按钮。设置网格布局模式,并按4排1列的方式排列。并设置好这几个组件的外观。(为了增加趣味性,我把游戏名称设置为“抓水母”。)
给三个按钮设置按键监听。
点击开始按钮时,关闭当前窗口,打开之前设置的主要游戏窗口。
start.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {frame.dispose();Frame frame = new Frame();frame.init(best);frame.game(speed);}});
点击速度按钮时,改变速度,也就是面板刷新速度,并用相关字符串给出提示
set.addActionListener(new ActionListener() {int i = 0;@Overridepublic void actionPerformed(ActionEvent e) {if (i == 3) {set.setText("ffffast");i++;speed = 50;}if (i == 2) {set.setText("fast");i++;speed = 150;}if (i == 1) {set.setText("normal");i++;speed = 300;}if (i == 0) {set.setText("slow");i++;speed = 500;}if (i == 4) {i = 0;}}});
点击历史最高按钮时,显示最高分数
history.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {history.setText(best + "");}});}
其中,为了存储和传递速度和最高分数这两个数字,我在每个相关的类中都设置了speed和score两个全局变量,并且调用这些类的对象时,也要进行参数传递,防止出现不匹配的问题。
2)、创建一个JFrame的继承类OverFrame作为结束提示
其中包含“Game over”的JLabel,“重新开始”按钮,“退出”按钮。
当点击重新开始按钮时,关闭当前前窗口,重新打开游戏主界面
yes.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {frame.dispose();Frame frame = new Frame();frame.init(best);frame.game(speed);}});
点击退出按钮后,回到开始界面。
esc.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {OverFrame.this.dispose();frame.dispose();Frame frame = new Frame();frame.init(best);StartPanel startPanel = new StartPanel(best, frame);frame.add(startPanel);}});
至此贪吃蛇游戏基本实现。
实验二 贪吃蛇游戏开发相关推荐
- 实验项目二 贪吃蛇游戏开发
一.实验要求 1.实现贪吃蛇游戏基本功能,屏幕上随机出现一个"食物",称为豆子, 2.上下左右控制"蛇"的移动,吃到"豆子"以后" ...
- 实验项目二:贪吃蛇游戏开发
贪吃蛇游戏开发 一.前言 二.基本流程 三.游戏界面设计 1. 界面构造 2. 蛇的构造 3. 食物构造 四.游戏过程 1. 蛇的移动 2. 蛇吃食物与碰撞检测 五.游戏结束 1. 结束界面 六.扩展 ...
- 实验二、贪吃蛇游戏开发
实验要求 实现贪吃蛇游戏基本功能,屏幕上随机出现一个"食物",称为豆子,上下左右控制"蛇"的移动,吃到"豆子"以后"蛇" ...
- 实验二 贪吃蛇的游戏开发
实验目的: 实现贪吃蛇游戏基本功能,屏幕上随机出现一个"食物",称为豆子,上下左右控制"蛇"的移动,吃到"豆子"以后"蛇" ...
- 计算机软件实习项目二 —— 贪吃蛇游戏 (实验准备)
目录 一.实验目的 二.编程语言和平台 三.实验难点: 四.参考资料 一.实验目的 1.实现贪吃蛇游戏基本功能,屏幕上随机出现一个"食物",称为豆子 2.上下左右控制"蛇 ...
- c语言设计贪吃蛇实验报告,贪吃蛇游戏程序设计实验报告
<贪吃蛇游戏程序设计实验报告>由会员分享,可在线阅读,更多相关<贪吃蛇游戏程序设计实验报告(11页珍藏版)>请在人人文库网上搜索. 1.Windows编程大作业贪吃蛇设计与实现 ...
- 结对-贪吃蛇游戏-开发环境搭建过程
结对编程成员:赵建辉,马壮 搭建环境: 会 html,css,以及java开发知识. 会应用sublime,dw等编辑软件 编写程序阶段: 1.利用html搭建前端页面,构建游戏的页面框架 2.利用j ...
- 基于C语言的贪吃蛇游戏开发与设计
文章目录 前言 一.背景知识 二.实验分析和理解 三.头文件 四.设计流程 五.流程图 六.算法设计 (1)相关变量 (2)创建链表 (3)随机出现食物并记录食物出现的坐标 (4)绘制初始界面和游戏地 ...
- 实践 贪吃蛇游戏开发
1.代码实现: GamePanel类 package snakegame; import java.awt.Color; import java.awt.Font; import java.awt.G ...
- 计算机软件实习项目二 —— 贪吃蛇游戏 (代码实现) 12-16
代码实现 不得不说python真是太香了,我感觉写起来比C++快,而且代码量更少,还有非常多十分方便的方法可以使用.在pycharm里有非常多的快捷键十分的方便,相较项目使用的visual stu ...
最新文章
- 新创建的Django项目Django administration用户名和密码配置
- 5怎么关闭作弊模式_相机5种主要的拍摄模式该怎么用
- zTree树形菜单使用实例
- oracle追加index,oracle add index
- java app的强制更新吗_java – Spring JPA / Hibernate事务强制插入而不是更新
- 利用python将图片版PDF转文字版PDF
- linux制作flash软件,Linux 下的三款 Flash 独立播放器
- js经纬度坐标和度分秒互转
- for循环中控制事务单个提交问题
- 048、JAVA的Iterator迭代器
- 方舟生存进化服务器存档位置,方舟生存进化如何转移存档
- CH340G软件识别、usb转串口软件识别、测试
- 老男孩上海校区Python面试题
- 2D基本图形的Sign Distance Function (SDF)详解(上)
- (13)TEBD基态计算+DMRG算法
- C++中左移运算符<<、右移运算符>>、以及位与运算符
- [论文学习]Mask R-CNN
- java-字符串,抽象类与抽象
- 计算机虚拟筛选方法,药物发现的虚拟筛选基本方法 .ppt
- 串口软件Vofa+,超好用。可用于高速采集数据直观化显示动态