JavaGUI 19 动态小蛇(Timer)

8.3 动态小蛇


8.3.1 监听空格(开始游戏)

这次我们采取 将键盘事件 被 我们 GamePanel 实现的方式,就不 创建个 新的 事件类了。

package com.muquanyu.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class GamePanel extends JPanel implements KeyListener {

    //蛇的长度
    int length;
    //蛇头的 X坐标 和 Y坐标
    int[] snakeX = new int[900];
    int[] snakeY = new int[900];
    //蛇头方向
    String snakeDirection = null;
    //游戏当前的状态:开始、停止 默认为 "停止"
    boolean isStart = false;

    //初始化
    public void init() {
        //初始化蛇的长度
        length = 3;
        //初始化蛇头 坐标
        snakeX[0] = 125;snakeY[0] = 100;
        //初始化蛇身 坐标
        snakeX[1] = 100;snakeY[1] = 100;
        snakeX[2] = 75;snakeY[2] = 100;
        //蛇头方向
        snakeDirection = "right";

    }

    //绘制面板,我们游戏中的所有东西,都是用画笔来 画
    @Override
    public void paintComponent(Graphics g) {
        //清屏,不会出现闪烁。
        super.paintComponent(g);
        //绘制静态的面板
        this.setBackground(Color.BLACK);
        //第一个参数是画到哪个设备上,那肯定是我们 创建的 面板上呀!
        Data.header.paintIcon(this, g, 25, 11);
        //画游戏区域
        g.fillRect(25, 75, 850, 600);

        //画静态小蛇
        switch(snakeDirection)
        {
            case "right":
                Data.right.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            case "left":
                Data.left.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            case "up":
                Data.up.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            case "down":
                Data.down.paintIcon(this, g, snakeX[0], snakeY[0]);
                break;
            default:break;
        }

        for(int i = 1;i<length;++i)
        {
            Data.body.paintIcon(this, g, snakeX[i], snakeY[i]);
        }

        //游戏状态
        if(!isStart)
        {
            g.setColor(Color.white);
            //设置字体
            Font 微软雅黑 = new Font("微软雅黑", Font.BOLD, 40);
            g.setFont(微软雅黑);
            g.drawString("按下空格开始游戏",300,300);
        }



    }

    public GamePanel() {
            init();
            //获得焦点和键盘事件
        this.setFocusable(true);//获得焦点事件
        this.addKeyListener(this);//获得键盘监听事件

    }

    //键盘监听事件
    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if(keyCode == KeyEvent.VK_SPACE)
        {
            System.out.println("按下了空格!");
            isStart = !isStart;
            repaint();//开始重画
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}

我们知道,只要我们 把 isStart 设定为 true,在绘画 的时候,它就不会 去 绘画 提示的文字。那么 我们 通过 监听 空格键,把 isStart 取反,再进行 重画,更新所有的画面!不就实现了 游戏开始 和 停止的操作吗?

这里如果 懂的话,其实 让小蛇 动起来的操作,理解起来就不是很难了。无非就是 更新 小蛇的数据然后用 Timer 一直进行重画!!!


8.3.2 Timer 定时器(时钟)

使用定时器之前,就必须 先 继承或作为 Actionlistener 的实现。这是 为 了 Timer 做前奏准备。因为 我们 需要 用 Actionlistener 做最基本的监听事件!

  • 创建 Timer 对象(定时器)
//创建一个时钟 进行 监听
    Timer timer = new Timer(33,监听事件);

它是以毫秒为单位,进行监听的!1000ms = 1s。

所以要想模仿出 30 帧,那就得 33 ms 监听一次!

需要注意的是,Timer 开启后,默认 会去 监听 你一开始创建的时候,设定的那个 监听事件!这个监听事件可以是 很多组件的监听事件。


8.3.2.1 自动识别内容是否正确(不需要 按 回车键)

package com.muquanyu.snake;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class TimerDemo extends JFrame {
    MyAction m = new MyAction();
    Timer t = new Timer(300,m);
    JTextField jTextField;
    public TimerDemo()
    {
        setBounds(200,200,500,500);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        jTextField = new JTextField("请输入密钥,我们会一直判定!");
        jTextField.addActionListener(m);

        add(jTextField);

        t.start();


        setVisible(true);



    }
    public static void main(String[] args) {
        new TimerDemo();
    }

    class MyAction implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {

            if(jTextField.getText().equals("helloworld"))
            {
                System.out.println("输入密钥正确!");
                t.stop();
            }
            else{
                System.out.println("输入密钥错误!");
            }
        }
    }
}

