简介:
(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 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();
// 实例化
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 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 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 nodeBody;
public GameJPanel() {
// 创建蛇身
headNode = new Node(130, 210, headNodeDir);
// 添加监听
this.addKeyListener(this);
// 实例化随机数类对象
random = new Random();
// 随机产生坐标,并且产生食物
randomCoord();
// 实例化蛇身集合
nodeBody = new Vector();
}
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();//启动线程
}
}