java实现贪吃蛇小游戏

时间:2022-03-23 23:47:26

简介:

(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--25int 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);}@Overridepublic 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);}}@Overridepublic 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();}}/** * 键入某个键时调用这个方法 */@Overridepublic void keyTyped(KeyEvent e) {// TODO Auto-generated method stub}/** * 按下某个键时调用这个方法 */@Overridepublic 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;}}/** * 释放某个键时调用这个方法 */@Overridepublic 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);}@Overridepublic 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;}}
Snake2package 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);}@Overridepublic 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);}@Overridepublic void run() {}@Overridepublic void keyTyped(KeyEvent e) {// TODO Auto-generated method stub}@Overridepublic void keyPressed(KeyEvent e) {// TODO Auto-generated method stub}@Overridepublic 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);//首先获取ContainerContainer c=this.getContentPane();gameJPanel=new GameJPanel();//添加面板c.add(gameJPanel);new Thread(gameJPanel).start();//启动线程}}