javaSE第一个项目——贪吃蛇

项目:思想 | 思维

1.1.1 窗体

​ 展示游戏的主界面

​ Frame -> 窗体 【位置、大小、背景色、标题】

package com.example.snake;

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * 绘制窗体
 * Frame -> 窗体  【位置、大小、背景色、标题】
 * 设置窗体可见
 * 关闭窗口的事件监听
 * 窗口是不能重新设置大小的
 */
public class SnakeFrame01 extends Frame {

    //构造器中初始化窗体
    public SnakeFrame01(){
        this.setLocation(200,100);
        this.setSize(600,500);

        this.setBackground(Color.magenta);
        this.setTitle("贪吃蛇V1.0");
        this.setResizable(false);
        //设置窗体显示
        this.setVisible(true);

        //注册窗口监听
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                //退出虚拟机
                System.exit(0);
            }
        });
    }

    public static void main(String[] args) {
        new SnakeFrame01();
    }
}
1.1.2 蛇
蛇的主体
  1. paint->该方法是Frame中提供的,每一次窗体显示|更新的时候自动调用
  2. 蛇的主体绘制

​ 封装一个节点类(Node)存储坐标,绘制自己本身

Node.java

package com.example.snake;

import java.awt.*;

/**
 * 节点类: 保存坐标  x,y
 */
public class Node {
    private int x;
    private int y;

    public Node(){

    }

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

    public int getX() {
        return x;
    }

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

    public int getY() {
        return y;
    }

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

    /**
     * 绘制节点
     * @param g
     */
    public void drawNode(Graphics g,int i){
        if (0==i){
            g.fillOval(this.x,this.y,20-1,20-1);
        } else {
            g.fillRect(this.x,this.y,20-1,20-1);
        }
    }
}

窗体中,维护蛇的主体。Node[]

package com.example.snake;

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * 绘制窗体
 * Frame -> 窗体  【位置、大小、背景色、标题】
 * 设置窗体可见
 * 关闭窗口的事件监听
 * 窗口是不能重新设置大小的
 */
public class SnakeFrame02 extends Frame {

    //蛇的主体部分
    private Node[] body={
            new Node(140,100),
            new Node(120,100),
            new Node(100,100)
    };

    //构造器中初始化窗体
    public SnakeFrame02(){
        this.setLocation(200,100);
        this.setSize(600,500);

        this.setBackground(Color.magenta);
        this.setTitle("贪吃蛇V1.0");
        this.setResizable(false);
        //设置窗体显示
        this.setVisible(true);

        //注册窗口监听
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                //退出虚拟机
                System.exit(0);
            }
        });
    }

    /**
     * 窗体显示 | 更新 自动调用
     * 绘制图形、线条、文本
     * @param g 画笔
     */
    @Override
    public void paint(Graphics g) {
        g.setColor(Color.orange);
        //绘制蛇的主体部分
        for (int i=0; i<body.length; i++){
            body[i].drawNode(g,i);
        }
    }

    public static void main(String[] args) {
        new SnakeFrame02();
    }
}
移动

一直重绘窗体repaint()—>paint()

/**
* 手动一直重绘窗体
*/
public void start(){
 while (isRunning){
     super.repaint();  //手动让窗体重绘
     try {
         Thread.sleep(500);  //暂停事件
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
 }
}

改变坐标

/**
     * 通过修改坐标达到移动的效果
     */
public void remove(){
    //向右移动
    //每一次只需要给一个新的头节点,去掉尾节点即可
    Node[] temp = new Node[body.length];
    //去掉尾部
    System.arraycopy(body,0,temp,1,body.length-1);
    temp[0] = new Node(body[0].getX()+20,body[0].getY());
    this.body = temp;
}
通过键盘控制方向

​ 添加键盘监听事件

//注册键盘监听
this.addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        int dir = -1;
        //上 1,下 3,右 0,左 2
        switch (e.getKeyCode()){
            case KeyEvent.VK_UP: //向上走
                dir = 1;
                break;
            case KeyEvent.VK_DOWN: //向下走
                dir = 3;
                break;
            case KeyEvent.VK_LEFT: //向左走
                dir = 2;
                break;
            case KeyEvent.VK_RIGHT: //向右走
                dir = 0;
                break;
        }

        //根据合法性 修改方向 (不允许在同一条直线上修改)
        if (direction%2 != dir%2){
            direction = dir;
        }
    }

