使用java编写贪吃蛇小游戏

小游戏(贪食蛇)
线程
面向对象
文件操作
内部类
数据库

步骤:
1–了解swing相关知识和小游戏设计
通过main方法启动初始化界面
逐步添加界面元素

	蛇的构成(多个关节组成)和运动方式(确认好方向,去尾加头)
	食物的构成怎么出现(随机,位置与界面宽度和高度保持整除关系(余数为龙))
	蛇如何吃豆子(头部元素与豆子元素坐标重合)
	确定使用哪一种容器(LinkedList)
	
2--配置界面
	宽度、高度
3--画出蛇
	准备数据(一个关节就是一个对象  一条蛇是一个容器)
	初始多个关节数据(关联着)
			
4--蛇运动(按照方向运动)  可以自定义线程、Timer
	方向控制(键盘方向键控制方向  、 snake按照要求的方向继续运动)
	360、撞墙、咬自己、调速、暂停
	
5--蛇吃豆子(不能出现在蛇身上 、 不能出界)
	Bean
	初始化豆子
	画出豆子
	吃豆子(循环)
	身体长长
	
6--记录每次游戏的成绩
	初始化数据通过配置文件配置(.properties、txt)

7 关卡设计
	3关  (速度、分数、时间)

开始代码编程:
所需类: SnakeEle.java(蛇的类) , Bean.java(豆子类) , Constant(常量类) , Main(实现代码类) , config.properties配置文件

----------SnakeEle.java------------

package com.java20180917.snake;

import java.io.Serializable;
/*

  • 蛇身体关节元素 实体类

  • */
    public class SnakeEle implements Serializable{

    private static final long serialVersionUID = -8508352375136668149L;
    private int se_size;
    private int se_x;
    private int se_y;
    public SnakeEle() {
    super();
    }
    public SnakeEle(int se_size, int se_x, int se_y) {
    super();
    this.se_size = se_size;
    this.se_x = se_x;
    this.se_y = se_y;
    }
    public int getSe_size() {
    return se_size;
    }
    public void setSe_size(int se_size) {
    this.se_size = se_size;
    }
    public int getSe_x() {
    return se_x;
    }
    public void setSe_x(int se_x) {
    this.se_x = se_x;
    }
    public int getSe_y() {
    return se_y;
    }
    public void setSe_y(int se_y) {
    this.se_y = se_y;
    }
    @Override
    public String toString() {
    return “SnakeEle [se_size=” + se_size + “, se_x=” + se_x + “, se_y=” + se_y + “]”;
    }
    }
    ---------- Bean.java------------
    package com.java20180917.snake;

import java.io.Serializable;

public class Bean implements Serializable{

private static final long serialVersionUID = 1894645424910104002L;

private int be_x;
private int be_y;
private int be_size;
public Bean() {
	super();
	// TODO Auto-generated constructor stub
}
public Bean(int be_x, int be_y, int be_size) {
	super();
	this.be_x = be_x;
	this.be_y = be_y;
	this.be_size = be_size;
}
public int getBe_x() {
	return be_x;
}
public void setBe_x(int be_x) {
	this.be_x = be_x;
}
public int getBe_y() {
	return be_y;
}
public void setBe_y(int be_y) {
	this.be_y = be_y;
}
public int getBe_size() {
	return be_size;
}
public void setBe_size(int be_size) {
	this.be_size = be_size;
}
public static long getSerialversionuid() {
	return serialVersionUID;
}
@Override
public String toString() {
	return "Bean [be_x=" + be_x + ", be_y=" + be_y + ", be_size=" + be_size + "]";
}
@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + be_size;
	result = prime * result + be_x;
	result = prime * result + be_y;
	return result;
}
@Override
public boolean equals(Object obj) {
	if (this == obj)
		return true;
	if (obj == null)
		return false;
	if (getClass() != obj.getClass())
		return false;
	Bean other = (Bean) obj;
	if (be_size != other.be_size)
		return false;
	if (be_x != other.be_x)
		return false;
	if (be_y != other.be_y)
		return false;
	return true;
}

}
----------Constant.java------------
package com.java20180917.snake;
/*

  • 常亮接口

  • /
    public interface Constant {
    /

    • 方向常亮
    • */
      public static final String UP = “up”;
      public static final String DOWN = “down”;
      public static final String LEFT = “left”;
      public static final String RIGHT = “right”;

    public static final String TIP = “GAME OVER!”;
    public static final String STOP = “stop” ;
    public static final String PAUSE = “pause” ;
    }

