java实现贪吃蛇小游戏

简介:

(1)玩法,玩家通过控制上、下、左、右四键来控制蛇来寻找地图上的食物,当蛇吃下食物时蛇身变长

(2)基本步骤:

首先,自定义窗体,在窗体上添加面板。

然后,绘制地图、蛇头、蛇身、键的监听和线程操作

然后,解决蛇要吃的食物问题

还有,解决碰壁问题

最后,实现是否退出游戏

总结:这个游戏实现了基本功能,有待有共同爱好的你继续完善

贪吃蛇游戏的窗体:
package frame;
import java.awt.BorderLayout;
/**
 * 窗体
 * 
 * @author Administrator
 * 
 */
public class GameJFrame extends JFrame {

	private JPanel contentPane;
	/**
	 * Create the frame.
	 */
	public GameJFrame() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(539, 451);
		contentPane = new GameJPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		this.setLocationRelativeTo(null);
		this.setTitle("贪吃蛇游戏");
		this.setVisible(true);
	}

}
游戏面板的操作,绘制游戏面板、蛇身、判断蛇身碰壁、及键盘按钮处理:
package frame;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import snake.domain.Box;

/**
 * 游戏面板
 * 
 * @author Administrator
 * 
 */
public class GameJPanel extends JPanel implements Runnable, KeyListener {
	// 声明食物
	private Box food, head;
	private int headX = 220, headY = 120;

	private Vector<Box> snake;
	// 声明蛇头的区域范围
	private int minY = 20, maxY = 380; 
	private int minX = 20, maxX = 480;

	private final int DIR_DEFAULT = -1;
	private final int DIR_UP = 1;
	private final int DIR_DOWN = 2;
	private final int DIR_LEFT = 3;
	private final int DIR_RIGHT = 4;

	// 游戏是否退出的标识符
	private boolean isExit = true;
	private boolean isDeath = true;
	// 随机数
	private Random random;
	// 在局部中已经声明所以没必要在声明
	// private int randomX, randomY;
	// private int foodX, foodY;
	private Image image;

	public GameJPanel() {
		// 这是用的一种工具流的方法加载图片
		/*
		 * // 加载图片放到构造函数中,在全局变量中声明 try {
		 * image=ImageIO.read(getClass().getClassLoader
		 * ().getResourceAsStream("images//z.jpg")); } catch (IOException e) {
		 * // TODO Auto-generated catch block e.printStackTrace(); }
		 */
		image = Toolkit.getDefaultToolkit().getImage("src//images//z.jpg");
		// 绝对路径
		// image=Toolkit.getDefaultToolkit().getImage("E:\\javaproject1\\javalianxi2\\SnakGamePaneJPanel\\src\\images\\z.jpg");
		// 相对路径
		// image=Toolkit.getDefaultToolkit().getImage(GameJPanel.class.getResource("../images/z.jpg"));

		// 实例化随机对象
		random = new Random();
		initGame();
		// 注册监听事件
		addKeyListener(this);
		// 设置焦点事件使贪吃蛇能动起来受键盘控制
		this.setFocusable(true);
		// 启动线程
		new Thread(this).start();

	}

	private void initGame() {
		// 实例化集合
		snake = new Vector<Box>();
		// 实例化
		head = new Box(headX, headY, DIR_DEFAULT);
		// 添加到集合
		snake.add(head);
		// 实例化食物
		initFood();
	}

	/**
	 * 重新产生食物
	 */
	private void initFood() {
		// 随机产生的数字
		// 因为在这一个地方使用,所以没必要在全局中声明
		int randomX = random.nextInt(24) + 1;// 1--25
		int randomY = random.nextInt(19) + 1;// 1--20

		// 计算出食物的坐标
		int foodX = randomX * Box.BOXSIZE;
		int foodY = randomY * Box.BOXSIZE;
		// 获取了蛇头下一个位置的x与y坐标
		int xy[] = getNextHeadXY(head.getDIR());
		// 判断食物的坐标是否与蛇头下个位置一致
		if (xy[0] == foodX && xy[1] == foodY) {
			initFood();
			return;

		}

		// 怎么排除食物与蛇头或蛇身的位置冲突问题

		for (int i = 0; i < snake.size(); i++) {
			// 获取蛇头与蛇身,蛇头的下一个位置也不能产生食物
			Box body = snake.get(i);
			if (body != snake.lastElement()) {
				if (foodX == body.getX() && foodY == body.getY()) {
					initFood();
					return;
				}
			}
		}
		// 实例化食物
		food = new Box(foodX, foodY);
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);// 重新绘制的过程
		g.setColor(Color.red);
		/**
		 * 画横线
		 */
		g.fill3DRect(0, 0, 20, 40, true);

