Greedy-Snake
此游戏已打包为 .exe 文件,可以直接下载先玩一下 download →要有 jre 环境噢,只要装了java的都有
先看效果图吧,开始游戏界面
点击 开始 按钮后的效果
上代码啦
先定义一个启动程序开始我们的代码好吧。
// StartMyGreedySnake.java
public class StartMyGreedySnake {
public static void main(String[] args) {
new StartView(); // 启动游戏界面
}
}
首先设计一个进入游戏的窗口
用 StartView
类实现
// StartView.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class StartView implements MouseListener {
// 创建开始游戏欢迎界面
private Frame jf = new Frame("贪吃蛇");
// 提示性标签
private JLabel jl;
// 设置进入游戏按钮
private JButton jb;
// 实现按钮方法
private void setButton() {
jf.setLayout(null);
jb = new JButton("开始游戏"); // 按钮提示内容
jb.setBounds(300, 500, 400, 70);
jb.setFont(new Font("楷体_2312", Font.BOLD, 50)); // 按钮字体及大小
jb.setBackground(new Color(177, 120, 205, 205));
jf.add(jb); // 加入窗口
jb.addMouseListener(this); // 为按钮添加监听
}
// 实现标签方法
private void setLabel() {
jf.setLayout(null);
jl = new JLabel("欢迎来到贪吃蛇游戏"); // 标签内容
jl.setFont(new Font("楷体_2312", Font.BOLD, 30)); // 字体
jl.setBackground(new Color(196, 34, 169)); // 字体颜色
jl.setBounds(380, 100, 400, 80); // 相对位置
jf.add(jl); // 加到窗口
}
StartView() {
// 设置窗口背景颜色
jf.setBackground(new Color(190, 44, 79));
// 设置按钮信息
setButton();
// 设置标签并加入窗口
setLabel();
// 设置点击右上角的关闭窗口结束程序运行
jf.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
// 设置窗口绝对位置
jf.setLocation(500, 200);
// 设置窗口大小
jf.setSize(1000, 800);
// 窗口大小不可变
jf.setResizable(false);
// 窗口可见
jf.setVisible(true);
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getSource().equals(jb)) {
// 进入游戏窗口
new SnakeGameView().showView();
// 设置原窗口不可见
jf.setVisible(false);
// 释放原窗口资源
jf.dispose();
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
然后是实现游戏界面有关的代码
定义一个 Node
节点类用以设置和获得蛇头节点和蛇蛋位置
// Node.java
public class Node {
private int nodeX;
private int nodeY;
public Node(int nodeX, int nodeY) {
this.nodeX = nodeX;
this.nodeY = nodeY;
}
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;
}
}
写一个 Snake
类,用链表表示蛇身,并实现蛇的移动,产生蛇蛋和判断蛇是否越界等。
// Snake.java
import java.util.LinkedList;
import java.util.Random;
public class Snake {
private LinkedList<Node> snakeBody;
private Random random;
//Snake的方向 0上, 1 右 , 2 下 , 3 左
private int direction = 0;
private SnakeGameView snakeGameView;
public Snake(int snakeHeadX, int snakeHeadY, SnakeGameView snakeGameView) {
random = new Random();
snakeBody = new LinkedList<>();
snakeBody.add(new Node(snakeHeadX, snakeHeadY));
this.snakeGameView = snakeGameView;
}
//获取Snake的方向
public int getDirection() {
return direction;
}
//设置Snake方向
public void setDirection(int direction) {
this.direction = direction;
}
//获取Snake头部结点
public Node getSnakeHead() {
return snakeBody.getFirst();
}
//获取Snake尾部结点
public Node getSnakeTail() {
return snakeBody.getLast();
}
//获取Snake的Body
public LinkedList<Node> getSnakeBody() {
return snakeBody;
}
//Snake移动
public void snakeMove() {
switch (direction) {
case 0:
snakeBody.addFirst(new Node(getSnakeHead().getNodeX(), getSnakeHead().getNodeY() - DrawView.VIEW_NUMBER));
break;
case 1:
snakeBody.addFirst(new Node(getSnakeHead().getNodeX() + DrawView.VIEW_NUMBER, getSnakeHead().getNodeY()));
break;
case 2:
snakeBody.addFirst(new Node(getSnakeHead().getNodeX(), getSnakeHead().getNodeY() + DrawView.VIEW_NUMBER));
break;
case 3:
snakeBody.addFirst(new Node(getSnakeHead().getNodeX() - DrawView.VIEW_NUMBER, getSnakeHead().getNodeY()));
break;
}
snakeBody.removeLast();
}
//Snake 吃Egg
public void eatEgg(Node egg) {
if (snakeBody.getFirst().getNodeX() == egg.getNodeX() && snakeBody.getFirst().getNodeY() == egg.getNodeY()) {
snakeBody.add(egg);
//产蛋
snakeGameView.setEgg(random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER,
random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER);
SnakeGameView.gameScore = SnakeGameView.gameScore + 10;
snakeGameView.getGameScores().setText(SnakeGameView.gameScore + "");
}
}
//Snake越界
public void snakeRunInterface() {
if (this.getSnakeHead().getNodeX() < 0 || this.getSnakeHead().getNodeY() < 0 ||
this.getSnakeHead().getNodeX() > (DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER) ||
this.getSnakeHead().getNodeY() > (DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER)) {
SnakeGameView.gameState = false;
}
}
}
写一个 GameControl
类,用以获得键盘上方向键和 Enter、Esc 键的功能
// GameControl.java
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class GameControl implements KeyListener {
private Snake snake;
public GameControl(Snake snake) {
this.snake = snake;
}
public void keyReleased(KeyEvent ke) {
switch (ke.getKeyCode()) {
case KeyEvent.VK_UP:
snake.setDirection(0);
break;
case KeyEvent.VK_RIGHT:
snake.setDirection(1);
break;
case KeyEvent.VK_DOWN:
snake.setDirection(2);
break;
case KeyEvent.VK_LEFT:
snake.setDirection(3);
break;
case KeyEvent.VK_ENTER:
if (SnakeGameView.gameState == true)
SnakeGameView.gameState = false;
else
SnakeGameView.gameState = true;
break;
case KeyEvent.VK_ESCAPE:
System.exit(0);
break;
}
}
public void keyPressed(KeyEvent arg0) {
}
public void keyTyped(KeyEvent arg0) {
}
}
写一个 DrawView
类,用以画图形界面,包括网格,蛇身,蛇蛋
// DrawView.java
import java.awt.*;
public class DrawView extends Panel {
//画板的宽度高度
public static final int VIEW_WIDTH = 40;
public static final int VIEW_HEIGHT = 40;
public static final int VIEW_NUMBER = 15;
private Image iBuffer;
private Graphics gBuffer;
private Snake snake;
private Node egg;
public DrawView(Snake snake, Node egg) {
this.snake = snake;
this.egg = egg;
}
public void paint(Graphics g) {
snake.snakeMove();
this.drawGridding(g);
this.drawSnake(g);
this.drawEgg(g);
snake.eatEgg(egg);
}
public void update(Graphics g) {
if (iBuffer == null) {
iBuffer = createImage(DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 1, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 1);
gBuffer = iBuffer.getGraphics();
}
gBuffer.setColor(getBackground());
gBuffer.fillRect(0, 0, DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 1, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 1);
paint(gBuffer);
g.drawImage(iBuffer, 0, 0, this);
}
//画网格
public void drawGridding(Graphics g) {
g.setColor(new Color(105, 84, 68));
for (int i = 0; i < VIEW_WIDTH; i++) {
g.drawLine(0, i * VIEW_NUMBER, VIEW_WIDTH * VIEW_NUMBER, i * VIEW_NUMBER);
}
for (int i = 0; i < VIEW_HEIGHT; i++) {
g.drawLine(i * VIEW_NUMBER, 0, i * VIEW_NUMBER, VIEW_WIDTH * VIEW_NUMBER);
}
}
//画Snake
public void drawSnake(Graphics g) {
for (int i = 0; i < snake.getSnakeBody().size(); i++) {
g.setColor(new Color(110, 191, 64));
if (i == 0)
g.setColor(new Color(255,255,255));
g.fillRect(snake.getSnakeBody().get(i).getNodeX(), snake.getSnakeBody().get(i).getNodeY(), VIEW_NUMBER, VIEW_NUMBER);
}
}
//画Egg
public void drawEgg(Graphics g) {
g.setColor(Color.yellow);
g.fillRect(egg.getNodeX(), egg.getNodeY(), VIEW_NUMBER, VIEW_NUMBER);
}
}
再写一个 GameRunThread
类,用线程控制游戏开始
package greedysnake3;
public class GameRunThread implements Runnable {
private int runTime = 200;
private DrawView drawView;
private Snake snake;
public GameRunThread(DrawView drawView, Snake snake) {
this.drawView = drawView;
this.snake = snake;
}
public void run() {
while (true) {
snake.snakeRunInterface();
if (SnakeGameView.gameState) {
drawView.repaint();
try {
Thread.sleep(runTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
最后写游戏界面类,在这个类里调用其他类的方法以实现游戏功能
// SnakeGameView.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Random;
public class SnakeGameView extends Frame implements MouseListener {
// 定义游戏窗口名
//JFrame jf = new JFrame("Greedy-Snake");
// 声明画笔
private DrawView drawView;
// 声明开始,重新开始,退出游戏按钮
JButton start, reset, exit;
// 声明分数标签
JLabel grade;
// 声明分数文本框
JTextField score;
// 声明游戏操作方法文本域
JTextArea hint;
private Random random;
public static boolean gameState = true;
public static int gameScore = 0;
private Snake snake;
private Node egg;
private GameRunThread grt;
public static Thread thread;
public SnakeGameView() {
random = new Random();
//初始化Snake
snake = new Snake(10 * DrawView.VIEW_NUMBER + random.nextInt(19) * DrawView.VIEW_NUMBER,
10 * DrawView.VIEW_NUMBER + random.nextInt(19) * DrawView.VIEW_NUMBER, this);
//初始化Egg
egg = new Node(random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER,
random.nextInt(DrawView.VIEW_WIDTH - 1) * DrawView.VIEW_NUMBER);
//初始化画板
drawView = new DrawView(snake, egg);
grt = new GameRunThread(drawView, snake);
thread = new Thread(grt);
// 初始化画板
setPanel();
// 设置按钮
setButton();
// 设置标签
setLabel();
// 设置文本框
setText();
// 初始化文本域操作方法
setJTA();
}
//设置鸡蛋
public void setEgg(int eggX, int eggY) {
this.egg.setNodeX(eggX);
this.egg.setNodeY(eggY);
}
// 获得分数
public JTextField getGameScores() {
return score;
}
public void showView() {
drawView.setBackground(new Color(51, 51, 51));
drawView.setBounds(20, 50, DrawView.VIEW_WIDTH * DrawView.VIEW_NUMBER + 1, DrawView.VIEW_HEIGHT * DrawView.VIEW_NUMBER + 1);
this.setTitle("Greedy-Snake");
this.setSize(1000, 800);
this.setLocation(500, 200);
this.setLayout(null);
this.setBackground(new Color(2, 250, 250));
this.add(drawView);
this.addKeyListener(new GameControl(snake));
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
System.exit(0);
}
});
this.setVisible(true);
}
// 初始化画板
private void setPanel() {
drawView = new DrawView(snake, egg);
grt = new GameRunThread(drawView, snake);
thread = new Thread(grt);
}
// 实现按钮方法
private void setButton() {
this.setLayout(null);
// 设置开始按钮
start = new JButton("开始");
start.setBounds(800, 60, 180, 50);
start.setFont(new Font("宋体", 1, 18));
this.add(start);
// 设置重新开始按钮
reset = new JButton("重新开始");
reset.setFont(new Font("宋体", 1, 18));
reset.setBounds(800, 150, 180, 50);
this.add(reset);
// 设置退出按钮
exit = new JButton("退出游戏");
exit.setFont(new Font("宋体", 1, 18));
exit.setBounds(800, 240, 180, 50);
this.add(exit);
// 初始化按钮添加监听器
initial();
}
// 给各按钮添加监听器
void initial() {
start.addMouseListener(this);
reset.addMouseListener(this);
exit.addMouseListener(this);
}
// 设置标签的方法
private void setLabel() {
this.setLayout(null);
grade = new JLabel("分数");
grade.setFont(new Font("宋体", 1, 20));
grade.setBounds(800, 330, 100, 50);
this.add(grade);
}
// 显示分数的方法
private void setText() {
this.setLayout(null);
score = new JTextField("0", JTextField.CENTER);
score.setFont(new Font("宋体", 1, 20));
score.setBounds(800, 420, 100, 50);
score.setEditable(false);
this.add(score);
}
// 显示操作方法
private void setJTA() {
this.setLayout(null);
hint = new JTextArea("操作指导 ← 向左 → 向右 ↑ 向上 ↓ 向下 Esc 退出");
hint.setFont(new Font("宋体", 0, 24));
hint.setBackground(new Color(243, 5, 100));
hint.setBounds(40, 700, 920, 60);
hint.setEditable(false);
this.add(hint);
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getSource().equals(start)) {
SnakeGameView newSnake = new SnakeGameView();
newSnake.initial();
this.setVisible(false);
this.dispose();
new SnakeGameView().showView();
thread.start();
}
if (e.getSource().equals(reset)) {
SnakeGameView newSnake = new SnakeGameView();
newSnake.initial();
this.setVisible(false);
this.dispose();
new SnakeGameView().showView();
thread.start();
}
if (e.getSource().equals(exit)) {
System.exit(0);
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
注,核心源码摘自 链接
本来自己照着想写一个自己的贪吃蛇,奈何学习完后,就差一点点了,还是没能成功,所以最后在上面大佬的代码上加以改进写出了这个还是带有 bug 的贪吃蛇小游戏,望读者尽力闭坑,不过这里面的很多东西还是值得加以学习的