贪吃蛇项目开发笔录

文章目录


(结尾有成果演示)
(结尾有成果演示)
(结尾有成果演示)

贪吃蛇(结尾有成果演示)

1.项目介绍

能写出简单的界面,实现键盘蛇的显现,移动与吃食物的可视化。并加入时间,暂停,碰撞判定等设置。

2.项目分步

1.写出简单的可视化界面(包括棋盘信息,静止的蛇);
2.让蛇化静为动,包括蛇的移动设置和方向,定时器的刷新界面设置与监听设置;
3.写出在棋盘上随机生成的食物,并实现蛇吃食物的过程;
4.实现蛇与自身本体的碰撞与和墙壁的碰撞结果。

3.代码实现

(1).可视化界面的实现

<1>.可视化边框的初始化函数(GamePanel类)
添加属性画板(包括set和get方法,此处省略)
	//面板
    private JPanel jp;

添加边框初始化方法
    private void initFrame(){
        //设置窗体的大小
        setSize(800,800);
        //设置窗体的位置
        setLocation(400,20);
        //设置关闭按钮的作用(退出程序)
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗体大小的不可变性
        setResizable(false);
        //设置窗体的标题
        setTitle("SnakeGame from A Active Boy");
    }
<2>.面板内容的初始化
	//初始化棋盘方法(包括棋盘)
    public void initPanel(){
        jp = new JPanel(){
            @Override
            public void paint(Graphics g) {
                //画横竖交叉的棋盘
                for (int i = 0;i < 40;i++){
                    g.drawLine(i*20,0,i*20,800);
                    g.drawLine(0,i*20,800,i*20);
                }
            }
        };
        add(jp);
    }
<3>.将初始化加入GamePanel的构造方法中
 	//构造方法
    public GamePanel() {
        //初始化面板
        initFrame();

        //初始化棋盘
        initPanel();
    }
<4>.主函数调用函数(StartGame类)
public static void main(String[] args) {
        //初始化对象的创建(目的是执行GamePanel类的构造方法)
        new GamePanel().setVisible();
    }
<5>.基础可视化实现效果

在这里插入图片描述

<6>.绘制一条静止的蛇
在面板类中加入蛇属性
	//蛇
    private Snake snake;
在构造方法中添加初始化蛇的方法
	//构造方法
    public GamePanel() throws HeadlessException{
        //初始化面板
        initFrame();
        //初始化棋盘
        initPanel();
        //初始化蛇
        initSnake();
    }
初始化蛇的方法
	//初始化一条蛇的方法
    public void initSnake(){
        snake = new Snake();
    }
在画板上画蛇
	//初始化棋盘方法(包括棋盘和静态蛇)
    public void initPanel(){
        jp = new JPanel(){
            @Override
            public void paint(Graphics g) {
            	//清空界面
            	g.clearRect(0,0,800,800);
                //画横竖交叉的棋盘
                for (int i = 0;i < 40;i++){
                    g.drawLine(i*20,0,i*20,800);
                    g.drawLine(0,i*20,800,i*20);
                }
                //绘制一条静态的蛇
                //获得蛇身体的集合
                LinkedList<Node> body = snake.getBody();
                //遍历蛇身体集合,给每个元素填充即可
                for (Node node:body){
                    g.fillRect(node.getX()*20,node.getY()*20,20,20);
                }
            }
        };
        add(jp);
    }
<7>.主函数测试
public static void main(String[] args) {
        //初始化对象的创建(目的是执行GamePanel类的构造方法)
        new GamePanel().setVisible();
    }
<8>.测试效果图

在这里插入图片描述

(2).让蛇化静为动的实现

<1>.蛇的移动
创建一个枚举类型存储蛇的移动方向
//蛇的方向
public enum Direction {
    UP,DOWN,LEFT,RIGHT;
}
在蛇类中添加方向属性
	//蛇移动的方向(枚举)
    private Direction direction;
在蛇类的构造方法中初始化蛇的方向
	//构造方法
    public Snake(){

        //初始化身体的结点集合
        body = new LinkedList <>();
        //初始化蛇的头和身体结点
        for (int i = 19;i > 15;i--){
            body.addFirst(new Node(i,20));
        }
        //初始化蛇的方向
        direction = Direction.LEFT;
    }