		for (int col = 0; col < 450; col += 40) {
			g.drawImage(image, 0, col, null);
			g.drawImage(image, 500, col, null);
		}
		for (int row = 0; row <= 480; row += 20) {
			g.drawImage(image, row, 0, null);
			g.drawImage(image, row, 400, null);
		}

		g.drawLine(20, 20, 500, 20);
		g.drawLine(20, 20, 20, 400);
		g.drawLine(500, 20, 500, 400);
		g.drawLine(20, 400, 500, 400);

		/**
		 * 画竖线
		 */
		/*
		 * for (int col = 20; col < 330; col += 20) { g.drawLine(col, minY, col,
		 * maxY); }
		 */
		// 设置绘制食物的颜色
		g.setColor(Color.red);
		// 绘制食物
		g.fill3DRect(food.getX(), food.getY(), Box.BOXSIZE, Box.BOXSIZE, true);
		/**
		 * 遍历集合
		 */

		for (int i = 0; i < snake.size(); i++) {
			if (i == 0) {
				// 设置绘制蛇头的颜色
				g.setColor(Color.green);
			} else {
				g.setColor(Color.yellow);
			}
			// 获取集合中指定位置的对象
			Box box = snake.get(i);
			// 绘制蛇头
			g.fill3DRect(box.getX(), box.getY(), Box.BOXSIZE, Box.BOXSIZE, true);
		}

	}

	@Override
	public void run() {
		try {
			while (isDeath) {
				if (isExit) {
					// 判断是否碰撞
					if (checkHit()) {
						// 实例化食物
						initFood();
					}

					if (moveHead()) {// 移动蛇头
						moveSnakeBody();
					} else {
						displayMsg();
					}// 移动蛇身

					// 重新绘制这个面板
					this.repaint();
					Thread.sleep(200);
				}
			}
		} catch (Exception ex) {
			ex.getStackTrace();
		}
	}

	private void moveSnakeBody() {
		// 声明蛇头
		Box head = null;
		// 声明每个蛇身对象
		Box body = null;
		int headDir = 0;
		int temp = 0;

		/*
		 * Iterator<Box> it=snake.iterator(); while(it.hasNext()){ Box
		 * box=it.next(); }
		 */
		if (snake.size() > 1) {
			// 遍历集合
			for (int i = 0; i < snake.size(); i++) {
				if (i == 0) {
					head = snake.get(i);
					headDir = head.getDIR();// 获取蛇头的方向
				} else {
					body = snake.get(i);
					int dir = body.getDIR();// 拿到蛇身的方向

					switch (dir) {
					case DIR_UP:
						body.setY(body.getY() - 20);
						break;
					case DIR_DOWN:
						body.setY(body.getY() + 20);
						break;
					case DIR_LEFT:
						body.setX(body.getX() - 20);
						break;
					case DIR_RIGHT:
						body.setX(body.getX() + 20);
						break;
					}
					temp = dir;// 记录第一个蛇身的方向{记录蛇头的方向(每次都是把上一个舍身当做蛇头来处理)}
					dir = headDir;// 把蛇身的方向赋值给下一个蛇身
					body.setDIR(dir);// 保证了第一个蛇身的方向与蛇头一致(改变蛇身方向)
					headDir = temp;// 把第一个蛇身当做蛇头(把每一个蛇身当做蛇头)

				}

			}
		}

	}

	/**
	 * 检测碰撞问题
	 */
	private boolean checkHit() {
		// 根据坐标是否完全相同
		if (head.getX() == food.getX() && head.getY() == food.getY()) {
			// 首先获取最后一个集合对象 保证了要吃的食物与最后一个集合对象的方向一致
			Box box = snake.lastElement();
			int dir = box.getDIR();
			switch (dir) {
			case DIR_UP:
				food.setX(box.getX());
				food.setY(box.getY() + 20);
				break;
			case DIR_DOWN:
				food.setX(box.getX());
				food.setY(box.getY() - 20);
				break;
			case DIR_LEFT:
				food.setX(box.getX() + 20);
				food.setY(box.getY());
				break;
			case DIR_RIGHT:
				food.setX(box.getX() - 20);
				food.setY(box.getY());
				break;
			}
			food.setDIR(box.getDIR());
			// 添加到集合中
			snake.add(food);
			return true;
		}
		return false;
	}

	/**
	 * 移动蛇头
	 */
	private boolean moveHead() {

		/*
		 * //获取集合的迭代器 Iterator<Box> it=snake.iterator(); int i=0; //遍历
		 * while(it.hasNext()){ //获取蛇身 Box body=it.next(); if(i!=0){ //比较一遍
		 * if(body.getX()==head.getX()&&body.getY()==head.getY()){ return false;
		 * } } i++; }
		 */
		boolean flag = true;
		int dir = head.getDIR();// 获取蛇头的方向
		// 声明下一个位置的蛇头对象{这是一种方法}
		/*
		 * Box tempHead = new Box(); // 首先我能不能根据蛇头的方向来获取蛇头移动的下一个位置 switch (dir)
		 * { case DIR_UP: tempHead.setX(head.getX()); tempHead.setY(head.getY()
		 * - Box.BOXSIZE);
		 * 
		 * break; case DIR_DOWN: tempHead.setX(head.getX());
		 * tempHead.setY(head.getY() + Box.BOXSIZE); break; case DIR_LEFT:
		 * tempHead.setX(head.getX() - Box.BOXSIZE); tempHead.setY(head.getY());
		 * break; case DIR_RIGHT: tempHead.setX(head.getX() + Box.BOXSIZE);
		 * tempHead.setY(head.getY()); break; }
		 */
		/*
		 * // 声明下一个位置的蛇头坐标 int x = 0, y = 0; // 首先我能不能根据蛇头的方向来获取蛇头移动的下一个位置
		 * switch (dir) { case DIR_UP: x = head.getX(); y = head.getY() -
		 * Box.BOXSIZE;
		 * 
		 * break; case DIR_DOWN: x = head.getX(); y = head.getY() + Box.BOXSIZE;
		 * break; case DIR_LEFT: x = head.getX() - Box.BOXSIZE; y = head.getY();
		 * break; case DIR_RIGHT: x = head.getX() + Box.BOXSIZE; y =
		 * head.getY(); break; }
		 */
		// 先定义数组,再封装方法
		/**
		 * 根据蛇头方向获取蛇头下一个位置的坐标 [0]代表是x坐标 [1]代表是y坐标
		 */
		// 获取蛇头下一个位置的坐标,存放在数组中
		int[] xy = getNextHeadXY(dir);
/*		// 移动蛇头时候要处理碰撞身体问题

		for (int i = 0; i < snake.size(); i++) {
			// 获取蛇身
			Box body = snake.get(i);

			// 这是加了一个判断使蛇头能吃掉一个蛇身而不死亡
			
			 * if(body==snake.lastElement()){
			 * 
			 * }else{ // 判断蛇身与蛇头移动下一个位置的坐标是否相同 if (body.getX() == x&&
			 * body.getY() ==y) { return false; } }
			 
			// 方法改进
			// 最后一个不要判断,当蛇头移动到最后一个的时候,最后一个移走了
			if (body != snake.lastElement()) {
				// 判断蛇身与蛇头移动下一个位置的坐标是否相同
				if (body.getX() == xy[0] && body.getY() == xy[1]) {
					return false;
				}
			}*/
		
//		进一步改进
			// 移动蛇头时候要处理碰撞身体问题
		int size=snake.size();
			for (int i = 0; i < size; i++) {
				// 获取蛇身
				Box body = snake.get(i);
				if(size==2){
					if (body.getX() == xy[0] && body.getY() == xy[1]) {
						return false;
					}
				}
				// 方法改进
				// 最后一个不要判断,当蛇头移动到最后一个的时候,最后一个移走了
			if (body != snake.lastElement()) {
					// 判断蛇身与蛇头移动下一个位置的坐标是否相同
					if (body.getX() == xy[0] && body.getY() == xy[1]) {
						return false;
					}
				}

		}
		// 如果蛇头碰不到蛇身再处理
		switch (dir) {
		case DIR_UP:
			if (head.getY() <= minY) {
				flag = false;
			} else {
				head.setY(head.getY() - Box.BOXSIZE);
			}
			break;

		case DIR_DOWN:
			if (head.getY() >= maxY) {
				flag = false;
			} else {
				head.setY(head.getY() + Box.BOXSIZE);
			}
			break;
		case DIR_LEFT:
			if (head.getX() <= minX) {
				flag = false;
			} else {
				head.setX(head.getX() - Box.BOXSIZE);
			}
			break;
		case DIR_RIGHT:
			if (head.getX() >= maxX) {
				flag = false;
			} else {
				head.setX(head.getX() + Box.BOXSIZE);
			}
			break;
		}
		return flag;
	}

	private int[] getNextHeadXY(int dir) {
		// 声明下一个位置的蛇头坐标
		int xy[] = new int[2];
		// 首先我能不能根据蛇头的方向来获取蛇头移动的下一个位置
		switch (dir) {
		case DIR_UP:
			xy[0] = head.getX();
			xy[1] = head.getY() - Box.BOXSIZE;

			break;
		case DIR_DOWN:
			xy[0] = head.getX();
			xy[1] = head.getY() + Box.BOXSIZE;
			break;
		case DIR_LEFT:
			xy[0] = head.getX() - Box.BOXSIZE;
			xy[1] = head.getY();
			break;
		case DIR_RIGHT:
			xy[0] = head.getX() + Box.BOXSIZE;
			xy[1] = head.getY();
			break;
		}
		return xy;
	}

	// 进一步改动
	/*
	 * public void displayMsg() { isExit = false;
	 */
	public void displayMsg() {
		isExit = false;
		int temp = JOptionPane.showConfirmDialog(null, "游戏死亡", "提示",
				JOptionPane.OK_OPTION);

		// temp==0代表要
		if (temp == 0) {
			isDeath = false;
		} else {
			isExit=true;
			initGame();
		}
	}

	/**
	 * 键入某个键时调用这个方法
	 */
	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	/**
	 * 按下某个键时调用这个方法
	 */
	@Override
	public void keyPressed(KeyEvent e) {
		int KeyCode = e.getKeyCode();
		switch (KeyCode) {
		case KeyEvent.VK_UP:
			// 加一个判断使按与蛇头方向相反时不会死掉
			if (head.getDIR() == DIR_DOWN) {
				break;
			} else {
				head.setDIR(DIR_UP);
				break;
			}
		case KeyEvent.VK_DOWN:
			if (head.getDIR() == DIR_UP) {
				break;
			} else {
				head.setDIR(DIR_DOWN);
				break;
			}
		case KeyEvent.VK_LEFT:
			if (head.getDIR() == DIR_RIGHT) {
				break;
			} else {
				head.setDIR(DIR_LEFT);
				break;
			}
		case KeyEvent.VK_RIGHT:
			if (head.getDIR() == DIR_LEFT) {
				break;
			} else {
				head.setDIR(DIR_RIGHT);
				break;
			}
			// 按下空格建的时候暂停游戏
		case KeyEvent.VK_SPACE:
			// 这是解释过程
			/*
			 * isExit=false; isExit=true;
			 */
			isExit = !isExit;
			break;
		}
	}

	/**
	 * 释放某个键时调用这个方法
	 */
	@Override
	public void keyReleased(KeyEvent e) {

	}
}
游戏主面板:
package frame;