根据按键修改方向

/**
   * 根据方向来确定一个新的头节点
   * @return
   */
public Node getNewHead(){
      Node newNode = body[0];
      switch (this.direction){
          //上 1,下 3,右 0,左 2
          case 0: //右
              newNode = new Node(newNode.getX()+20,newNode.getY());
              break;
          case 2: //左
              newNode = new Node(newNode.getX()-20,newNode.getY());
              break;
          case 1: //上
              newNode = new Node(newNode.getX(),newNode.getY()-20);
              break;
          case 3: //下
              newNode = new Node(newNode.getX(),newNode.getY()+20);
              break;
      }
      return newNode;
  }
1.1.3 食物

食物的主体 Node

产生食物 —>随机产生[不要超过边界;满足窗体中一个隐形的格子]

/**
* 随机产生食物
*/
public void productFood(){
 food = new Node(randomNum(0,600/20-1)*20, randomNum(2,500/20-1)*20);
}
1.1.4 蛇吃食物

何时能吃到食物

/**
 * 是否吃到食物:下一步的头节点的位置刚好和食物的位置相等
 * @return
 */
public boolean isEat(){
    Node newNode = getNewHead();
    if (newNode.equals(food)){
        return true;
    }

    return false;
}

食物重新产生

蛇的主体会增加

/**
* 吃到食物了,增加蛇的身体
*    将食物变成蛇的头部
* 食物重新产生
*    食物消失,重生成
*/
public void addBody(){
 Node[] temp = new Node[body.length+1];
 temp[0] = food;
 System.arraycopy(body,0,temp,1,body.length);
 body = temp;
 food = null;
 productFood();
}
1.1.5 游戏结束

碰到边界

碰到自己的身体

/**
     * 游戏是否结束
     * 1.
     * 2.
     * @return true 游戏结束    false 未结束
     */
public void isOver(){
    Node head = body[0];
    //边界
    if (head.getX()<=0 || head.getX()>=600-20 || head.getY()<=20 || head.getY()>=500-20){
        isRunning = false;
    }

    //身体
    for (int i=1; i<body.length; i++){
        if (head.equals(body[i])){
            isRunning = false;
        }
    }
}

优化代码和最终实现

package com.example.snake;

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * 绘制窗体
 * Frame -> 窗体  【位置、大小、背景色、标题】
 * 设置窗体可见
 * 关闭窗口的事件监听
 * 窗口是不能重新设置大小的
 *
 * 2.绘制蛇的身体
 *
 * 3.动起来
 *  1)一直重绘窗体
 *  2)修改坐标
 *
 * 4.手动控制方向
 *  1)、添加键盘监听事件
 *
 *  2)、根据按键修改方向
 *
 * 5.食物
 *   主体
 *   位置
 *
 * 6.蛇吃食物
 *   何时吃到食物
 *   产生食物
 *   蛇的身体加长
 * 7.游戏结束
 *   头部碰到身体
 *   头部碰到边界
 */
public class SnakeFrame08 extends Frame {

    private static final int WINDOW_X = 200;
    private static final int WINDOW_Y = 100;
    private static final int WINDOW_W = 600;
    private static final int WINDOW_H = 500;

    //上 1,下 3,右 0,左 2
    private static final int up = 1;
    private static final int down = 3;
    private static final int right = 0;
    private static final int left = 2;

    //程序还在继续吗
    private boolean isRunning = true;

    //上 1,下 3,右 0,左 2
    private int direction = 0;

    //食物
    private Node food = new Node(randomNum(0,WINDOW_W/20-1)*20, randomNum(2,WINDOW_H/20-1)*20);

    //蛇的主体部分
    private Node[] body={
            new Node(140,100),
            new Node(120,100),
            new Node(100,100)
    };