蛇的移动方法实现
public void move(){
        //获取蛇头结点
        Node head = body.getFirst();
        switch (direction){
            case UP :
                body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()-1));
                break;
            case DOWN :
                body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()+1));
                break;
            case LEFT :
                body.addFirst(new Node(body.getFirst().getX()-1,body.getFirst().getY()));
                break;
            case RIGHT :
                body.addFirst(new Node(body.getFirst().getX()+1,body.getFirst().getY()));
        }
        //删除尾结点
        body.removeLast();

        //重新获取头结点
        head = body.getFirst();
    }
<2>.定时器和定时器任务的引入实现
添加定时器的属性(GamePanel类)
	//定时器
    private Timer timer;
定时器的初始化方法实现
private void initTimer() {
        //创建定时器对象
        timer = new Timer();
        //创建定时器任务对象
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                //蛇移动
                snake.move();
                //重绘棋盘
                jp.repaint();
            }
        };
        //每100ms执行一次定时任务
        timer.scheduleAtFixedRate(timerTask,5,100);
    }
将初始化定时器方法加入构造方法
	//构造方法
    public GamePanel() throws HeadlessException{
        //初始化面板
        initFrame();
        //初始化棋盘
        initPanel();
        //初始化蛇
        initSnake();
        //初始化定时器
        initTimer();
    }
<3>设置键盘的监听
实现键盘的监听功能方法
	//设置键盘监听的方法
    private void setKeyListener() {
        addKeyListener(new KeyAdapter() {
            //键盘按下时自动执行以下方法
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()){
                    case KeyEvent.VK_UP:
                        if (snake.getDirection()!= Direction.DOWN){
                            snake.setDirection(Direction.UP);
                        }
                        break;
                    case KeyEvent.VK_DOWN:
                        if (snake.getDirection()!= Direction.UP){
                            snake.setDirection(Direction.DOWN);
                        }
                        break;
                    case KeyEvent.VK_LEFT:
                        if (snake.getDirection()!= Direction.RIGHT){
                            snake.setDirection(Direction.LEFT);
                        }
                        break;
                    case KeyEvent.VK_RIGHT:
                        if (snake.getDirection()!= Direction.LEFT){
                            snake.setDirection(Direction.RIGHT);
                        }
                        break;
                }
            }
        });
    }
将监听加入构造方法
	//构造方法
    public GamePanel() throws HeadlessException{
        //初始化面板
        initFrame();
        //初始化棋盘
        initPanel();
        //初始化蛇
        initSnake();
        //初始化定时器
        initTimer();
        //设置键盘的监听
        setKeyListener();
    }

(3).食物功能的实现

<1>.食物结点的显现
添加食物属性(GamePanel类)
	//食物
    private Node food;
在结点类中加入生成随机数方法(Node类)
	//随机生成位置的方法
    public void random(){
        //创建随机类对象
        Random r = new Random();
        //随机生成横坐标
        this.x = r.nextInt(40);
        //随机生成纵坐标
        this.y = r.nextInt(40);
    }
食物的位置初始化方法
	//食物的初始化
    private void initFood(){
        //新建一个食物结点对象
        food = new Node();
        //得到随机食物的位置
        food.random();
    }
在构造方法中添加食物初始化
	//构造方法
    public GamePanel() throws HeadlessException{
        //初始化面板
        initFrame();
        //初始化棋盘
        initPanel();
        //初始化蛇
        initSnake();
        //初始化定时器
        initTimer();
        //设置键盘的监听
        setKeyListener();
        //食物的初始化方法
        initFood();
    }
食物的绘制(GamePanel类)
	//初始化棋盘方法(包括棋盘和静态蛇和食物)
    public void initPanel(){
        jp = new JPanel(){
            @Override
            public void paint(Graphics g) {
                //清空棋盘
                g.clearRect(0,0,800,800);
                //画横竖交叉的棋盘
                for (int i = 0;i < 40;i++){
                    g.drawLine(i*20,0,i*20,800);
                    g.drawLine(0,i*20,800,i*20);
                }
                //绘制一条静态的蛇
                //获得蛇身体的集合
                LinkedList<Node> body = snake.getBody();
                //遍历蛇身体集合,给每个元素填充即可
                for (Node node:body){
                    g.fillRect(node.getX()*20,node.getY()*20,20,20);
                }
                
                //绘制食物
                g.fillRect(food.getX()*20,food.getY()*20,20,20);
            }
        };
        add(jp);
    }
结果测试

在这里插入图片描述

