贪吃蛇的Java实现

贪吃蛇的Java实现

第一次写贪吃蛇游戏,重要在算法和联系效果,界面调用了swing包的绘图方法。这里记录下来,不足的地方请大家批评指正。

首先是需求分析,对于贪吃蛇,总共有如下几点需求:

  • 蛇会在舞台上定时自动运行
  • 用键盘光标键控制蛇的运行
  • 蛇不能反向运行,反向运行没有动作发生
  • 蛇吃到食物后长度会增加一节
  • 食物被吃掉之后会随机生成新的食物
  • 蛇碰到边界或吃到自己会死亡

具体分析如下图:
贪吃蛇需求粉丝思维导图

结合以上分析,可以将需求提取成3个类

  • 食物节点类:充当食物,组成蛇的身体
  • 蛇类:主要角色,承载众多属性和方法
  • 舞台类:用于绘制舍、食物、定时及键盘响应等

从以上3个类出发,进一步梳理确定他们的属性及方法,如下:
贪吃蛇结构思维导图
其中,test类是为了测试写好的代码片。具体的方法是我写完程序之后再添加上去的,在编程之前只能确定好大体的方向。
运行效果:
这里写图片描述
下面直接贴上代码片:

Cell类

package com.centerm.snake;
/**
 * 
 * 创建Cell类,作为蛇身体的组成部分,也用作充当食物
 *
 */
