Java简易贪吃蛇——实验项目(上)

运行结果截图

Java简易贪吃蛇——实验项目(上)_第1张图片

需求分析:

  • 用户操作→贪吃蛇应用产生事件⇌控制台更新网格状态→网格棋盘存放需要改变状态的类→贪吃蛇

根据上述需求,设计以下类

  1. Snake.java类:设计蛇的身体(节点)、根据贪吃蛇移动的方法来变化它的坐标位置
  2. Grid.java类:设计网格的长宽,设计生成食物(节点)的位置,设计判断贪吃蛇会触发的事件(触墙、吃到自己、吃到食物后长大)
  3. Controller.java类:设计游戏页面
  4. SnakeApp.java类:引入线程,添加各类对象,运行类

进一步分析各类,考虑封装等情况

  1. Snake类与Grid类中都含有设计元素(蛇,食物)的节点,所以需要封装一个节点类:Node.java类
  2. Snake类需要根据蛇的方向改变身体状态,Grid类需要判断蛇改变身体方向后会不会触发事件,所以需要封装一个方向类:Direction.java类
  3. Controller类需要绘制游戏区域(以及菜单),SnakeApp类会对游戏区域进行初始化,所以需要设计一个游戏:GameView.java类

设计:Direction.java类中方法:

Direction(int directionCode) {}:每个方向对应一个值
public boolean compatibleWith(Direction d){}:排除操作方向相反与移动方向相反的情况,防止游戏直接失败

代码:

enum Direction {
     
    UP(0),
    RIGHT(1),
    DOWN(2),
    LEFT(3);

    private final int directionCode;

    Direction(int directionCode) {
     
        this.directionCode = directionCode;
    }

    //这里采用绝对值匹配
    public boolean compatibleWith(Direction d){
     
        if (Math.abs(this.directionCode-d.directionCode)==2) return false;
        else return true;
    }
}

设计Node.java类中方法:

Node(int x, int y){}:坐标x与坐标y共同指向一个节点

代码:

class Node {
     
    private final int x;
    private final int y;

    //成员变量x和y构成了一个节点
    Node(int x, int y){
     
        this.x = x;
        this.y = y;
    }

    public int getX(){
     
        return x;
    }

    public int getY(){
     
        return y;
    }
}

设计Snake.java类中的方法:

public Node getHead(){}:获取数组第一个元素
public Node addBody(Node newBody){}:添加身体节点(蛇尾处)
public LinkedList getBody(){}:获取身体长度(用于计算分数)
public Node move(Direction direction){}:根据贪吃蛇移动的方法来变化它的坐标位置

代码:

class Snake {
     

    //创建Node类型的身体数组,根据Node.java中的设置可知这是一个通过坐标组成的动态数组
    //根据List的分类可知LinkedList链表数组适用于添加元素,所以这里利用创建body对象使用linkedList动态数组
    //采用泛型对LinkedList中的元素进行限制
    private LinkedList<Node> body = new LinkedList<Node>();

    //返回数组第一个元素(蛇头)
    public Node getHead(){
     
        return body.getFirst();

    }

    //在数组末尾添加新元素(蛇尾)
    public Node addBody(Node newBody){
     
        body.addLast(newBody);
        return newBody;
    }

    //获取蛇身体长度
    public LinkedList<Node> getBody(){
     
        return body;
    }

    //根据贪吃蛇移动的方法来变化它的坐标位置
    public Node move(Direction direction){
     
        Node newHead;
        //newHead为body数组中的新增的第一个元素
        //通过switch case为数组body中添加第一个元素newHead,同时移除最后一个元素节点

        switch (direction){
     //四种情况来设置newHead的位置
            case UP:
                newHead = new Node(body.getFirst().getX(),body.getFirst().getY()-1);
                break;
            case RIGHT:
                newHead = new Node(body.getFirst().getX()+1,body.getFirst().getY());
                break;
            case DOWN:
                newHead = new Node(body.getFirst().getX(),body.getFirst().getY()+1);
                break;
            case LEFT:
                newHead = new Node(body.getFirst().getX()-1,body.getFirst().getY());
                break;
            default:
                newHead = null;
        }
        body.add(0,newHead);//添加第一个节点
        return body.removeLast();//返回删除最后一个节点
    }
}

设计Grid.java类中的方法:

private int getRecord() {}:获取记录分数
public int getScore(){}:获取当前游戏分数
public int returnRecord(){}:返回记录分数
public int increaseScore(){}:记录增加的分数
public void changeRecord(int result) throws Exception{}:通过文件读取处过去最高分数
private Node snakeAddBody(Node body){}:当蛇增加一个身体节点后,返回增加节点后的蛇
private Snake initSnake(){}:对蛇的长度与方向进行初始化
private Node createFood(){}:随机生成食物
private Node snakeMove(){}:判断蛇移动后游戏能否继续(根据Sanke.move,还需判断移动方向是否有效以及移动后游戏能否继续)
private boolean isHeadValid(){}:判断头部接触的位置是否有效
private boolean headTouchBody(int headX, int headY){}:判断头部是否接触身体
public boolean nextRound(){}:根据头部位置是否有效来判断是否继续游戏
private boolean isHeadFood(){}:判断头部是否接触食物
public void changeDirection(Direction newDirection){}:判断方向改变是否有效(相反方向无效,根据Direction.compatibleWith()方法判断)
public Snake getSnake(){}:获取Snake类的对象
public Node getFood(){}:获取Node类的对象

