贪吃蛇(GUI简单实现)

package Update;
/*
枚举:就是定义几个常量
 */
public enum Direction {
    UP,DOWN,LEFT,RIGHT
}
package Update;
/*
   结点类:
   每一条蛇是由若干个结点组成的链表集合,每一个结点通过横纵坐标来确定位置
   食物是棋盘中单独的、随机的结点组成的集合
    */
public class Node {
    int x;//结点的横坐标
    int y;//结点的纵坐标
    Node next;//指向下一个结点
   Node prev;//指向上一个结点

    public Node() {
        this.next = null;
    }

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

    public Node(int x, int y, Node prev, Node next) {
        this.x = x;
        this.y = y;
        this.prev=prev;
        this.next = next;
    }
}
 
package Update;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
/*
食物的本质是结点的集合
 */
public class Food {
    LinkedList<Node> foodList;
    public Food(){
        initFood();//初始化食物
    }

    private void initFood() {
        foodList=new LinkedList<>();
        Random r=new Random();
        for(int i=0;i<40;i++){//初始化食物链表,有40个食物
            foodList.offer(new Node(r.nextInt(39),r.nextInt(38)));
        }
    }
}
 
package Update;

import java.util.ArrayList;
import java.util.Random;

/*
怪物类:本质上是结点类的集合
 */
public class Monster {
    ArrayList<Node>monsterList=new ArrayList<>();

    public Monster(){
        initMonster();//初始化小怪物集合
    }

    private void initMonster() {//一共有10个小怪物,小怪物的位置持续改变
        for(int i=0;i<10;i++){
            Random r=new Random();
            monsterList.add(new Node(r.nextInt(40),r.nextInt(40)));
        }
    }

    public void changePosition(){//小怪物改变位置
        for(int i=0;i<monsterList.size();i++){
            Node temp=monsterList.get(i);
            Random r=new Random();
            temp.x=r.nextInt(40);
            temp.y=r.nextInt(40);
        }
    }
}
 
package Update;
/*
链表类:这是蛇身的储存结构
 */
public class SnackBody {
    Node head=new Node(666,888,null,null);//不用来储存元素,作为哨兵结点,就不用考虑链表为空的情况

    /*
    add方法:就是在尾部添加结点的方法
     */
    public void add(Node add){
        Node p=findLast();
        p.next=new Node(add.x,add.y,p,null);
    }

    /*
    addFirst方法
     */
    public void addFirst(Node add){
       Node p=new Node(add.x,add.y,head,head.next);
      head.next.prev=p;
      head.next=p;
    }

    /*
    寻找最后一个结点的方法
     */
    public Node findLast() {
        //链表非空
        Node p;
        for(p=head;p.next!=null;p=p.next){}
        return p;
    }

    public Node getFirst(){
        if(head.next!=null) {
            return head.next;
        }else{
             throw new IllegalArgumentException() ;
        }
    }

    public void removeLast(){
        Node last=findLast();
        Node latter=last.prev;
        latter.next=null;
        last.prev=null;
    }

    public int size(){
        int i;
        Node p;
        for(p=head.next,i=0;p!=null;p=p.next,i++){

        }
        return i;
    }

    public Node get(int index){
        int i;
        Node p;
        for(p=head.next,i=0;p!=null;p=p.next,i++){
            if(i==index){
                return p;
            }
        }
        return null;
    }
}
package Update;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;

public class Snack {
    SnackBody snackBody;
    Direction direction = Direction.LEFT;//将蛇头的移动方向默认为左边
    boolean isLiving = true;//蛇是否还活着
    Queue<int[]> path=new LinkedList<>();//队列用于储存贪吃蛇的运动轨迹
    int grade=0;//贪吃蛇的得分
    /*
    存活状态下:贪吃蛇发生了移动过程,就会将此时的横纵坐标储存到路径当中
    一旦贪吃蛇死亡,就把队列中的所有元素倒出
     */

    public Snack() {
        initSnack();//初始化蛇的方法
    }


    //初始化蛇的方法
    private void initSnack() {
        //蛇出生的时候有6个结点
        snackBody = new SnackBody();
        snackBody.add(new Node(16, 20));
        snackBody.add(new Node(17, 20));
        snackBody.add(new Node(18, 20));
        snackBody.add(new Node(19, 20));
        snackBody.add(new Node(20, 20));
        snackBody.add(new Node(21, 20));
    }