import java.awt.BorderLayout;

public class IndexJFrame extends JFrame implements ActionListener{

	private JPanel contentPane;
	private JButton play_btn,ins_btn,score_btn,about_btn;
	

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					IndexJFrame frame = new IndexJFrame();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public IndexJFrame() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize( 539, 451);
		contentPane = new JPanel();
		contentPane.setBackground(new Color(204, 204, 51));
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		contentPane.setLayout(null);
		
		play_btn = new JButton("开始游戏");
		play_btn.setBounds(174, 139, 157, 23);
		contentPane.add(play_btn);
		
		ins_btn = new JButton("游戏说明");
		ins_btn.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
			}
		});
		ins_btn.setBounds(174, 185, 157, 23);
		contentPane.add(ins_btn);
		
		score_btn = new JButton("最高纪录");
		score_btn.setBounds(174, 228, 157, 23);
		contentPane.add(score_btn);
		
		about_btn = new JButton("关于我们");
		about_btn.setBounds(174, 276, 157, 23);
		contentPane.add(about_btn);
		this.setLocationRelativeTo(null);
		//注册事件
		play_btn.addActionListener(this);
		ins_btn.addActionListener(this);
		score_btn.addActionListener(this);
		about_btn.addActionListener(this);
		
		
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		String command=e.getActionCommand();
		switch (command) {
		case "开始游戏":
			this.dispose();
			new GameJFrame();//游戏界面
			break;
		}
	}
}
食物的实现
package snake.domain;

