您的位置:首页 > 编程语言 > Java开发

Java经典小游戏——贪吃蛇简单实现(附源码)

2020-07-27 23:22 295 查看

文章目录

  • 四 打jar包
  • 源码

  • 在我们学习java的时候,为了提高我们的兴趣,我们经常会使用所学到的知识去做一些小游戏,这篇blog就介绍了一个经典而且好理解的小游戏-贪吃蛇。

    一、使用知识

    • Jframe
    • GUI
    • 双向链表
    • 线程

    二、使用工具

    • IntelliJ IDEA
    • jdk 1.8

    三、开发过程

    3.1素材准备

    首先在开发之前应该准备一些素材,已备用,我主要找了一个图片以及一段优雅的音乐。

    3.2 开发过程

    3.2.1 创建项目
    • 首先进入idea首页 open一个你想放项目的文件夹
    • 进入之后右键文件名 new 一个新的Directory——Snake

    • 把准备好的素材复制到文件中
    • 继续创建目录 src/Sanke
    • 选中src Mark Directory as — Souces 把src添加为根目录
    3.2.2 页面设计
    • 创建java Class 文件 Snake - new - java class SnakeName 接下来的时候会对这个SnakeName.java里面的代码不停完善
      首先设置窗口格式
    package Sanke;
    
    import javax.swing.*;
    
    /**
    * @author Swyee
    **/
    public class SnakeGame extends JFrame {
    
    SnakeGame(){
    this.setBounds(100, 50, 700, 500);//设置窗口大小
    this.setLayout(null);//更改layout 以便添加组件
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭窗口的状态
    this.setResizable(false);//窗口不可以改变大小
    this.setVisible(true);//设置焦点状态为true
    }
    public static void main(String[] args) {
    new SnakeGame();
    }
    
    }

    • 继续创建新的文件 SnakeGrid
    package Sanke;
    
    import java.awt.*;
    
    /**
    * @author Swyee
    **/
    public class SnakeGrid extends Panel {
    SnakeGrid(){
    this.setBounds(0, 0, 700, 400);
    this.setBackground(Color.black);设置背景颜色
    }
    
    }
    • 将页面引用到SnakeGame.java中
    package Sanke;
    
    import javax.swing.*;
    
    /**
    * @author Swyee
    **/
    public class SnakeGame extends JFrame {
    SnakeGrid snakeGrid= new SnakeGrid();
    SnakeGame(){
    this.setBounds(100, 50, 700, 500);//设置窗口大小
    this.setLayout(null);//更改layout 以便添加组件
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭窗口的状态
    this.setResizable(false);//窗口不可以改变大小
    this.add(snakeGrid);
    this.setVisible(true);//设置焦点状态为true
    }
    public static void main(String[] args) {
    new SnakeGame();
    }
    
    }

    运行样式

    • 设置背景图片 背景音乐
      在SnakeGrid.java中增加Music方法 设置画笔 绘图
    package Sanke;
    
    import java.applet.Applet;
    import java.applet.AudioClip;
    import java.awt.*;
    import java.io.File;
    import java.net.MalformedURLException;
    import java.net.URI;
    import java.net.URL;
    import javax.swing.ImageIcon;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    
    /**
    * @author Swyee
    **/
    public class SnakeGrid extends JPanel {
    ImageIcon image = new ImageIcon("Snake/sky.jpg");//图片文件地址
    File f= new File("Snake/music.wav");//音乐文件地址
    SnakeGrid(){
    this.setBounds(0, 0, 700, 400);
    this.setBackground(Color.black);
    }
    
    /**
    * 设置画笔
    * @param g
    */
    @Override
    public void paint(Graphics g) {
    super.paint(g);
    image.paintIcon(this, g, 0, 0); //设置背景图片
    }
    //读取音乐文件
    void Music(){
    try {
    URI uri = f.toURI();
    URL url = uri.toURL();
    AudioClip aau= Applet.newAudioClip(url);
    aau.loop();
    } catch (MalformedURLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    }

    在SnakeName中调用

    package Sanke;
    
    import javax.swing.*;
    
    /**
    * @author Swyee
    **/
    public class SnakeGame extends JFrame {
    SnakeGrid snakeGrid= new SnakeGrid();
    SnakeGame(){
    this.setBounds(100, 50, 700, 500);//设置窗口大小
    this.setLayout(null);//更改layout 以便添加组件
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭窗口的状态
    this.setResizable(false);//窗口不可以改变大小
    this.add(snakeGrid);
    //设置焦点
    snakeGrid.setFocusable(true);
    snakeGrid.requestFocus();
    snakeGrid.Music();//调用打开音乐的方法
    this.setVisible(true);//设置焦点状态为true
    }
    public static void main(String[] args) {
    new SnakeGame();
    }
    
    }

    呈现

    3.23 画蛇

    蛇的身体将会有双向链表组成,双向链表能记录一个节点的上一个节点和下一个节点。蛇的移动其实就是节点的变化,从而达到一种移动的视觉。

    • 新建java Snake 创建节点
    package Sanke;
    
    import java.awt.Graphics;
    
    public class Snake {
    public static final int span=20;//间距
    public static final String up="u";
    public static final String down="d";
    public static final String left="l";
    public static final String right="r";
    class Node{
    int row;
    int col;
    String dir;//方向
    Node next;
    Node pre;
    Node(int row,int col,String dir){
    this.row = row;
    this.col = col;
    this.dir = dir;
    }
    public void draw(Graphics g) {
    g.fillOval(col*span, row*span, span,span);
    
    }
    
    }
    
    }
    • 画蛇
      在snake里面增加draw()方法
    /*
    把蛇画出来
    */
    public void draw(Graphics g) {
    g.setColor(Color.yellow);
    for(Node n=head;n!=null;n=n.next){
    n.draw(g);
    g.setColor(Color.green);
    }
    
    }

    在SnakeGrid.java中创建蛇

    Snake snake = new Snake();//创建蛇

    并在paint中调用snake.draw(g);

    /**
    * 设置画笔
    * @param g
    */
    @Override
    public void paint(Graphics g) {
    super.paint(g);
    image.paintIcon(this, g, 0, 0); //设置背景图片
    snake.draw(g);
    }
    • 控制蛇的移动
      在snake中增加键盘调用的方法:
    /*
    调用键盘的上下左右键
    head.dir记录现在操作的是什么按钮,从而更改蛇的状态
    向上移送时,下键失效,其他四个方向也是如此判断
    */
    public void keyboard(KeyEvent e) {
    switch(e.getKeyCode()){
    case KeyEvent.VK_UP:
    if(head.dir.equals(down)){
    break;
    }
    head.dir=up;
    break;
    case KeyEvent.VK_DOWN:
    if(head.dir.equals(up)){
    break;
    }
    head.dir=down;
    break;
    case KeyEvent.VK_LEFT:
    if(head.dir.equals(right)){
    break;
    }
    head.dir=left;
    break;
    case KeyEvent.VK_RIGHT:
    if(head.dir.equals(left)){
    break;
    }
    head.dir=right;
    break;
    default:
    break;
    }
    }

    增加头部的方法

    /*
    增加头部
    不管移动哪个方向都是在相应位置增加一个节点
    */
    public void addHead(){
    Node node = null;
    switch (head.dir){
    case "l":
    node = new Node(head.row,head.col-1,head.dir);
    break;
    case "r":
    node = new Node(head.row,head.col+1,head.dir);
    break;
    case "u":
    node = new Node(head.row-1,head.col,head.dir);
    break;
    case "d":
    node = new Node(head.row+1,head.col,head.dir);
    break;
    default:
    break;
    }
    node.next=head;
    head.pre=node;
    head=node;
    
    }

    删除尾部的方法

    /*
    删除尾部 删除最后一个节点
    */
    public void deleteTail(){
    tail.pre.next=null;
    tail=tail.pre;
    }

    增加move的方法

    /*
    增加move方法 一增一减,实现蛇的移动
    */
    public void move() {
    addHead();
    deleteTail();
    }

    在SnakeGrid中创建一个线程类,用来执行蛇的移动方法

    class SnakeThread extends Thread{
    @Override
    public void run() {
    while (true){
    try {
    Thread.sleep(300);//沉睡300ms 用来控制蛇的移动速度
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    repaint();//每次沉睡完之后都执行一下repaint()方法,重新绘画
    }
    }

    print方法中调用remove 在SnakeGrid()创建键盘监听事件:

    package Sanke;
    
    import java.applet.Applet;
    import java.applet.AudioClip;
    import java.awt.*;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    import java.io.File;
    import java.net.MalformedURLException;
    import java.net.URI;
    import java.net.URL;
    import javax.swing.ImageIcon;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    
    /**
    * @author Swyee
    **/
    public class SnakeGrid extends JPanel {
    Snake snake = new Snake();//创建蛇ImageIcon image = new ImageIcon("Snake/sky.jpg");//图片文件地址
    File f= new File("Snake/music.wav");//音乐文件地址
    SnakeThread snakeThread = new SnakeThread();
    SnakeGrid(){
    this.setBounds(0, 0, 700, 400);
    this.setBackground(Color.black);
    snakeThread.start();
    this.addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
    snake.keyboard(e);
    }
    });
    }
    
    /**
    * 设置画笔
    * @param g
    */
    @Override
    public void paint(Graphics g) {
    super.paint(g);
    image.paintIcon(this, g, 0, 0); //设置背景图片
    snake.move();//蛇移动
    snake.draw(g);
    }
    //读取音乐文件
    void Music(){
    try {
    URI uri = f.toURI();
    URL url = uri.toURL();
    AudioClip aau= Applet.newAudioClip(url);
    aau.loop();
    } catch (MalformedURLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    class SnakeThread extends Thread{
    @Override
    public void run() {
    while (true){
    try {
    Thread.sleep(300);//沉睡300ms 用来控制蛇的移动速度
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    repaint();//每次沉睡完之后都执行一下repaint()方法,重新绘画
    }
    }}
    
    }
    
    

    执行main方法可以看到可以通过键盘进行控制移动了

    3.24创建蛇的食物

    增加食物的实例 以及画食物的方法 反映食物坐标的方法 新建Food.java

    package Sanke;
    
    import java.awt.*;
    
    public class Food {
    int row;
    int col;
    
    Food(){
    row = 10;//创建食物的大小
    col  =10;
    }
    public void repearShow(){
    row = (int)(Math.random()*18);//生成随机数 乘以食物的大小可以得到坐标
    col = (int)(Math.random()*32);
    }
    public void draw(Graphics g) {//把食物画出来
    g.setColor(Color.red);
    g.fillRect(col*20, row*20, 20, 20);//表示坐标
    
    }
    public Rectangle getCoordinates(){
    return new Rectangle(col*20,row*20,20,20);//获得食物的坐标
    
    }
    
    }

    修改Snake.java 增加判断蛇头位置的方法,修改午无参构造方法,改为有参构造,把food添加进来 修改move方法

    package Sanke;
    
    import java.awt.*;
    import java.awt.event.KeyEvent;
    
    /**
    * @author Swyee
    */
    public class Snake {
    public static final int span=20;//间距
    public static final String up="u";
    public static final String down="d";
    public static final String left="l";
    public static final String right="r";
    Node body;//蛇的身体
    Node head;//蛇的头部
    Node tail;//蛇的头部
    Food food;
    Snake(Food food){
    body = new Node(5,20,left);
    head = body;
    tail = body;
    this.food=food;
    }
    class Node{
    int row;
    int col;
    String dir;//方向
    Node next;
    Node pre;
    Node(int row,int col,String dir){
    this.row = row;
    this.col = col;
    this.dir = dir;
    }
    public void draw(Graphics g) {
    g.fillOval(col*span, row*span, span,span);//坐标
    
    }
    }
    /*
    把蛇画出来
    */
    public void draw(Graphics g) {
    g.setColor(Color.yellow);
    for(Node n=head;n!=null;n=n.next){
    n.draw(g);
    g.setColor(Color.green);
    }
    
    }/*
    调用键盘的上下左右键
    head.dir记录现在操作的是什么按钮,从而更改蛇的状态
    向上移送时,下键失效,其他四个方向也是如此判断
    */
    public void keyboard(KeyEvent e) {
    switch(e.getKeyCode()){
    case KeyEvent.VK_UP:
    if(head.dir.equals(down)){
    break;
    }
    head.dir=up;
    break;
    case KeyEvent.VK_DOWN:
    if(head.dir.equals(up)){
    break;
    }
    head.dir=down;
    break;
    case KeyEvent.VK_LEFT:
    if(head.dir.equals(right)){
    break;
    }
    head.dir=left;
    break;
    case KeyEvent.VK_RIGHT:
    if(head.dir.equals(left)){
    break;
    }
    head.dir=right;
    break;
    default:
    break;
    }
    }/*
    增加头部
    */
    public void addHead(){
    Node node = null;
    switch (head.dir){
    case "l":
    node = new Node(head.row,head.col-1,head.dir);
    break;
    case "r":
    node = new Node(head.row,head.col+1,head.dir);
    break;
    case "u":
    node = new Node(head.row-1,head.col,head.dir);
    break;
    case "d":
    node = new Node(head.row+1,head.col,head.dir);
    break;
    default:
    break;
    }
    node.next=head;
    head.pre=node;
    head=node;
    
    }
    /*
    删除尾部
    */
    public void deleteTail(){
    tail.pre.next=null;
    tail=tail.pre;
    }
    /*
    增加move方法
    */
    public void move() {
    addHead();
    if(this.getSnakeRectangle().intersects(food.getCoordinates())){//当蛇头与食物重合的时候 蛇吃食物 食物刷新,不再删除尾巴,达到一种蛇增长的要求
    
    food.repearShow();
    }else{
    deleteTail();
    }
    }
    public Rectangle getSnakeRectangle(){//获取蛇头的坐标
    
    return new Rectangle(head.col*span,head.row*span,span,span);
    
    }
    }
    
    

    在修改snakegrid.java 贪吃蛇的功能就基本实现了

    Food food = new Food();
    Snake snake = new Snake(food);//创建蛇
    ImageIcon image = new ImageIcon("Snake/sky.jpg");//图片文件地址
    File f= new File("Snake/music.wav");//音乐文件地址
    SnakeThread snakeThread = new SnakeThread();
    @Override
    public void paint(Graphics g) {
    super.paint(g);
    image.paintIcon(this, g, 0, 0); //设置背景图片
    snake.move();//蛇移动
    snake.draw(g);
    food.draw(g);
    }
    3.2.5增加蛇的存活状态

    在Snake中增加蛇的存活状态,每一次移动都判断下是否存活,修改SnakeGrid的线程,执行时进行判断是否存活

    public void DeadOrLive(){//超出边框范围 蛇头撞到身体 游戏结束
    if(head.row<0 || head.row>rows-1 || head.col<0 ||head.col>cols){
    islive=false;
    }
    for(Node n=head.next;n!=null;n=n.next){
    if(n.col==head.col && n.row==head.row){
    islive=false;
    }
    }
    
    }
    public void move() {
    addHead();
    if(this.getSnakeRectangle().intersects(food.getCoordinates())){//当蛇头与食物重合的时候 蛇吃食物 食物刷新,不再删除尾巴,达到一种蛇增长的要求
    
    food.repearShow();
    }else{
    deleteTail();
    }
    DeadOrLive();//每移动一步都要判断一下是否存活
    }
    class SnakeThread extends Thread{
    boolean flag = true;
    @Override
    public void run() {
    while (Snake.islive && flag) {
    try {
    Thread.sleep(300);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    if (Snake.islive ) {
    repaint();
    }
    
    }
    if (!flag == false) {
    JOptionPane.showMessageDialog(SnakeGrid.this, "游戏结束");
    }
    }
    3.2.6 增加按钮
    • 最后的时候,给这个小游戏增加几个按钮,用来实现暂停开始
      新建Button.java
    package Sanke;
    
    import javax.swing.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    public class Button extends JPanel{
    public static boolean isMove=true;//表示运行状态
    SnakeGrid snakeGrid;
    Button(SnakeGrid snakeGrid){
    this.snakeGrid=snakeGrid;
    this.setBounds(0, 400, 700, 100);
    JButton jb1 = new JButton("暂停游戏");
    JButton jb2 = new JButton("继续游戏");
    JButton jb3 = new JButton("重新游戏");
    this.add(jb1);
    this.add(jb2);
    this.add(jb3);
    jb1.addActionListener(new ActionListener() {
    
    @Override
    public void actionPerformed(ActionEvent e) {
    isMove=false;
    
    }
    });
    jb2.addActionListener(new ActionListener() {
    
    @Override
    public void actionPerformed(ActionEvent e) {
    isMove=true;
    snakeGrid.setFocusable(true);
    snakeGrid.requestFocus();
    }
    });
    jb3.addActionListener(new ActionListener() {
    
    @Override
    public void actionPerformed(ActionEvent e) {//重新创建蛇等 重新开始游戏
    snakeGrid.snakeThread.stopThread();
    
    Food food = new Food();
    snakeGrid.food=food;
    snakeGrid.snake=new Snake(food);
    Snake.islive=true;
    isMove=true;
    SnakeGrid.SnakeThread st = snakeGrid.new SnakeThread();
    snakeGrid.snakeThread=st;
    st.start();
    
    snakeGrid.setFocusable(true);
    snakeGrid.requestFocus();
    }
    });
    
    }
    }

    再修改SnakeGrid中的thread

    package Sanke;
    
    import java.applet.Applet;
    import java.applet.AudioClip;
    import java.awt.*;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    import java.io.File;
    import java.net.MalformedURLException;
    import java.net.URI;
    import java.net.URL;
    import javax.swing.ImageIcon;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    
    /**
    * @author Swyee
    **/
    public class SnakeGrid extends JPanel {
    Food food = new Food();
    Snake snake = new Snake(food);//创建蛇
    ImageIcon image = new ImageIcon("Snake/sky.jpg");//图片文件地址
    File f= new File("Snake/music.wav");//音乐文件地址
    SnakeThread snakeThread = new SnakeThread();SnakeGrid(){
    this.setBounds(0, 0, 700, 400);
    this.setBackground(Color.black);
    snakeThread.start();
    this.addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
    snake.keyboard(e);
    }
    });
    }
    
    /**
    * 设置画笔
    * @param g
    */
    @Override
    public void paint(Graphics g) {
    super.paint(g);
    image.paintIcon(this, g, 0, 0); //设置背景图片
    snake.move();//蛇移动
    snake.draw(g);
    food.draw(g);
    }
    //读取音乐文件
    void Music(){
    try {
    URI uri = f.toURI();
    URL url = uri.toURL();
    AudioClip aau= Applet.newAudioClip(url);
    aau.loop();
    } catch (MalformedURLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    class SnakeThread extends Thread{
    boolean flag = true;
    @Override
    public void run() {
    while(Snake.islive &&flag){
    try {
    Thread.sleep(300);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    if(Snake.islive&& Button.isMove){
    repaint();
    }
    
    }
    if(!flag==false){
    JOptionPane.showMessageDialog(SnakeGrid.this, "游戏结束");
    }
    
    }
    public void stopThread(){
    flag=false;
    }
    }
    }
    
    

    在主页面中把按钮添加上去

    package Sanke;
    
    import javax.swing.*;
    
    /**
    * @author Swyee
    **/
    public class SnakeGame extends JFrame {
    SnakeGrid snakeGrid= new SnakeGrid();
    Button button = new Button(snakeGrid);
    SnakeGame(){
    this.setBounds(100, 50, 700, 500);//设置窗口大小
    this.setLayout(null);//更改layout 以便添加组件
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭窗口的状态
    this.setResizable(false);//窗口不可以改变大小
    this.add(snakeGrid);
    this.add(button);
    //设置焦点
    snakeGrid.setFocusable(true);
    snakeGrid.requestFocus();
    snakeGrid.Music();//调用打开音乐的方法
    this.setVisible(true);//设置焦点状态为true
    }
    public static void main(String[] args) {
    new SnakeGame();
    }
    
    }


    到这里这个小游戏就全部做完了,当然也可以在其基础上增加其他功能

    也可以把这个小游戏打成jar包的形式进行运行
    ,将打好的jar包和资源文件放在同一个目录下,即可正常运行访问

    四 打jar包

    idea打jar包方式:
    https://blog.csdn.net/qq_44433261/article/details/107433540
    命令行运行jar包方式:
    https://blog.csdn.net/qq_44433261/article/details/107433355

    源码

    最后附上源码链接:
    链接: https://pan.baidu.com/s/1iUmSUnvpi_YKUNsPs3ugIQ 提取码: zxsk

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: