小小贪吃蛇——JAVA

回顾了一下JavaSE基础,顺便用一下午的时间写了一个小小贪吃蛇
本篇文章献给Java初学者,或者大一大二的学生朋友们。

因为当时大一下学期学习Java基础后,期末要求做一个小项目,都是一些小游戏,比如俄罗斯方块,贪吃蛇什么的。当时博主太菜了,累死累活整了4天,才弄一个半成品,而且学习效果也不咋地。。到时到处找资源,看教学视频都看不懂,。。

不过博主经过不懈的努力,终于从一个菜鸟变成了 小白(笑哭)。

希望本篇文章可以对你有帮助。每行代码几乎都有注释,我就不再多做解释了,文章末尾我会奉上源码,和马士兵的贪吃蛇教程,还有直接可以运行的程序(个人感觉他的小蛇部分做的有些复杂,我的比他的简单一些,功能也差不多)

博主这次做的算是1.0的版本吧!
界面一般,功能一般,如果你有兴趣,可以继续完善,做出 属于你自己的小蛇吧!

我的贪吃蛇除了具备一般小蛇的特性以外,有以下特性:
    1、左右可穿墙(上下的墙碰到就死)
    2、速度随着吃的蛋蛋的数目加快,就是吃的越多,速度越快(分数也是)

咱们自己做的游戏,规则咱们可以随便定。

附上几个图片,真图为证:
这里写图片描述

可以穿墙的呦!
这里写图片描述

GAME OVER:
这里写图片描述
首先是院子,也就是地图。
废话不多说,附上代码(引入的包太占位置了,我给去了):

public class Yard extends JFrame{
    //定义院子的大小
    public static final int ROWS = 40;  //行数
    public static final int COLS = 50; //列数
    public static final int BLOCK_SIZE = 20; //每个小格子的尺寸
    private static int score = 0;   //分数
    private static int count = 0;   //吃蛋蛋的次数
    private static int speed = 10;  //速度
    Snake snake = new Snake(this);  //初始化一条小蛇
    Egg egg = new Egg();            //初始化一个蛋蛋


    public static void main(String[] args){
        new Yard().launch();        //启动游戏

    }
    Image offScreenImage = null;    //解决闪烁

    @Override
    //解决闪烁
    public void update(Graphics g) {
        if(offScreenImage == null) {
            offScreenImage = this.createImage(COLS * BLOCK_SIZE, ROWS * BLOCK_SIZE);
        }
        Graphics gOff = offScreenImage.getGraphics();
        paint(gOff);
        g.drawImage(offScreenImage, 0, 0,  null);
    }
    //来一个院子
    public void launch(){
        this.setLocation(500, 200); //设置窗口初始位置
        this.setSize(COLS*BLOCK_SIZE,ROWS*BLOCK_SIZE);  //设置窗口大小
        this.addWindowListener(new WindowAdapter(){     //当关闭窗口时,同时关闭java正在运行的虚拟机
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);     //关闭虚拟机
            }
        });
        this.setVisible(true);      //让窗口可显示
        new Thread(new PaintThread()).start();  //开启线程,让小蛇动起来
        this.addKeyListener(new KeyMonitor());  //加入监听器
    }
    //画板
    @Override
    public void paint(Graphics g) {
        // TODO Auto-generated method stub
        Color c = g.getColor();     //获取画板color,用于设置颜色
        g.setColor(c.gray);         //设置当前颜色为灰色
        g.fillRect(0, 0, COLS*BLOCK_SIZE, ROWS*BLOCK_SIZE); //把颜色填充到整个窗口
        for (int i = 1; i < COLS-1; i++) {
            g.setColor(c.white);    //设置当前颜色为白色
            g.fill3DRect(i*BLOCK_SIZE,2*BLOCK_SIZE,BLOCK_SIZE,BLOCK_SIZE,true); //上边的墙
            g.fill3DRect(i*BLOCK_SIZE,(ROWS-2)*BLOCK_SIZE,BLOCK_SIZE,BLOCK_SIZE,true);  //上边的墙
        }
        g.setColor(c.darkGray);
        //画横线
        for (int i = 1; i < ROWS; i++) {
            g.drawLine(BLOCK_SIZE, i*BLOCK_SIZE, (COLS-1)*BLOCK_SIZE, i*BLOCK_SIZE);
        }
        //画竖线
        for (int i = 1; i < COLS; i++) {
            g.drawLine(i * BLOCK_SIZE, BLOCK_SIZE*2, i * BLOCK_SIZE, (ROWS-1) * BLOCK_SIZE);
        }

        g.setColor(c.yellow);
        g.setFont(new Font("宋体",Font.BOLD,25)); //设置上下文字体
        g.drawString("score:"+score, 20, 80);       //用于显示score分数
        g.drawString("speed:"+speed, 20, 110);      //用于显示speed速度
        snake.paint(g);     //在这个画板中画一条小蛇
        if(snake.eat(egg)){ //如果小蛇吃到了蛋蛋
            count++;        //吃蛋蛋的次数+1,
            score += 10*count;//加分,吃的越多,分加的越高
            speed += 5; //速度增加
        }
        egg.draw(g);        //画一个蛋蛋
        if(snake.isGameOver()){     //如果GAME OVER了,在中间显示一个GANE OVER
            g.setColor(c.yellow);
            g.setFont(new Font("宋体",Font.BOLD,80));
            g.drawString("GAME OVER!", 300, 400);
        }
    }

    //线程内部类,本线程用于整个游戏的运行
    private class PaintThread implements Runnable{
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(!snake.isGameOver()){
                repaint();  //刷新页面
                try {
                    snake.move();       //蛇移动
                    Thread.sleep(300/speed*10);     //这里控制蛇移动的速度
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            repaint();  //游戏结束后刷新一下页面,用于显示GAME OVER
        }
    }
    //监听器
    private class KeyMonitor extends KeyAdapter{
        //监听键盘的指令
        @Override
        public void keyPressed(KeyEvent e) {
            snake.keyPressed(e);    
        }
    }
}

接着是小蛇:

public class Snake {
    //一下分别是四个方向的状态码,用来标识方向
    public static final int DIR_UP = 1;     //向上走
    public static final int DIR_DOWN = 2;   //向下走
    public static final int DIR_LEFT = 3;   //向左走
    public static final int DIR_RIGHT = 4;  //像右走
    //默认的DIR为4,即往右走
    public static int DIR = 4;
    //size标识每个小格子的大小
    int size = Yard.BLOCK_SIZE;
    private Yard yard;  //用来绑定对应的院子
    LinkedList<Point> snake = new LinkedList<Point>();  //用链表来存储蛇身子,方便加头去尾
    public Snake(Yard yard){//用来绑定对应的院子
        initSnake();    //初始化小蛇
        this.yard = yard;   //绑定院子
    }
    //初始化蛇
    public void initSnake(){
        //先来三个节点,作为初始的身子
        snake.addFirst(new Point(Yard.BLOCK_SIZE*(Yard.COLS-2)/2,Yard.BLOCK_SIZE*Yard.ROWS/2));
        snake.addFirst(new Point(Yard.BLOCK_SIZE*(Yard.COLS)/2,Yard.BLOCK_SIZE*Yard.ROWS/2));
        snake.addFirst(new Point(Yard.BLOCK_SIZE*(Yard.COLS+2)/2,Yard.BLOCK_SIZE*Yard.ROWS/2));
    }
    //创建蛇的头,方便使用
    Point head = new Point();
    //画蛇
    public void paint(Graphics g){
        Color c = g.getColor();
        g.setColor(c.darkGray);
        head = this.snake.getFirst();   //单独做了一个蛇头,用圆角矩形表示
        g.fillRoundRect(head.x, head.y, Yard.BLOCK_SIZE, Yard.BLOCK_SIZE,10,10);
        g.setColor(c.black);
        //遍历链表,来画出整条小蛇
        for (int i = 1; i < snake.size(); i++) {    //蛇身子用普通的矩形表示
            g.fillRect(snake.get(i).x, snake.get(i).y, Yard.BLOCK_SIZE, Yard.BLOCK_SIZE);
        }
    }