<2>.蛇吃食物的动作实现
蛇吃食物的方法实现
	//蛇吃食物的方法
    public void eat(Node food){
        switch (direction){
            case UP:
                body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()-1));
                break;
            case DOWN:
                body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()+1));
                break;
            case LEFT:
                body.addFirst(new Node(body.getFirst().getX()-1,body.getFirst().getY()));
                break;
            case RIGHT:
                body.addFirst(new Node(body.getFirst().getX()+1,body.getFirst().getY()));
                break;
        }
    }
定时器任务中加入蛇吃食物的判定
	//初始化定时器的方法
    private void initTimer() {
        //创建定时器对象
        timer = new Timer();
        //创建定时器任务对象
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                //蛇移动
                snake.move();
                
                //判断蛇移动后是否吃到食物
                if (snake.getBody().getFirst().getX()== food.getX()&&snake.getBody().getFirst().getY() == food.getY()){
                    //调用蛇的吃食物方法
                    snake.eat(food);
                    //随机生成一枚食物
                    food.random();
                }
                
                //重绘棋盘
                jp.repaint();
            }
        };
        //每100ms执行一次定时任务
        //一定要有延迟!不然会报食物初始化失败的空指针异常。
        timer.scheduleAtFixedRate(timerTask,5,100);
    }

(4).蛇与本体和墙壁的碰撞判定

<1>.蛇的死亡状态添加
在蛇的属性栏添加生死状态(Snake类)
	//蛇的生死状态
    private boolean isLive;
在蛇的移动方法中加入生死判定
		 //移动的方法
    //蛇的移动仅需要改变蛇的头结点和尾结点即可
    public void move(){
        if (isLive) {
            //获取蛇头结点
            Node head = body.getFirst();
            switch (direction) {
                case UP:
                    body.addFirst(new Node(head.getX(), head.getY() - 1));
                    break;
                case DOWN:
                    body.addFirst(new Node(head.getX(), head.getY() + 1));
                    break;
                case LEFT:
                    body.addFirst(new Node(head.getX() - 1, head.getY()));
                    break;
                case RIGHT:
                    body.addFirst(new Node(head.getX() + 1, head.getY()));
                    break;
            }
            //删除尾结点
            body.removeLast();
        }
在蛇的移动方法中加入撞墙事件
	//重新获取头结点
        Node head = body.getFirst();

        //判断撞墙事件
        if (head.getX()<=0||head.getX()>=40||head.getY()<=0||head.getY()>=40){
            isLive = false;
        }
在蛇的移动中加入追尾事件
	//判断追尾事件
        for (int i = 1;i < body.size();i++) {
            if (head.getX()==body.get(i).getX()&&head.getY()==body.get(i).getY()){
                //System.out.println(body.get(i).getX());
                isLive = false;
            }
        }

(5).完整代码展示(共五个类)

<1>.StartGame类:
//主类,包含主方法,启动游戏类类(.exe)
public class StartGame {
    public static void main(String[] args) {
        new GamePanel().setVisible(true);
    }
}
<2>.GamePanel类:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;


public class GamePanel extends JFrame {
    //蛇
    private Snake snake;
    //面板
    private JPanel jp;
    //定时器
    private Timer timer;
    //食物
    private Node food;

    //构造方法
    public GamePanel() throws HeadlessException{
        //初始化面板
        initFrame();
        //初始化棋盘
        initPanel();
        //初始化蛇
        initSnake();
        //初始化定时器
        initTimer();
        //设置键盘的监听
        setKeyListener();
        //食物的初始化方法
        initFood();
    }

    //边框的初始化
    //初始化窗体的参数方法
    private void initFrame(){
        //设置窗体的大小
        setSize(800,800);
        //设置窗体的位置
        setLocation(400,20);
        //设置关闭按钮的作用(退出程序)
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗体大小的不可变性
        setResizable(false);
        //设置窗体的标题
        setTitle("SnakeGame from A Active Boy");
    }

    //初始化棋盘方法(包括棋盘和静态蛇和食物)
    public void initPanel(){
        jp = new JPanel(){
            @Override
            public void paint(Graphics g) {
                //清空棋盘
                g.clearRect(0,0,800,800);
                //画横竖交叉的棋盘
                for (int i = 0;i < 40;i++){
                    g.drawLine(i*20,0,i*20,800);
                    g.drawLine(0,i*20,800,i*20);
                }
                //绘制一条静态的蛇
                //获得蛇身体的集合
                LinkedList<Node> body = snake.getBody();
                //遍历蛇身体集合,给每个元素填充即可
                for (Node node:body){
                    g.fillRect(node.getX()*20,node.getY()*20,20,20);
                }

                //绘制食物
                g.fillRect(food.getX()*20,food.getY()*20,20,20);
            }
        };
        add(jp);
    }
    //初始化一条蛇的方法
    public void initSnake(){
        snake = new Snake();
    }

    //初始化定时器的方法

    //初始化定时器的方法
    private void initTimer() {
        //创建定时器对象
        timer = new Timer();
        //创建定时器任务对象
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                //蛇移动
                snake.move();

                //蛇吃食物
                Node head = snake.getBody().getFirst();
                if (head.getX()==food.getX()&&head.getY()==food.getY()){
                    snake.eat(food);
                    food.random();
                }

                //重绘棋盘
                jp.repaint();
            }
        };
        //每100ms执行一次定时任务
        timer.scheduleAtFixedRate(timerTask,5,100);
    }

    //设置键盘监听的方法
    private void setKeyListener() {
        addKeyListener(new KeyAdapter() {
            //键盘按下时自动执行以下方法
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()){
                    case KeyEvent.VK_UP:
                        if (snake.getDirection()!= Direction.DOWN){
                            snake.setDirection(Direction.UP);
                        }
                        break;
                    case KeyEvent.VK_DOWN:
                        if (snake.getDirection()!= Direction.UP){
                            snake.setDirection(Direction.DOWN);
                        }
                        break;
                    case KeyEvent.VK_LEFT:
                        if (snake.getDirection()!= Direction.RIGHT){
                            snake.setDirection(Direction.LEFT);
                        }
                        break;
                    case KeyEvent.VK_RIGHT:
                        if (snake.getDirection()!= Direction.LEFT){
                            snake.setDirection(Direction.RIGHT);
                        }
                        break;
                }
            }
        });
    }
    //食物的初始化
    private void initFood(){
        //新建一个食物结点对象
        food = new Node();
        //得到随机食物的位置
        food.random();
    }
}
<3>.Snake类:
import java.util.LinkedList;