    /*
    蛇��进行移动的方法move:
    沿着蛇头的方向移动
    具体实现就是蛇在蛇头移动的方向添加一个新的结点,在蛇的尾部删除一个结点
     */
    public void move() {
        //蛇是活着的可以移动
        if (isLiving) {
            Node head = snackBody.getFirst();//获取蛇头
            switch (direction) {
                case UP: {//蛇头移动方向向上,在蛇头的上面添加结点,在蛇尾删除结点:对snackBody操作
                    snackBody.addFirst(new Node(head.x, head.y - 1));//添加
                    snackBody.removeLast();//删除
                    break;
                }
                case DOWN: {//蛇头移动方向向下,在蛇头的下面添加结点,在蛇尾删除结点:对snackBody操作
                    snackBody.addFirst(new Node(head.x, head.y + 1));//添加
                    snackBody.removeLast();//删除
                    break;
                }
                case LEFT: {//蛇头移动方向向左,在蛇头的左边添加结点,在蛇尾删除结点:对snackBody操作
                    snackBody.addFirst(new Node(head.x - 1, head.y));//添加
                    snackBody.removeLast();//删除
                    break;
                }
                case RIGHT: {//蛇头移动方向向右,在蛇头的右边添加结点,在蛇尾删除结点:对snackBody操作
                    snackBody.addFirst(new Node(head.x + 1, head.y));//添加
                    snackBody.removeLast();//删除
                    break;
                }
            }
            int[]arr={head.x, head.y};
            path.offer(arr);
        }

        Node head = snackBody.getFirst();//获取蛇头
        //判断是否撞墙:x<0撞到左墙壁,x>39撞到右墙壁,y<0撞到上墙壁,y>39撞到下墙壁
        if (head.x <=0 || head.x >= 39 || head.y <=0 || head.y >=39) {
            isLiving = false;//蛇死了,无法移动
        }

        //判断蛇头和身体的其他部分是否发生重合,如果撞到自己的身体也会死
        for (int i = 1; i < snackBody.size(); i++) {
            if (head.x == snackBody.get(i).x && head.y == snackBody.get(i).y) {
                isLiving = false;//蛇死了,无法移动
            }
        }



    }

    public void eat(Node food) {//沿着蛇头的移动方向添加一个结点
        Node head = snackBody.getFirst();//获取蛇头
        switch (direction) {
            case UP: {//蛇头移动方向向上,在蛇头的上面添加结点
                snackBody.addFirst(new Node(head.x, head.y - 1));//添加
                break;
            }
            case DOWN: {//蛇头移动方向向下,在蛇头的下面添加结点
                snackBody.addFirst(new Node(head.x, head.y + 1));//添加
                break;
            }
            case LEFT: {//蛇头移动方向向左,在蛇头的左边添加结点
                snackBody.addFirst(new Node(head.x - 1, head.y));//添加
                break;
            }
            case RIGHT: {//蛇头移动方向向右,在蛇头的右边添加结点
                snackBody.addFirst(new Node(head.x + 1, head.y));//添加
                break;
            }
        }
        //改变被吃结点的位置
        Random r=new Random();
        food.x=r.nextInt(39);
        food.y=r.nextInt(39);

    }
}
package Update;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

public class MainJFrame extends JFrame{
    JPanel jp;//游戏棋盘
    private Snack snack;//蛇
    Timer timer;//创建定时器,在指定的时间内调用蛇移动的方法
    Timer timer1;//创建定时器,在指定的时间内调用改变怪兽位置的方法
    Food food;//创建食物链表
    Monster monster;//创建怪物链表

    public MainJFrame(){
        initFrame();//初始化窗体参数的方法
        initGamePanel();//初始化棋盘画布的方法
        initSnack();//初始化蛇
        initTimer();//初始化定时器
        setkeyListener();//设置键盘监听,让蛇根据键盘进行上下左右移动
        initFood();//初始化食物
        initMonster();//初始化怪物
    }



    /*
    初始化窗体的方法
     */
    private void initFrame() {
        setSize(610,640);//设置窗体的大小
        setLocation(400,200);//设置窗体的位置,左上角距离左边400,距离上边100
        setResizable(false);//固定窗体的大小
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭按钮就是退出程序
    }

    /*
    初始化棋盘画布的方法:棋盘的横线,竖线,蛇,食物
     */

    private void initGamePanel() {
        jp=new JPanel(){//匿名内部类:用来绘制棋盘画布的内容
            @Override
            public void paint(Graphics g) {//g相当于画笔,可以用来绘制基本图形,如直线、矩形
                //清空棋盘:对应重绘棋盘repaint()
                g.clearRect(0,0,600,600);
                //画出41条横线,有40个格子
                for(int i=0;i<=40;i++){
                    g.drawLine(0,15*i,600,15*i);
                }
                //画出41条竖线,有1600个15*15的格子
                for(int j=0;j<=40;j++){
                    g.drawLine(15*j,0,15*j,600);
                }
                //绘制蛇(由结点Node组成):绘制矩形
                SnackBody body=snack.snackBody;//得到蛇的身体
                for(int i=0;i<body.size();i++) {//遍历蛇的身体的每个结点
                    Node node=body.get(i);
                    int X = node.x;
                    int Y = node.y;
                    g.fillRect(node.x * 15, node.y * 15, 15, 15); //用画笔绘制填充矩形
                }

                //绘制食物
                for (int i = 0; i < food.foodList.size(); i++) {
                    g.fillRect(food.foodList.get(i).x*15,food.foodList.get(i).y*15,15,15);
                }

                //绘制小怪兽
                for(int i=0;i<monster.monsterList.size();i++){
                    g.fillRect(monster.monsterList.get(i).x*15,monster.monsterList.get(i).y*15,15,15);
                    g.setColor(Color.BLUE);//小怪兽的颜色是蓝色
                }
            }
        };
        add(jp);//将画布对象添加到窗体中去
    }