public class Cell {
    private int x;
    private int y;
    public Cell(){
    }
    public Cell(int x, int y){
        this.x = x;
        this.y = y;
    }
    public int getX(){
        return this.x;
    }
    public int getY(){
        return this.y;
    }
    public String toString(){
        return "["+this.x+","+this.y+"]";`
    }
}
-------------------
Snake类

package com.centerm.snake;

import java.util.Arrays;
/**
 * 
 * Snake类,作为蛇体,包含了众多元素和方法
 *
 */

public class Snake {
    public static final int DEFAULT_LENGTH = 12;
    public static final int SNAKE_UP = 1;
    public static final int SNAKE_DOWN = -1;
    public static final int SNAKE_LEFT = 2;
    public static final int SNAKE_RIGHT = -2;

    private Cell[] cells;
    private int currentDirection;

    /** 在构造函数中初始化一条默认的蛇 ,长度为12,位于左上方,默认方向向下*/
    public Snake(){
        cells = new Cell[DEFAULT_LENGTH];
        for(int i = 0; i<cells.length; i++){
            cells[i] = new Cell(i,0);
        }
        currentDirection = SNAKE_DOWN;
    }

    /** 包含方法    */
    public boolean contains(int x, int y){
        for(int i = 0; i<cells.length; i++){
            if(cells[i].getX() == x && cells[i].getY() == y){
                return true;
            }
        }
        return false;
    }

    /** 创建预测节点,在还没有吃时,创建某方向上即将吃到的节点,服务于其它方法 */
    private Cell createHead(int direction){
        int x = cells[0].getX();
        int y = cells[0].getY();

        switch (direction) {
        case SNAKE_UP:
            y -- ;  break;
        case SNAKE_DOWN:
            y ++ ;  break;
        case SNAKE_LEFT:
            x -- ;  break;
        case SNAKE_RIGHT:
            x ++ ;  break;
        default:
            break;
        }
        return new Cell(x,y);
    }

    /** 沿某个方向爬行,并判断有没有吃到食物,若有,则增长一节 */
    /** 先创建方向上的预测节点,若与食物不重合,则移动,若与食物重合
     * 则增加长度,且预测节点可以当作食物增加到节点0中     */
    public boolean creep(int direction, Cell food){
        if(currentDirection+direction == 0){
            return false;
        }
        Cell cell = createHead(direction);
        boolean eaten = cell.getX() == food.getX() && 
                        cell.getY() == food.getY();
        if(eaten){
            cells = Arrays.copyOf(cells, cells.length+1);
        }

        for(int i = cells.length-1; i>=1; i--){
            cells[i] = cells[i-1];
        }

        cells[0] = cell;
        currentDirection = direction;
        return eaten;
    }

    /** 沿当前方向爬行,自动运行时可以调用   */
    public boolean creep(Cell food){
        return creep(currentDirection, food);
    }

    /** 撞击判断,预测下一步是否越界或者吃到自己    */
    public boolean hit(int direction){
        if(currentDirection+direction == 0){
            return false;
        }

        Cell cell = createHead(direction);
        boolean wrong = cell.getX() < 0 || cell.getX() >= SnakeStage.COLS ||
                        cell.getY() < 0 || cell.getY() >= SnakeStage.ROWS;
        if(wrong){
            return true;
        }

        for(int i = 0; i<cells.length-1; i++){
            if(cell.getX() == cells[i].getX() &&
                    cell.getY() == cells[i].getY()){
                return true;
            }
        }

        return false;
    }

    /** 判断在当前方向上走会不会撞到边界,自动运行时用 */
    public boolean hit(){
        return hit(currentDirection);
    }


    public Cell[] getCells() {
        return cells;
    }

    /** 测试用,获取当前方向  */
    public int getCurrentDirection(){
        return currentDirection;
    }

    /

**  测试用,用于打印当前蛇体    */
    public String toString(){
        return Arrays.toString(cells);
    }
}
-------------------
SnakeStage类
package com.centerm.snake;
/**
 * 
 * SnakeStage类,舞台,实现绘制和管理作用
 *
 */

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;

public class SnakeStage extends JPanel{
    public static final int COLS = 35;
    public static final int ROWS = 35;
    private static final int CELL_SIZE = 10;

    private Snake snake;
    private Cell food;

    /** 构造函数    ,初始化蛇体和食物   */
    public SnakeStage(){
        snake = new Snake();
        food = createFood();
    }

    /** 创建食物,随机创建一个与蛇体不重复的食物    */
    private Cell createFood(){
        Random random = new Random();
        int x ;
        int y ;
        do{
            x = random.nextInt(COLS);
            y = random.nextInt(ROWS);
        }while(snake.contains(x, y));

        return new Cell(x, y);
    } 

    /** 重写JPanel中的paint()函数,画出蛇和食物,之后后台会一直调用这个函数    */
    public void paint(Graphics g){
        //填充背景色
        g.setColor(Color.darkGray);
        g.fillRect(0, 0, this.getWidth(), this.getHeight());
        //绘制食物
        g.setColor(Color.red);
        g.fill3DRect(CELL_SIZE*food.getX(), CELL_SIZE*food.getY(),
                CELL_SIZE, CELL_SIZE, true);
        //绘制蛇
        g.setColor(Color.yellow);
        Cell[] cells = snake.getCells();
        for(int i = 0; i<cells.length; i++){
            g.fill3DRect(CELL_SIZE*cells[i].getX(), 
                    CELL_SIZE*cells[i].getY(),
                    CELL_SIZE, CELL_SIZE, true);
        }
    }

    /**     开启贪吃蛇,使用定时器实现自动爬行,使用键盘响应方法实现控制方向爬行      */
    public void action(){
        /*  使用定时器实现自动爬行 */
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            public void run() {
                if(snake.hit()){
                    snake = new Snake();
                    food = createFood();
                }else{
                    boolean eaten = snake.creep(food);
                    if(eaten){
                        food = createFood();
                    }
                }
                //每更新一次就要重绘一次,下面这个方法会尽快调用paint()
                repaint();
            }
        }, 0, 1000/10);

        /*  使用JPanel的键盘响应实现自动爬行 */
        this.requestFocus();    //焦点来到面板
        this.addKeyListener(new KeyAdapter(){   //创建键盘监听器
            public void keyPressed(KeyEvent e) {    
                switch(e.getKeyCode()){
                case KeyEvent.VK_UP:
                    creepTo(Snake.SNAKE_UP);
                    break;
                case KeyEvent.VK_DOWN:
                    creepTo(Snake.SNAKE_DOWN);
                    break;
                case KeyEvent.VK_LEFT:
                    creepTo(Snake.SNAKE_LEFT);
                    break;
                case KeyEvent.VK_RIGHT:
                    creepTo(Snake.SNAKE_RIGHT);
                    break;
                default:
                    break;
                }
            }
        });
    }

    /** 这个方法本来应该写在键盘判断里面,但是写在每个case都要写一遍,这里就提出来 */
    private void creepTo(int direction){
        if(snake.hit(direction)){
            snake = new Snake();
            food = createFood();
        }else{
            if(snake.creep(direction,food)){
                food = createFood();
            }
        }
        //响应完按键马上重绘
        repaint();
    }

    /**     程序入口        */
    public static void main(String[] args){
        JFrame frame = new JFrame("贪吃蛇");   //创建一个框架
        SnakeStage panel = new SnakeStage();//创建一个面板
        frame.setLayout(null);  //取消框架默认设置
        frame.add(panel);       //在框架上添加面板
        panel.setSize(CELL_SIZE*COLS, CELL_SIZE*ROWS);//设置尺寸
        panel.setLocation(10, 10);  //设置面板在框架中的问题
        frame.setSize(CELL_SIZE*COLS+40, CELL_SIZE*ROWS+60);//设置尺寸
        panel.setBorder(new LineBorder(Color.black));   //设置面板边界
        frame.setLocationRelativeTo(null);  //使框架在屏幕居中显示
        frame.setVisible(true); //将框架和面板显示出来
        frame.setDefaultCloseOperation(JFrame.
EXIT_ON_CLOSE);//退出使能
        panel.action(); //启动贪吃蛇
    }

    /** 调试用 */
    public String toString(){
        return "{"+snake+"}"+"\n"+food;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值