public class Snake {
    //蛇的身体与头用一个链表集合储存
    private LinkedList <Node> body;
    //蛇移动的方向(枚举)
    private Direction direction;
    //蛇的生死状态
    private boolean isLive;

    //构造方法
    public Snake(){

        //初始化身体的结点集合
        body = new LinkedList <>();
        //初始化蛇的头和身体结点
        for (int i = 19;i > 15;i--){
            body.addFirst(new Node(i,20));
        }
        //初始化蛇的方向
        direction = Direction.LEFT;
        //初始化蛇的生死
        isLive = true;
    }

    //移动的方法
    //蛇的移动仅需要改变蛇的头结点和尾结点即可
    public void move(){
        if (isLive) {
            //获取蛇头结点
            Node head = body.getFirst();
            switch (direction) {
                case UP:
                    body.addFirst(new Node(head.getX(), head.getY() - 1));
                    break;
                case DOWN:
                    body.addFirst(new Node(head.getX(), head.getY() + 1));
                    break;
                case LEFT:
                    body.addFirst(new Node(head.getX() - 1, head.getY()));
                    break;
                case RIGHT:
                    body.addFirst(new Node(head.getX() + 1, head.getY()));
                    break;
            }
            //删除尾结点
            body.removeLast();
        }
        //重新获取头结点
        Node head = body.getFirst();

        //判断撞墙事件
        if (head.getX()<0||head.getX()>=40||head.getY()<0||head.getY()>=40){
            isLive = false;
        }
        
		//判断追尾事件
		for (int i = 2;i < body.size();i++) {
            if (head.getX()==body.get(i).getX()&&head.getY()==body.get(i).getY()){
                //System.out.println(body.get(i).getX());
                isLive = false;
            }
        }
    }