    //构造器中初始化窗体
    public SnakeFrame08(){
        this.setLocation(WINDOW_X,WINDOW_Y);
        this.setSize(WINDOW_W,WINDOW_H);

        this.setBackground(Color.magenta);
        this.setTitle("贪吃蛇V1.0");
        this.setResizable(false);
        //设置窗体显示
        this.setVisible(true);

        //注册窗口监听
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                //退出虚拟机
                System.exit(0);
            }
        });

        //注册键盘监听
        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                int dir = -1;
                //上 1,下 3,右 0,左 2
                switch (e.getKeyCode()){
                    case KeyEvent.VK_UP: //向上走
                        dir = up;
                        break;
                    case KeyEvent.VK_DOWN: //向下走
                        dir = down;
                        break;
                    case KeyEvent.VK_LEFT: //向左走
                        dir = left;
                        break;
                    case KeyEvent.VK_RIGHT: //向右走
                        dir = right;
                        break;
                }

                //根据合法性 修改方向 (不允许在同一条直线上修改)
                if (direction%2 != dir%2){
                    direction = dir;
                }
            }
        });

        start();
    }

    /**
     * 窗体显示 | 更新 自动调用
     * 绘制图形、线条、文本
     * @param g 画笔
     */
    @Override
    public void paint(Graphics g) {
        g.setColor(Color.orange);
        //绘制蛇的主体部分
        for (int i=0; i<body.length; i++){
            body[i].drawNode(g,i);
        }

        g.setColor(Color.CYAN);
        food.drawNode(g,-1);

        if (isRunning){
            if (isEat()){
                //System.out.println("吃到食物了");
                addBody();
            } else {
                //修改坐标
                remove();
            }
            isOver();
        }else {
            //System.out.println("game over");
            g.setColor(Color.green);
            g.setFont(new Font(null,Font.ITALIC,100));
            g.drawString("game over",WINDOW_W/2-220,WINDOW_H/2);
        }
    }

    /**
     * 游戏是否结束
     * 1.
     * 2.
     * @return true 游戏结束    false 未结束
     */
    public void isOver(){
        Node head = body[0];
        //边界
        if (head.getX()<=0 || head.getX()>=WINDOW_W-20 || head.getY()<=20 || head.getY()>=WINDOW_H-20){
            isRunning = false;
        }

        //身体
        for (int i=1; i<body.length; i++){
            if (head.equals(body[i])){
                isRunning = false;
            }
        }
    }

    /**
     * 吃到食物了,增加蛇的身体
     *    将食物变成蛇的头部
     * 食物重新产生
     *    食物消失,重生成
     */
    public void addBody(){
        Node[] temp = new Node[body.length+1];
        temp[0] = food;
        System.arraycopy(body,0,temp,1,body.length);
        body = temp;
        food = null;
        productFood();
    }

    /**
     * 是否吃到食物:下一步的头节点的位置刚好和食物的位置相等
     * @return
     */
    public boolean isEat(){
        Node newNode = getNewHead();
        if (newNode.equals(food)){
            return true;
        }

        return false;
    }

    /**
     * 通过修改坐标达到移动的效果
     */
    public void remove(){
        //向右移动
        //每一次只需要给一个新的头节点,去掉尾节点即可
        Node[] temp = new Node[body.length];
        temp[0] = getNewHead();
        //去掉尾部
        System.arraycopy(body,0,temp,1,body.length-1);
        this.body = temp;
    }

    /**
     * 随机产生食物
     */
    public void productFood(){
        food = new Node(randomNum(0,WINDOW_W/20-1)*20, randomNum(2,WINDOW_H/20-1)*20);
    }

    /**
     * 根据方向来确定一个新的头节点
     * @return
     */
    public Node getNewHead(){
        Node newNode = body[0];
        switch (this.direction){
            //上 1,下 3,右 0,左 2
            case 0: //右
                newNode = new Node(newNode.getX()+20,newNode.getY());
                break;
            case 2: //左
                newNode = new Node(newNode.getX()-20,newNode.getY());
                break;
            case 1: //上
                newNode = new Node(newNode.getX(),newNode.getY()-20);
                break;
            case 3: //下
                newNode = new Node(newNode.getX(),newNode.getY()+20);
                break;
        }
        return newNode;
    }

    /**
     * 手动一直重绘窗体
     */
    public void start(){
        while (isRunning){
            super.repaint();  //手动让窗体重绘
            try {
                Thread.sleep(500);  //暂停事件
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        super.repaint();
    }

    /**
     * 生成指定区间的随机整数
     * @param min
     * @param max
     * @return
     */
    public static int randomNum(int min, int max){
        return (int)(Math.random()*(max-min+1) + min);
    }

    public static void main(String[] args) {
        new SnakeFrame08();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值