/**
 * 把方格当做了蛇头和蛇身的替代品
 * 食物
 * @author Administrator
 * 
 */
public class Box {

	private int x;// 方格的x坐标
	private int y;// 方格的y坐标
	public static final int BOXSIZE = 20;
	private int DIR;// 运动的方向

	public Box() {
		super();
		// TODO Auto-generated constructor stub

	}
	

	public Box(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}


	public Box(int x, int y, int dIR) {
		super();
		this.x = x;
		this.y = y;
		DIR = dIR;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getDIR() {
		return DIR;
	}

	public void setDIR(int dIR) {
		DIR = dIR;
	}

}
Snake2
package snake2;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import java.util.Vector;

import javax.swing.JPanel;

public class GameJPanel extends JPanel implements KeyListener, Runnable {
	/**
	 * 
	 */
	// 定义蛇头
	private Node headNode;
	// 游戏结束标志
	private boolean temp = true;
	// 蛇头的方向
	private int headNodeDir = 2;
	// 定义1,2,3,4,-1分别表示上,下,左,右,停止
	private static final int DIR_UP = 1;
	private static final int DIR_DOWN = 2;
	private static final int DIR_LEFT = 3;
	private static final int DIR_RIGHT = 4;
	private static final int DIR_STOP = -1;
	// 产生随机数对象
	private Random random;
	// 声明食物
	private Node eatNode;
	// 声明蛇身
	private Vector<Node> nodeBody;