    //蛇吃食物的方法
    public void eat(Node food){
        switch (direction){
            case UP:
                body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()-1));
                break;
            case DOWN:
                body.addFirst(new Node(body.getFirst().getX(),body.getFirst().getY()+1));
                break;
            case LEFT:
                body.addFirst(new Node(body.getFirst().getX()-1,body.getFirst().getY()));
                break;
            case RIGHT:
                body.addFirst(new Node(body.getFirst().getX()+1,body.getFirst().getY()));
                break;
        }
    }
    //set方法和get方法

    public void setBody(LinkedList <Node> body) {
        this.body = body;
    }

    public LinkedList <Node> getBody() {
        return body;
    }

    public Direction getDirection() {
        return direction;
    }

    public void setDirection(Direction direction) {
        this.direction = direction;
    }
}
<4>.Diraction类(枚举):
//蛇的方向
public enum Direction {
    UP,DOWN,LEFT,RIGHT;
}
<5>.Node类:
import java.util.Random;

public class Node {
    //结点的横坐标
    private int x;
    //结点的纵坐标
    private int y;

    //构造方法
    public Node(){

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

    //随机生成位置的方法
    public void random(){
        //创建随机类对象
        Random r = new Random();
        //随机生成横坐标
        this.x = r.nextInt(40);
        //随机生成纵坐标
        this.y = r.nextInt(40);
    }

    //Set和Get方法

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

    public int getX() {
        return x;
    }

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

    public int getY() {
        return y;
    }
}

(6).效果展示:

贪吃蛇项目成果演示

4.拓展内容

<1>.空格键控制游戏的开始与否,并可用于复活死亡的蛇
向蛇的属性栏里添加游戏是否开始变量
	//游戏的暂停状态
    public boolean isStart;
向构造方法里添加游戏是否开始的初始化
	//构造方法
    public Snake(){

        //初始化身体的结点集合
        body = new LinkedList <>();
        //初始化蛇的头和身体结点
        for (int i = 19;i > 15;i--){
            body.addFirst(new Node(i,20));
            //System.out.println(body.get(0).getX());
        }
        //初始化蛇的方向
        direction = Direction.LEFT;
        //初始化蛇的生死
        isLive = true;
        //游戏是否开始
        isStart = false;
    }
在蛇的移动代码里添加判定条件
public void move(){
        if (isLive&&isStart) {
        	(代码省略)
        }
}        
加入键盘空格键的监听
//设置键盘监听的方法
    private void setKeyListener() {
        addKeyListener(new KeyAdapter() {
            //键盘按下时自动执行以下方法
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()) {
                    (此处代码省略)
                    case KeyEvent.VK_SPACE:
                        //如果蛇死了,复活
                        //如果蛇活着,暂停
                        if (!snake.isLive){
                            initSnake();
                            initFood();
                            snake.isLive = true;
                            snake.isStart = true;
                        }else {
                            snake.isStart = !snake.isStart;
                            if (snake.isStart){
                                System.out.println("游戏已开始!");
                            }else{
                                System.out.println("游戏已暂停!");
                            }
                        }
                        break;
                }
            }
        });
<2>.加入计分功能
向蛇的属性中加入得分
	//分数
    public int score;
向蛇的构造方法中添加分数的初始化
	//构造方法
    public Snake(){

        //初始化身体的结点集合
        body = new LinkedList <>();
        //初始化蛇的头和身体结点
        for (int i = 19;i > 15;i--){
            body.addFirst(new Node(i,20));
            //System.out.println(body.get(0).getX());
        }
        //初始化蛇的方向
        direction = Direction.LEFT;
        //初始化蛇的生死
        isLive = true;
        //游戏是否开始
        isStart = false;
        //初始化分数
        score = 0;
    }
在蛇的移动方法中添加死亡打印分数语句
	//移动的方法
    //蛇的移动仅需要改变蛇的头结点和尾结点即可
    public void move(){
        if (isLive&&isStart) {
           (代码省略)
        }
        //死亡时打印分数
        if (!isLive){
            //打印分数
            System.out.println("游戏结束,你的分数为"+score+"。");
        }
	}
在蛇的吃食物任务中添加加分功能
	//初始化定时器的方法
    private void initTimer() {
        //创建定时器对象
        timer = new Timer();
        //创建定时器任务对象
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                //蛇移动
                snake.move();

                //蛇吃食物
                Node head = snake.getBody().getFirst();
                if (head.getX() == food.getX() && head.getY() == food.getY()) {
                    snake.eat(food);
                    food.random();
                    //分数+1
                    snake.score++;
                }
                //重绘棋盘
                jp.repaint();
            }
        };
        //每100ms执行一次定时任务
            timer.scheduleAtFixedRate(timerTask, 5, 170);
    }

完!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值