    //蛇移动
    //移动的原理就加头去尾
    //用链表可以很轻松的实现,他封装了一些的方法,使用起来很方便,可以查一下api文档
    //在这里,如果到左右边界了,可以穿过去,实现穿墙操作
    public void move(){
        head = this.snake.getFirst();   //获取当前的头节点
        switch (DIR) {      //根据不同的方向,对应不同的移动方案
        case DIR_UP:
            this.snake.addFirst(new Point(head.x,head.y-size)); //添加一个头节点
            this.snake.removeLast();        //去尾操作,去掉尾巴节点
            break;
        case DIR_DOWN:
            this.snake.addFirst(new Point(head.x,head.y+size));
            this.snake.removeLast();
            break;
        case DIR_LEFT:
            if(head.x == size){ //到了左边的边界
                this.snake.addFirst(new Point((yard.COLS-2)*size,head.y));  //穿墙
            }else{
                this.snake.addFirst(new Point(head.x-size,head.y));
            }
            this.snake.removeLast();
            break;
        case DIR_RIGHT:
            if(head.x == (yard.COLS-2)*size){   //到了右边边界
                this.snake.addFirst(new Point(size,head.y));        //穿墙
            }else{
                this.snake.addFirst(new Point(head.x+size,head.y));
            }
            this.snake.removeLast();
            break;

        default:
            break;
        }
    }

    //判断是否撞墙或者咬到自己,游戏结束
    public boolean isGameOver(){
        head = this.snake.getFirst();
        //撞墙(上下的墙)(左右的墙可以穿过去)
        if( head.y <= 2*size || head.y >= (yard.ROWS-2)*size){
            return true;
        }
        //咬到自己
        for (int i = 1; i < this.snake.size(); i++) {
            if(head.x == this.snake.get(i).x && head.y == this.snake.get(i).y){
                return true;
            }
        }

        return false;
    }

    //监听键盘,用来改变方向
    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()){    //获取键的对应的码
        case KeyEvent.VK_UP:    //向上的键,下面的同理
            if(DIR != DIR_DOWN){    //这里控制不能直接反方向走
                DIR = DIR_UP;
            }
            break;
        case KeyEvent.VK_DOWN:
            if(DIR != DIR_UP)
                DIR = DIR_DOWN;
            break;
        case KeyEvent.VK_LEFT:
            if(DIR != DIR_RIGHT)
                DIR = DIR_LEFT;
            break;
        case KeyEvent.VK_RIGHT:
            if(DIR != DIR_LEFT)
                DIR = DIR_RIGHT;
            break;
        }
    }

    //吃蛋蛋
    //吃着了就加一个尾巴,就可以实现变长了
    public boolean eat(Egg egg){
        if(egg.x == this.head.x && egg.y == this.head.y){
            egg.createFood();       //重新创建一个蛋蛋
            Point last = this.snake.getLast();  //获取当前的尾巴节点
            this.snake.addLast(last);   //添加一个新的尾巴节点
            return true;
        }
        return false;
    }
}

最后是要吃的食物(蛋蛋):

package MySnake;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Random;

public class Egg {
    private static Random random = new Random();    //用来随机产生蛋蛋的对象
    int size = Yard.BLOCK_SIZE;     //每个小格子的大小
    //食物的坐标
    public int x;
    public int y;

    public Egg(){   //构造方法,产生一个蛋蛋
        createFood();
    }
    //创建食物
    public void createFood(){
        this.x = (random.nextInt(Yard.COLS-3)+2)*size;  //因为边框的问题,所以这里有改动,可以试验一下
        this.y = (random.nextInt(Yard.ROWS-5)+3)*size;
    }
    //画出食物
    public void draw(Graphics g){
        Color c = g.getColor();
        g.setColor(c.red);
        g.fillOval(x, y, size, size);   //画一个圆形来表示蛋蛋
    }
}

目前这个版本还是1.0,仅供初学者参考,(写太复杂了反而违背了本意)
以上是全部代码。。可能你还是看不懂,没关系,源码给你,慢慢研究:
http://pan.baidu.com/s/1hsjApxU

并附上马士兵的教程以及源码:
http://pan.baidu.com/s/1nvHVWYP

我顺便把程序打包了 ,双击直接运行,下面这个是在有JRE环境的电脑上可以运行:
http://pan.baidu.com/s/1geHmby7

后来考虑到个别小伙伴想在没有jre环境的电脑上运行,于是我花了半下午的时间又打包成真正的桌面应用程序,安装之后,随处都可运行:
http://pan.baidu.com/s/1eRJpTIE

个人感觉马士兵老师讲的这个贪吃蛇的小蛇部分有些复杂繁琐,可以参考我的代码结合着老师的视频,做出属于自己的贪吃蛇吧!!

拯救不开心!!!

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值