	public GameJPanel() {
		// 创建蛇身
		headNode = new Node(130, 210, headNodeDir);
		// 添加监听
		this.addKeyListener(this);
		// 实例化随机数类对象
		random = new Random();
		// 随机产生坐标,并且产生食物
		randomCoord();
		// 实例化蛇身集合
		nodeBody = new Vector<Node>();
	}
	private void randomCoord() {
		int col = random.nextInt(10);
		int eatNodeX = col * 20 + 50;
		int row = random.nextInt(15);
		int eatNodeY = row * 20 + 50;
		// 实例化
		eatNode = new Node(eatNodeX, eatNodeY, DIR_STOP);
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
		// 设置颜色
		g.setColor(Color.red);
		// 绘制行的直线
		for (int row = 50; row <= 350; row += 20) {
			g.drawLine(50, row, 250, row);
		}
		// 绘制列的直线
		for (int col = 50; col <= 250; col += 20) {
			g.drawLine(col, 50, col, 350);
		}
		// 绘制蛇头
		g.setColor(new Color(0.5f, 0.6f, 0.7f));
		g.fillRect(headNode.getNodeX(), headNode.getNodeY(), 20, 20);
		//绘制食物
		g.setColor(new Color(0.8f,0.8f,0.8f));
		g.fillRect(eatNode.getNodeX(), eatNode.getNodeY(), 20, 20);
	}

	@Override
	public void run() {

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

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

	}

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

	}

}
package snake2;
/**
 * 创建每个节点
 * 节点既可以是蛇头,也可以是蛇身
 * @author Administrator
 *
 */
public class Node {
	private int nodeX,nodeY;
	private int nodeDir;
	public Node(int nodeX,int nodeY,int nodeDir) {
		this.nodeX=nodeX;
		this.nodeY=nodeY;
		this.nodeDir=nodeDir;
	}
	public int getNodeX() {
		return nodeX;
	}
	public void setNodeX(int nodeX) {
		this.nodeX = nodeX;
	}
	public int getNodeY() {
		return nodeY;
	}
	public void setNodeY(int nodeY) {
		this.nodeY = nodeY;
	}
	public int getNodeDir() {
		return nodeDir;
	}
	public void setNodeDir(int nodeDir) {
		this.nodeDir = nodeDir;
	}
	
}


package snake2;

public class SnakeGame {
	public static void main(String[] args) {
		new SnakeJFrame();
	}

}
package snake2;
import java.awt.Container;

import javax.swing.JFrame;

import frame.GameJPanel;

public class SnakeJFrame extends JFrame {
	public GameJPanel gameJPanel;
	public SnakeJFrame() {
		this.setTitle("贪吃蛇游戏");
		this.setVisible(true);
		this.setBounds(300,200,330,430);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	//首先获取Container
		Container c=this.getContentPane();
		gameJPanel=new GameJPanel();
		//添加面板
		c.add(gameJPanel);
		new Thread(gameJPanel).start();//启动线程
		
	}
}







©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页