    /*
    初始化蛇的方法
     */
    private void initSnack() {
        snack=new Snack();//创建蛇的对象
    }

    private void initMonster(){
        monster=new Monster();//创建怪物对象
    }

    /*
    初始化定时器的方法
     */
    private void initTimer() {
        timer=new Timer();//创建定时器对象
        timer1=new Timer();//创建定时器对象
        //初始化定时任务:在指定时间内调用蛇的移动方法
        TimerTask timerTask=new TimerTask() {
            @Override
            public void run() {

                snack.move();

                Node head=snack.snackBody.getFirst();

                //判断蛇头是否和食物重合
                if(food!=null) {
                    for (Node node : food.foodList) {
                        if (head != null && node != null && head.x == node.x && head.y == node.y) {//蛇头和食物重合
                            snack.eat(node);//蛇吃食物,蛇的长度增加
                            //改变被吃结点的位置
                            Random r = new Random();
                            node.x = r.nextInt(39);
                            node.y = r.nextInt(39);
                            snack.grade+=1;//贪吃蛇的得分
                        }
                    }
                }

                //判断蛇头是否与小怪物重合:重合就会死
                if(monster!=null&&monster.monsterList!=null) {
                    for (int i = 0; i < monster.monsterList.size(); i++) {
                        if (head != null && monster.monsterList.get(i) != null && head.x == monster.monsterList.get(i).x && head.y == monster.monsterList.get(i).y) {
                            snack.isLiving = false;
                        }
                    }
                }


                //界面的更新,重绘游戏棋盘
                jp.repaint();
            }
        };
        TimerTask timerTask1=new TimerTask() {
            @Override
            public void run() {
                if(monster!=null&&monster.monsterList!=null&&snack.isLiving) {
                    monster.changePosition();
                }
            }
        };
        timer1.scheduleAtFixedRate(timerTask1,0,5000);
        //表示该定时器没有任何延迟地每150ms执行一次设定的蛇的移动的任务
        timer.scheduleAtFixedRate(timerTask,0,150);
    }

    /*
    设置键盘监听的方法:重写键盘按下的操作
    1.当键盘按下时,会自动调用move方法
    2.键盘中每一个键都有编号,只需要获取?8?8?1?5?8?9?1?5?8?7?1?5?7?9?1?5键的编号,改变蛇的方向
    3.注意蛇不能连续向两个相反的方向进行运动:if判断就行
     */
    private void setkeyListener() {
        addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {//e是键盘的键
                switch(e.getKeyCode()){
                    case 37:{//表示e是左键,把蛇的direction改成左
                        if(snack.direction!= Direction.RIGHT) {//蛇目前的运动方向不是向右,否则就会出现倒着走的情况
                            snack.direction = Direction.LEFT;
                        }
                        break;
                    }
                    case 38:{//表示e是上键,把蛇的direction改成上
                        if(snack.direction!= Direction.DOWN) { //蛇目前的运动方向不是向右,否则就会出现倒着走的情况
                            snack.direction = Direction.UP;
                        }
                        break;
                    }
                    case 39:{//表示e是右键,把蛇的方向改成右
                        if(snack.direction!= Direction.LEFT) {
                            snack.direction = Direction.RIGHT;//蛇目前的运动方向不是向左,否则就会出现倒着走的情况
                        }
                        break;
                    }
                    case 40:{//表示e是下键,把蛇的方向改成下
                        if(snack.direction!= Direction.UP) {//蛇目前的运动方向不是向上,否则就会出现倒着走的情况
                            snack.direction = Direction.DOWN;
                        }
                        break;
                    }
                }
            }
        });
    }

    /*
    初始化食物的方法
     */
    private void initFood() {
        food=new Food();//创建食物对象
    }

    public static void main(String[] args) {
        MainJFrame mj=new MainJFrame();//创建窗体对象
        mj.setVisible(true);//将窗体对象设置为可见

    }

}
package Update;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
/*
食物的本质是结点的集合
 */
public class Food {
    LinkedList<Node> foodList;
    public Food(){
        initFood();//初始化食物
    }

    private void initFood() {
        foodList=new LinkedList<>();
        Random r=new Random();
        for(int i=0;i<40;i++){//初始化食物链表,有40个食物
            foodList.offer(new Node(r.nextInt(39),r.nextInt(38)));
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值