代码:

class Grid {
     
    //方格的覆盖可以用一个boolean类型的二维数组来表示
    public final boolean status[][];
     //固定方格的宽与长
    private final int width;
    private final int height;
    private int score = 0;
    private int record;

    private Snake snake;
    private Node food;

    //蛇的初始方向默认向左;
    private Direction snakeDirection = Direction.LEFT;

    Grid(int width, int height) throws Exception {
     
        this.height = height;
        this.width = width;
        //这里采用稀疏数组,用f=便于直接用for循环一次性记录整个网格
        //由于网格只有被覆盖与未被覆盖的情况,所以使用boolean类型
        status = new boolean[width][height];//蛇占据的地方为true
        initSnake();
        createFood();
        this.record = getRecord();

    }

    private int getRecord() throws Exception {
     //获取记录分数,须在指定路径下创建文件夹
        BufferedReader recordFile = new BufferedReader(new FileReader("src/snake/scoresRecord.txt"));
        String a = recordFile.readLine();
        this.record = Integer.parseInt(a.trim());
        recordFile.close();
        return record;
    }

    public int getScore(){
     
        return score;
    }

    public int returnRecord(){
     
        return record;
    }

    public int increaseScore(){
     
        score++;
        return score;
    }

    public void changeRecord(int result) throws Exception{
     //通过文件记录分数
        FileWriter fw = new FileWriter("src/snake/scoresRecord.txt",false);
        BufferedWriter recordFile = new BufferedWriter(fw);
        recordFile.write(String.valueOf(result));
        fw.flush();
        recordFile.close();
    }

    private Node snakeAddBody(Node body){
     //当蛇增加一个身体节点后,返回增加节点后的蛇
        status[body.getX()][body.getY()] = true;
        snake.addBody(body);
        return body;
    }


    private Snake initSnake(){
     
     // 1.设置Snake的Body
     // 2.更新棋盘覆盖状态
        this.snake = new Snake();
        //蛇初始为横着的状态,长度为width的三分之一
        int headX = (int)(width/6*Math.random()+width/3);
        int headY = (int)(height*Math.random());
        while(headY <= 0|| headY>height-2){
     
            headY = (int)(height*Math.random());
        }
        Node head = new Node(headX,headY);
        snakeAddBody(head);
        for (int i=1; i<(int)(width/3); i++){
     
            headX++;
            snakeAddBody(new Node(headX,headY));
        }
        return snake;
    }

    private Node createFood(){
     
     //随机创建食物,生成食物的X坐标和Y坐标。使用Random类来生成随机数。
    //生成的食物X坐标和Y坐标不能超过棋盘大小,食物的位置不能和贪吃蛇的位置重叠
        int foodX = (int)(width*Math.random());
        int foodY = (int)(height*Math.random());
        while(status[foodX][foodY] || foodX<=0 || foodX>width-2 || foodY <= 0 || foodY>height-2){
     
            foodX = (int)(width*Math.random());
            foodY = (int)(height*Math.random());
        }
        this.food = new Node(foodX,foodY);
        return food;
    }

    private Node snakeMove(){
     
       //根据Sanke.move方法判断移动方向是否有效以及移动后游戏能否继续
        Node tail = snake.move(snakeDirection);
        status[snake.getHead().getX()][snake.getHead().getY()] = true;
        status[tail.getX()][tail.getY()] = false;
        return tail;
    }


    public boolean nextRound(){
     
    //根据头部位置是否有效来判断是否继续游戏
    //移动后游戏继续返回true,否则返回false
        Node tail = snakeMove();
        if(isHeadValid()){
     
            if (isHeadFood()) {
     
                snake.addBody(tail);
                status[tail.getX()][tail.getY()] = true;
                createFood();
                increaseScore();
            }
            return true;
        }
        else{
     
            return false;
        }
    }

    private boolean isHeadValid(){
     
        //判断头部接触的位置是否有效
        Node head = snake.getHead();
        int headX = head.getX();
        int headY = head.getY();
        if (headX <= 0 || headX >= width-1 || headY <= 0 || headY >= height-1 || headTouchBody(headX,headY)){
     
            return false;
        }
        else{
     
            return true;
        }
    }

    private boolean headTouchBody(int headX, int headY){
     
    //判断头部是否接触身体
        LinkedList<Node> a = snake.getBody();
        for (int i=1; i<a.size(); i++){
     
            if (headX == a.get(i).getX() && headY == a.get(i).getY()){
     
                return true;
            }
        }
        return false;
    }

    private boolean isHeadFood(){
     
    //判断头部是否接触食物
        Node head = snake.getHead();
        if (head.getX() == food.getX() && head.getY() == food.getY()){
     
            return true;
        }
        else{
     
            return false;
        }
    }

    public void changeDirection(Direction newDirection){
     
    //判断方向改变是否有效(相反方向无效,根据Direction.compatibleWith()方法判断)
        if (snakeDirection.compatibleWith(newDirection)){
     
            snakeDirection = newDirection;
        }
    }
    //引入snake对象
    public Snake getSnake(){
     
        return this.snake;
    }
    //引入食物位置
    public Node getFood(){
     
        return this.food;
    }
}

后续会继续更新,把剩下的代码补齐

你可能感兴趣的:(java简单项目,java)