----------Main.java------------
package com.java20180917.snake;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**

  • 1–初始化界面 2–初始化面板 3–面板与界面合并

*/
public class Main extends JFrame implements KeyListener {

private static final long serialVersionUID = -1803858577233572276L;

private JPanel jp = null;

// 窗口的宽度
private int fw = 600;
// 窗口的高度
private int fh = 600;

// 蛇身初始长度
private int count = 10;
// 蛇身元素,每个方块大小20
private int se_size = 20;
// 蛇的横坐标
private int se_x = 100;
// 蛇的纵坐标
private int se_y = 200;

// 豆子的大小
private int bean_size = 20;
// 豆子计数的初始化
private int bean_count = 0;
// 豆子集合
private LinkedList<Bean> bean = new LinkedList<Bean>();
// 蛇运动的默认方向为UP,向上运动
private String direction = Constant.UP;
// 弹框提示语
private String tip = "";
// 蛇的运动状态
private String status = "";
// 蛇运动的速度
private long speed = 200;
// 关卡初始化,第一关
private int guanqia = 1;
// 蛇运动的开始时间
private long snake_start;
// 蛇结束运动的时间
private long snake_end;
// 按下暂停键的开始时间
private long snake_pause_start;
// 松开暂停键的结束时间
private long snake_pause_end;
// 记录暂停的时间差
private long snake_pause_time;
// 蛇的集合
private LinkedList<SnakeEle> snake = new LinkedList<SnakeEle>();

// 总初识化方法
private void init() {
	// 文件的读写方法
	initProperties();
	// 初始化蛇的方法
	initSnake();
	// 初始化豆子的方法
	initBean();
	// 初始化面板的方法
	initPanel();
	// 初始化窗口的方法
	initFrame();
	// 线程
	initThread();

}

// 文件的读写方法
private void initProperties() {

	try {
		Properties pps = new Properties();

		/*
		 * load(InputStream inStream) 从输入流中读取属性列表,通过对指定的文件,进行装载来获取该文件中的所有键 - 值对
		 */
		pps.load(new FileInputStream("D:/A工具/java20180917/src/config.properties"));
		Enumeration enu = pps.propertyNames();// 获取配置文件的名字
		// 要修改的变量
		fw = Integer.parseInt(pps.getProperty("fw"));
		fh = Integer.parseInt(pps.getProperty("fh"));
		speed = Integer.parseInt(pps.getProperty("speed"));
		while (enu.hasMoreElements()) {
			String strKey = (String) enu.nextElement();
			String strValue = pps.getProperty(strKey);
			System.out.println(strKey + "=" + strValue);
		}

	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

}

// 豆子的初始化方法
private void initBean() {
	// 随机生成豆子,使其范围在窗口的范围里,窗口的宽高是600,所以豆子也在宽高为600的范围中
	// bean_x 豆子的横坐标,bean_y 豆子的纵坐标
	int bean_x = 20 * Math.round((new Random().nextInt(30)));
	int bean_y = 20 * Math.round((new Random().nextInt(30)));
	// 判断,豆子随机的产生不能出现在蛇身上
	if (!snake.contains(new SnakeEle(bean_size, bean_x, bean_y))) {
		bean.add(new Bean(bean_x, bean_y, bean_size));
	}

}

// 线程
private void initThread() {
	new Thread(new Runnable() {
		@Override
		public void run() {
			while (true) {
				// 如果蛇的头部,即第一个元素的横纵坐标与豆子的横纵坐标相等,重合
				if (snake.getFirst().getSe_x() == bean.getFirst().getBe_x()
						&& snake.getFirst().getSe_y() == bean.getFirst().getBe_y()) {
					// 先将豆子清除
					bean.clear();
					// 再调用初始化豆子方法
					initBean();
					// 对豆子进行计数
					bean_count++;
					// 同时再蛇的尾部添加元素
					snake.add(new SnakeEle(se_size, snake.getLast().getSe_x(), snake.getLast().getSe_y()));

					// 豆子的数量可以对3整除,就进入下一个关卡,关卡自增,速度变快
					if (bean_count % 3 == 0 && bean_count < 10) {
						// 关卡自增
						guanqia++;
						// 重画
						jp.repaint();

						// 弹框
						int op_result = JOptionPane.showConfirmDialog(jp, "下一关!", "恭喜你!",
								JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE);
						// 弹框有两个按钮,继续和取消,值分别为0和2
						if (op_result == 0) {
							// 重新开始游戏
							speed -= 50;
						}
						if (op_result == 2) {
							System.exit(0);
						}
					}

				}

				if ("".equalsIgnoreCase(status)) {
					// 判断是否咬到自己(即 头部坐标与身体上的任何一个元素的坐标重合)

					for (int i = 1; i < snake.size(); i++) {
						if (snake.getFirst().getSe_x() == snake.get(i).getSe_x()
								&& snake.getFirst().getSe_y() == snake.get(i).getSe_y()) {
							fangfa();
						}
					}
					// 操作snake容器中的元素
					// 移除最后一个
					snake.removeLast();
					// 判断头部向上是否出界
					if (direction.equalsIgnoreCase(Constant.UP)) {

						if (snake.getFirst().getSe_y() - se_size < 0) {
							// 如果新的头部y坐标出界 提示game over调用方法,弹出信息框
							fangfa();
						} else
							snake.addFirst(new SnakeEle(se_size, snake.getFirst().getSe_x(),
									snake.getFirst().getSe_y() - se_size));
					}
					// 判断头部向右是否出界
					if (direction.equalsIgnoreCase(Constant.RIGHT)) {
						// 如果新的头部y坐标出界 提示game over
						if (snake.getFirst().getSe_x() + se_size > fw) {
							// 如果新的头部y坐标出界 提示game over调用方法,弹出信息框
							fangfa();
						} else
							snake.addFirst(new SnakeEle(se_size, snake.getFirst().getSe_x() + se_size,
									snake.getFirst().getSe_y()));
					}
					// 判断头部向左是否出界
					if (direction.equalsIgnoreCase(Constant.LEFT)) {

						if (snake.getFirst().getSe_x() - se_size < 0) {
							// 如果新的头部y坐标出界 提示game over调用方法,弹出信息框
							fangfa();
						} else
							snake.addFirst(new SnakeEle(se_size, snake.getFirst().getSe_x() - se_size,
									snake.getFirst().getSe_y()));
					}
					// 判断头部向下是否出界
					if (direction.equalsIgnoreCase(Constant.DOWN)) {

						if (snake.getFirst().getSe_y() + se_size > fh) {
							// 如果新的头部y坐标出界 提示game over调用方法,弹出信息框
							fangfa();
						} else
							snake.addFirst(new SnakeEle(se_size, snake.getFirst().getSe_x(),
									snake.getFirst().getSe_y() + se_size));
					}
					// 重画
					jp.repaint();
					try {
						Thread.sleep(speed);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

				}
			}
		}
	}).start();
}

// 初始化蛇的方法
private void initSnake() {
	// 创建多个有关联着的SankeEle对象
	// 添加到snake容器中

	for (int i = 0; i < count; i++) {
		snake.add(new SnakeEle(se_size, se_x, se_y + se_size * i));
	}
	// 记录蛇运动的开始时间
	snake_start = System.nanoTime();

}

// 初始化面板的方法
private void initPanel() {
	jp = new JPanel() {
		private static final long serialVersionUID = 4376420223914299841L;

		@Override
		public void paint(Graphics g) {
			super.paint(g);

			if (!"".equals(tip)) {
				g.setColor(Color.RED);
				g.setFont(new Font("", Font.BOLD, 40));
				g.drawString(tip, 160, 160);
			}

			for (int i = 0; i < snake.size(); i++) {
				if (i == 0) {
					g.setColor(Color.RED);
				} else {
					g.setColor(Color.white);
				}
				g.fill3DRect(snake.get(i).getSe_x(), snake.get(i).getSe_y(), snake.get(i).getSe_size(),
						snake.get(i).getSe_size(), true);
			}

			for (int i = 0; i < bean.size(); i++) {
				g.setColor(Color.orange);
				g.fill3DRect(bean.get(i).getBe_x(), bean.get(i).getBe_y(), bean.get(i).getBe_size(),
						bean.get(i).getBe_size(), true);
			}

			Integer bc = new Integer(bean_count);
			String s = bc.toString();

			g.setFont(new Font("", Font.BOLD, 30));
			g.setColor(Color.white);
			g.drawString("总长:", 50, 50);
			g.setColor(Color.red);
			g.drawString(s, 140, 50);

			Long sp = new Long(speed);
			String spe = Long.toString(sp);
			g.setColor(Color.white);
			g.drawString("速度:", 50, 80);
			g.setColor(Color.red);
			g.drawString(spe, 130, 80);
			g.setColor(Color.white);
			g.drawString("关卡:", 420, 50);
			Integer gq = new Integer(guanqia);
			String gg = gq.toString();
			g.setColor(Color.red);
			g.drawString(gg, 500, 50);
			g.setColor(Color.white);
			snake_end = System.nanoTime();
			g.drawString("时长:" + (snake_end - snake_start - snake_pause_time) / 1000000000, 50, 110);

		}
	};
	jp.setOpaque(false);
}

// 弹出提示信息框的方法
private void fangfa() {
	tip = Constant.TIP;
	status = Constant.STOP;
	int op_result = JOptionPane.showConfirmDialog(jp, "GAME OVER!", "你挂了!", JOptionPane.OK_CANCEL_OPTION,
			JOptionPane.ERROR_MESSAGE);
	if (op_result == 0) {
		// 重新开始游戏
		tip = "";
		direction = Constant.UP;
		status = "";
		snake.clear();
		initSnake();
		bean_count = 0;
		guanqia = 1;
		speed = 200;
	}
	if (op_result == 2) {
		System.exit(0);
	}

}

// 初始化窗口的方法
private void initFrame() {

// this.setTitle(“测试!”);
this.setSize(fw, fh);
this.getContentPane().setBackground(Color.BLACK);
this.setUndecorated(true);

	this.add(jp);

	this.setAlwaysOnTop(true);
	this.setLocationRelativeTo(null);
	this.setVisible(true);
	// 当前对象具有多重省份 (这是由继承结构决定的)
	// 第一个this -- 窗口对象
	// 第二个this -- 键盘监听器
	this.addKeyListener(this);
}

public Main() {
	// 调用初始化的总方法
	init();
}

public static void main(String[] args) {
	new Main();
}

@Override
public void keyTyped(KeyEvent e) {
	// TODO Auto-generated method stub

}

@Override
public void keyPressed(KeyEvent e) {
	// 按下哪一个按键 停止虚拟机运行

// System.out.println(e.getKeyCode());
// 按下esc键 关停虚拟机
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
System.exit(0);
}
// 如果当前方向的值不是左键并且按下的键为右键,即蛇在向右运动时,按左键无效,不能立马掉头
if (!this.direction.equalsIgnoreCase(Constant.LEFT) && e.getKeyCode() == KeyEvent.VK_RIGHT) {
// 则当前方向向右
this.direction = Constant.RIGHT;
}
// 同上,即相对立的两个方向值不能同时存在
if (!this.direction.equalsIgnoreCase(Constant.RIGHT) && e.getKeyCode() == KeyEvent.VK_LEFT) {
this.direction = Constant.LEFT;
}
if (!this.direction.equalsIgnoreCase(Constant.UP) && e.getKeyCode() == KeyEvent.VK_DOWN) {
this.direction = Constant.DOWN;
}
if (!this.direction.equalsIgnoreCase(Constant.DOWN) && e.getKeyCode() == KeyEvent.VK_UP) {
this.direction = Constant.UP;
}

	// 松开暂停键
	if (e.getKeyCode() == KeyEvent.VK_SPACE) {
		// 先检查status的值 如果是 "" 就修改成 pause 如果是 pause 就改成 ""
		switch (status) {// 重启
		case Constant.PAUSE:
			this.status = "";
			// 记录松开暂停键的结束时间
			snake_pause_end = System.nanoTime();
			// 暂停的时间差
			snake_pause_time += (snake_pause_end - snake_pause_start);
			break;
		case "":// 按下暂停键
			// 当前状态为按下暂停键
			this.status = Constant.PAUSE;
			// 记录按下暂停键的开始时间
			snake_pause_start = System.nanoTime();
			break;
		}
	}

}

@Override
public void keyReleased(KeyEvent e) {
	// TODO Auto-generated method stub

}

}
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值