在这里插入图片描述

这个 案例,是为了让你明不白,Timer 对象 一旦被创造 出来,就可以 捆绑 一个 事件。然后 一旦开启,就会一直 执行那个 事件的监听。

如果是这样的话,我们是否可以 把 GamePanel 的 action 事件 绑定 在 上面呢?? 完全是可以的!

这样就能在 Action 事件里,进行 数据的刷新和重画了。


8.3.2.2 实现动态小蛇

@Override
    public void actionPerformed(ActionEvent e) {
        //如果游戏开始的话
        if(isStart)
        {
            //宽度:850 高度:600
            //右移
            //头移动
            switch(snakeDirection)
            {
                case "right":
                {
                    if(snakeX[0] == 850)
                    {
                        //那就直接让它 从左边 穿过来就行了
                        snakeX[0] = 25;
                    }
                    else{
                        //编程思想的写法,并不是什么高深算法!
                        //我们通过自己画图,也会发现这个 现象
                        //就是 蛇身的每一块 在移动的时候,恰好移动到了 前一个块的坐标!!!
                        //你们 自己画图 观察,就能看出来!那么我们只需要 从 最后一块 开始遍历
                        //然后 让它每次刷新 都 变成 前一块的 坐标不就完事了!!
                        for(int i = length-1;i>0;i--)
                        {
                            snakeX[i] = snakeX[i-1];
                            snakeY[i] = snakeY[i-1];
                        }
                        snakeX[0] = snakeX[0] + 25;
                    }
                    repaint();
                    break;
                }
                case "left":
                {
                    if(snakeX[0] == 25)
                    {
                        snakeX[0] = 850;
                    }
                    else{
                        for(int i = length-1;i>0;i--)
                            {
                                snakeX[i] = snakeX[i-1];
                                snakeY[i] = snakeY[i-1];
                            }
                        snakeX[0] = snakeX[0] - 25;
                    }
                    repaint();
                    break;
                }
                case "up":
                {
                    if(snakeY[0] == 75)
                    {
                        snakeY[0] = 600;
                    }
                    else{
                        for(int i = length-1;i>0;i--)
                        {
                            snakeX[i] = snakeX[i-1];
                            snakeY[i] = snakeY[i-1];
                        }
                        snakeY[0] = snakeY[0] - 25;
                    }
                    repaint();
                    break;
                }
                case "down":
                {
                    if(snakeY[0] == 650)
                    {
                        snakeY[0] = 75;
                    }
                    else{
                        for(int i = length-1;i>0;i--)
                        {
                            snakeX[i] = snakeX[i-1];
                            snakeY[i] = snakeY[i-1];
                        }
                        snakeY[0] = snakeY[0] + 25;
                    }
                    repaint();
                    break;
                }
                default:break;
            }

编程思想的写法,并不是什么高深算法!
我们通过自己画图,也会发现这个 现象
就是 蛇身的每一块 在移动的时候,恰好移动到了 前一个块的坐标!!!你们 自己画图 观察,就能看出来!那么我们只需要 从 最后一块 开始遍历
然后 让它每次刷新 都 变成 前一块的 坐标不就完事了!!

在每次 进行刷新判定的时候,都要 重画哟!

在这里插入图片描述

8.3.3 实现方向控制

  • 实现方向控制
 @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if(keyCode == KeyEvent.VK_SPACE)
        {
            System.out.println("按下了空格!");
            isStart = !isStart;
            repaint();//开始重画
        }
        if(keyCode == KeyEvent.VK_UP)
        {
            System.out.println("上方向键!");
            snakeDirection = "up";
        }
        if(keyCode == KeyEvent.VK_DOWN)
        {
            System.out.println("下方向键!");
            snakeDirection = "down";
        }
        if(keyCode == KeyEvent.VK_LEFT)
        {
            System.out.println("左方向键!");
            snakeDirection = "left";
        }
        if(keyCode == KeyEvent.VK_RIGHT)
        {
            System.out.println("右方向键!");
            snakeDirection = "right";
        }

    }

直接改变 snakeDirection 的值,就完事了,因为你改变了这个值,我们的 action 事件在 更新数据的时候 就会 不一样。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值