一、实验目的
本实验的目的是使学生了解一个小游戏开发的流程,贪吃蛇游戏的开发代码量较大,考验学生的编程能力。
二、实验器材
通过Java运行输出
三、实验内容
1)实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子。玩家能利用上下左右键控制“蛇”的移动,“蛇”吃到“豆子”后“蛇”身体加长一节,得分增加,“蛇”碰到边界或蛇头与蛇身相撞,“蛇”死亡,游戏结束。
2)进行交互界面的设计,要有开始键、暂停键和停止退出的选项,能够控制游戏进程。对蛇吃到豆子进行分值计算,可以设置游戏速度,游戏音乐等拓展元素。
四、实验要点
①生成图形面板,建立墙、蛇、豆子
②如何通过代码控制蛇吃到豆子后蛇长度增加
③如何控制蛇前进速度
④背景音乐的插入(待实现)
⑤用界面上的按键响应键盘(经改进)
五、实现代码和运行界面
建立package snake
类GluttonousSnake
package snake;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
public class GluttonousSnake extends JFrame implements ActionListener {
private JButton upButton, downButton, leftButton, rightButton;//控制方向按钮
private JTextField score;//分数
private SnakeCanvas snake;//蛇的模型
public GluttonousSnake() {
super("贪吃蛇");//设置标题
this.setSize(725, 515);//设置大小
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//设置退出按键
this.setLocationRelativeTo(null);//设置窗口居中
JPanel p = new JPanel();//控制按钮和显示分数面板
p.setBorder(new TitledBorder("控制和显示区"));//设置这个面板的标题
this.getContentPane().add(p, "East");//设置此面板的位置
p.setLayout(new GridLayout(4, 1));//设置此面板的布局方式,为网格布局方式
JPanel p2 = new JPanel();//在此面板中添加显示分数的面板
p2.setLayout(new FlowLayout());//设置为流布局方式
p2.add(new JLabel("得分:"));
score = new JTextField("0");
score.setEditable(false);
p2.add(score);
p.add(p2);
//添加按钮,有图片的并添加事件监听
upButton = new JButton(" up " );
upButton.setActionCommand("up");
upButton.addActionListener(this);
downButton = new JButton( " down ");
downButton.setActionCommand("down");
downButton.addActionListener(this);
leftButton = new JButton(" left ");
leftButton.setActionCommand("left");
leftButton.addActionListener(this);
rightButton = new JButton( " right ");
rightButton.setActionCommand("right");
rightButton.addActionListener(this);
p.add(upButton);
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 2));
p1.add(leftButton);
p1.add(rightButton);
p.add(p1);
p.add(downButton);
addMenu();//添加菜单
start();
this.setResizable(false);
this.setVisible(true);
}
private void start() {
snake = new SnakeCanvas();
this.getContentPane().add(snake);
}
private void addMenu() {
JMenuBar menubar = new JMenuBar();
this.setJMenuBar(menubar);
JMenu game = new JMenu("游戏");
JMenu help = new JMenu("帮助");
JMenuItem jitemNew = new JMenuItem("新游戏");
jitemNew.setActionCommand("new");
jitemNew.addActionListener(this);
JMenuItem jitemPause = new JMenuItem("暂停");
jitemPause.setActionCommand("pause");
jitemPause.addActionListener(this);
JMenuItem jitemExit = new JMenuItem("退出");
jitemExit.setActionCommand("exit");
jitemExit.addActionListener(this);
game.add(jitemNew);
game.add(jitemPause);
game.addSeparator();//菜单里设置分隔线
game.add(jitemExit);
menubar.add(game);
menubar.add(help);
}
public static void main(String[] args) {
new GluttonousSnake();
}
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equalsIgnoreCase("exit")) {
System.exit(EXIT_ON_CLOSE);
}
if (e.getSource() instanceof JButton) {
if (e.getActionCommand().equalsIgnoreCase("up")) {//响应向上按钮按下事件
snake.setDirect(1);//设置蛇头运动方向
snake.repaint();//对蛇模型重新画
snake.timer.start();//定时器开始
return;
}
if (e.getActionCommand().equalsIgnoreCase("down")) {
snake.setDirect(2);
snake.repaint();
snake.timer.start();
return;
}
if (e.getActionCommand().equalsIgnoreCase("left")) {
snake.setDirect(3);
snake.repaint();
snake.timer.start();
return;
}
if (e.getActionCommand().equalsIgnoreCase("right")) {
snake.setDirect(4);
snake.repaint();
snake.timer.start();
return;
}
}
if (e.getSource() instanceof JMenuItem) {
if (e.getActionCommand().equalsIgnoreCase("new")) {
//this.getContentPane().remove(snake);
snake.init();
snake.repaint();
snake.timer.start();
}
if (e.getActionCommand().equalsIgnoreCase("pause")) {
snake.timer.stop();
JOptionPane.showMessageDialog(this, "继续请按“确定”");
snake.timer.start();
}
}
}
}
类MapCanvas
package snake;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
//蛇运行的地图
public class MapCanvas extends Canvas{
public void paint(Graphics g) {
g.setColor(Color.pink);
for(int i=30;i<=450;i+=30){
g.drawLine(0,i,450,i);
}
for(int i=30;i<=450;i+=30){
g.drawLine(i,0,i,450);
}
g.drawLine(0, 0, 450, 0);
g.drawLine(0, 450, 450, 450);
g.drawLine(0, 0, 0, 450);
g.drawLine(450, 0, 450, 450);
}
}
类SnakeCanvas
package sanke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JOptionPane;
import javax.swing.Timer;
//蛇模型
public class SnakeCanvas extends MapCanvas implements ActionListener {
private int number = 5;//蛇身的初始长度
public Timer timer;//定时器
private Body[] bodys;//蛇身
private Body food;//食物
public int score=0;//分数
private int speed=250;//定时器延时事件
int direct = 3;//蛇头初始方向
private int[][] mapflag = new int[455][455];//地图标记
//初始化
public void init(){
this.direct=3;
this.number=5;
this.bodys = new Body[this.number];
mapflag = new int[455][455];
for (int i = 0, x = 300; i < this.number; i++, x += 30) {
bodys[i] = new Body(x, 180);
mapflag[bodys[i].x][bodys[i].y] = 2;//如果是蛇体 设标记为2
}
food = creatFood();//创建食物
}
public SnakeCanvas() {
init();
timer = new Timer(speed, this);//创建定时器对象
timer.start();//定时器启动
}
private class Body {//蛇头成员
int x, y;
Body(int x, int y) {
this.x = x;
this.y = y;
}
}
public void paint(Graphics g) {//画图
super.paint(g);
if(bodys==null){
return ;
}
for (int i = 0; i < this.number; i++) {
if(i==0){//设置蛇头颜色不同
g.setColor(Color.pink);
}else{
g.setColor(Color.white);
}
mapflag[bodys[i].x][bodys[i].y] = 2;
g.fillOval(bodys[i].x, bodys[i].y, 30, 30);//画蛇体
}
g.setColor(Color.red);
g.fillOval(food.x, food.y, 30, 30);//画食物
}
public Body creatFood() {
int x = 0, y = 0;
do {//随机生成食物位置
Random r = new Random();//创建随机数对象
x = r.nextInt(450);//在0到所给的数中随机产生一个伪随机数
y = r.nextInt(450);
//System.out.println(mapflag[x][y]+"!");
} while (x % 30 != 0 || y % 30 != 0 || mapflag[x][y]==2 );//保证位置不是蛇身的部分,并且确保整齐性
//System.out.println(x + " " + y);
mapflag[x][y] = 1;//将食物标记为1,下面就可以通过标记的不同来判断是吃到食物还是自身,从而进行不同的操作
return new Body(x, y);
}
public void actionPerformed(ActionEvent e) {
if(bodys==null){
return ;
}
if(!isMove()){//如果移动失败,游戏结束
JOptionPane.showMessageDialog(this, "Game Over !");
bodys=null;
timer.stop();
return ;
}
repaint();
}
//蛇移动,原理:仅需对蛇头进行操作,然后将蛇头之前的位置赋值给现在蛇身的第二个部分,
//让之前的第二个部分复制个现在的第三个部分。。。。以此类推,模拟蛇运动(这是关键)
public void move(int x,int y) {
Body[] b=new Body[bodys.length];
for(int i=0;i<this.number;i++){
b[i]=new Body(bodys[i].x,bodys[i].y);
}
this.bodys[0].x=x;
this.bodys[0].y=y;
for(int i=1;i<this.number;i++){
this.bodys[i]=b[i-1];
}
setMapflag();
}
private void setMapflag() {//地图标记的刷新
mapflag=new int[455][455];
mapflag[food.x][food.y]=1;
for(int i=0;i<this.number;i++){
mapflag[bodys[i].x][bodys[i].y]=2;
}
}
//判断是否移动成功
public boolean isMove() {
if(bodys==null){
return false;
}
int x=bodys[0].x;
int y=bodys[0].y;
switch (this.direct) {//通过方向的不同对蛇头进行不同的操作
case 1:
y -= 30;
break;
case 2:
y += 30;
break;
case 3:
x -= 30;
break;
case 4:
x += 30;
break;
}
if(x<0||y>=450||x>=450||y<0){//越界问题
return false;
}
if(mapflag[x][y]==1){//标记为1 ,则为蛇头,要添加蛇身长度
this.number++;
addSnake(x,y);//添加蛇长
// System.out.println("*");
return true;
}else if(mapflag[x][y]==2){//标记为2,则为蛇体,即自己吃到自己,游戏结束
// System.out.println("^");
return false;
}else{
// System.out.println("%");
move(x,y);
return true;
}
}
//添加蛇身,类似move();
private void addSnake(int x, int y) {
this.score++;
Body[] b=new Body[bodys.length];
for(int i=0;i<this.number-1;i++){
b[i]=new Body(bodys[i].x,bodys[i].y);
}
bodys=new Body[this.number];
this.bodys[0]=new Body(x,y);
for(int i=1;i<this.number;i++){
this.bodys[i]=b[i-1];
}
setMapflag();
food = creatFood();
}
public void setDirect(int i) {//设置蛇头方向
this.direct = i;
}
}
点击游戏含暂停,重新开始新游戏的选择