zoukankan      html  css  js  c++  java
  • Basic

    • 动作事件
    • 鼠标事件
    • 键盘事件

    一般事件类处于 java.awt.event 包中.

    动作事件 ActionListener 接口

    动作事件由 ActionEvent 类定义, 最常用的是单击按钮后产生的动作事件, 可以通过 ActionListener 接口的类处理相应的动作事件.

    ActionListener 接口只有一个抽象方法,将在动作发生后触发

    b1.addActionListener(new Trigger());   首先需要给要监听的对象, 比如 b1 是一个 button 添加监听对象, 而监听对象 Trigger 就是当这个事件被监听到事件时执行的代码.

    鼠标事件 MouseListener 接口

    这个接口有一下几个函数

    mouseEntered(MouseEvent e)  鼠标进入组件,鼠标按下,鼠标释放,鼠标单击,鼠标移出组件

    getSource() 获得触发事件的对象

    getButton() 代表鼠标按下的类型, BUTTON1 代表左键, BUTTON2 代表鼠标滚轮, BUTTON3 右键

    getClickCount() 获得单击按键次数

    键盘事件 KeyListener 接口

    也有一些方法, 用时在说}]

     

    清除绘制的图形

    clearRect(int x, int y, int width, int height)

    动画基础

    帧:  每一帧就是一副静态图像.

    帧频: 每秒中的帧数, 一般电影是 24 帧.

    通过循环播放静态图像(帧), 来显示动画运行过程. 所以, 一般的动画处理形式是:

    while (true) {
        // 处理游戏功能;
        // 使用 repaint() 函数要求重画屏幕
        // 暂停一小段时间    
    }

    消除动画闪烁:

    一个动画在运行的时候, 如果去向的切换是在屏幕上完成的, 则可能会造成屏幕的闪烁, 消除动画闪烁的最佳方法: 双缓冲技术.

    双缓存技术是在屏幕外做一个图像缓冲区,事先在这个缓冲区绘制图像,然后再将这个图像送到屏幕上去, 但是这个缓冲区需要占用一定的内存资源, 特别是图像比较大时,占用非常严重,所以需要考虑动画的质量和运行速度之间的重要性,有选择的进行开发。

    屏幕闪烁的原因: 是先用背景色覆盖组件再重绘图像的方式导致了闪烁。即使时间很多,但是重绘的面积较大,花去的事件也比较可观的,这个时间甚至可以大到足以让闪烁严重到让人无法忍受的地步。

    双缓冲: 先在内存中分配一个和我们动画窗口一样大的空间(在内存中的空间我们是看不到的), 然后利用 getGraphics()方法区获取双缓冲画笔, 接着利用双缓冲画笔给空间我们想画的东西, 最后将它全部一次性的显示到屏幕上, 这样我们动画窗口上显示就非常流畅了.

    一般采用重载 paint(Graphics g) 实现双缓冲. 

    package com.leon.game;
    
    import java.awt.Color;
    import java.awt.Container;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class DownBall extends JFrame {
    
        public DownBall() {
            this.setTitle("test game");
            Container c = this.getContentPane();
            c.add(new TetrisPanel());
            this.setBounds(400, 200, 300, 300);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setResizable(false);
            this.setVisible(true);
        }
        
        public static void main(String[] args) {
            DownBall db = new DownBall();
            db.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            });
    
        }
    }
    
    class TetrisPanel extends JPanel implements Runnable {
    
        public int ypos = -80;
        private Image iBuffer;
        private Graphics gBuffer;
        
        public TetrisPanel() {
            
            // 创建进程, 随后直接启动, 进程一定要接 Runnable 对象作为参数
            Thread t = new Thread(this);
            t.start();
        }
        
        public void paint(Graphics g) {
            // 创建缓冲区
            if (iBuffer == null) {
                iBuffer = createImage(this.getSize().width, this.getSize().height);
                // 双缓冲画笔
                gBuffer = iBuffer.getGraphics();
            }
            gBuffer.setColor(getBackground());
            gBuffer.fillRect(0, 0, this.getSize().width, this.getSize().height);
            gBuffer.setColor(Color.red);
            gBuffer.fillOval(90, ypos, 80, 80);
            
            g.drawImage(iBuffer, 0, 0, this);
        }
        
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ypos += 5;
                if (ypos > 300)
                    ypos = -80;
                // 调用本 component 的 paint 方法.
                repaint();
            }
        }    
    }

    定时器

    游戏开发中, 经常用到, Timer 组件. javax.swing.Timer 包.

    Timer(int delay, ActionListener Listener);  构造方法, 参数 listener 用于指定一个接收该计时器操作事件的监听器, 如果要处理这个事件, 就必须实现 ActionListener 接口类以及接口类中的 actionPerformed() 方法, Timer 组件类中的主要方法有:

    void start(): 激活 Timer对象,

      比如 timer = new Timer(500, this);   // 此时 timer 并未开始工作,   timer.start();  // 此时开始计时, 500毫秒后就会触发 timer 事件.

    void stop(): 停止 Timer对象

    void restart(): 重新激活 Timer对象

    碰撞检测

    矩形碰撞用的最多

    方法1: 判断相交

    public boolean isCollidingWith(int px, int py) {
       if ((px > getX() && px < getX() + getActorWidth())
        && (py > getY() && py < getY() + getActorHeight())) {
            return true;
        }
        return false;
    }

    方法2: 判断不相交

        /**
         * ax - a 矩形左上角 x 坐标
         * ay - a 矩形左上角 y 坐标
         * aw - a 矩形宽度
         * ah - a 矩形高度
         * bx - b 矩形左上角 x 坐标
         * by - b 矩形左上角 y 坐标
         * bw - b 矩形宽度
         * bh - b 矩形高度
         */
        public boolean isColliding(int ax, int ay, int aw, int ah,
                                   int bx, int by, int bw, int bh) {
            if (ay > by + bh || by > ay + ah
                    || ax > bx + bw || bx > ax + aw)
                return false;
            else 
                return true;
        }

    对第二种进行改进: 把图形1 和 图形2 的左上角(x, y) 和右下角 (x, y) 分别存储, 进行判断

    /**
         * rect1[0]: 矩形1左上角 x 坐标
         * rect1[1]: 矩形1左上角 y 坐标
         * rect1[2]: 矩形1右下角 x 坐标
         * rect1[3]: 矩形1右下角 y 坐标
         * rect2[0]: 矩形2左上角 x 坐标
         * rect2[1]: 矩形2左上角 y 坐标
         * rect2[2]: 矩形2右下角 x 坐标
         * rect2[3]: 矩形2右下角 y 坐标
         */
        public boolean isColliding2(int rect1[], int rect2[]) {
            if (rect1[0] > rect2[2]) return false;
            if (rect1[2] < rect2[0]) return false;
            if (rect1[1] > rect2[3]) return false;
            if (rect1[3] < rect2[1]) return false;
            return true;
        }

    圆形碰撞: 如果两个圆形之间的举例小于它们两个的半径的和, 那就发生了碰撞.

    /**
         * ax - a 圆形左上角 x 坐标
         * ay - a 圆形左上角 y 坐标
         * aw - a 圆形宽度
         * ah - a 圆形高度
         * bx - b 圆形左上角 x 坐标
         * by - b 圆形左上角 y 坐标
         * bw - b 圆形宽度
         * bh - b 圆形高度
         */
        public boolean isColliding(int ax, int ay, int aw, int ah,
                                   int bx, int by, int bw, int bh) {
            int r1 = (Math.max(aw, ah)/2 + 1);
            int r2 = (Math.max(bw, bh)/2 + 1);
            int rSquard = r1 * r1;
            int anrSquard = r2 * r2;
            int disX = ax - bx;
            int disY = ay - by;
            if ((disX * disX) + (disY * disY) < (rSquard + anrSquard))
                return true;
            else
                return false;
        }

    游戏中的声音

    声音跟图像一样, 也是通过字节流加载,  主要分为动作声音和背景声音,  javax.sound.sampled 可以播放声音, 支持 AIFF, AU, WAV 等格式.

  • 相关阅读:
    [cf1038E][欧拉路]
    [最小费用最大流(板子)]
    [网络流24题]
    [ACM International Collegiate Programming Contest, Amman Collegiate Programming Contest (2018)]
    [Split The Tree][dfs序+树状数组求区间数的种数]
    [CSL 的魔法][求排序最少交换次数]
    [CSL 的字符串][栈,模拟]
    ZOJ 3949 Edge to the Root 树形DP
    第十三周 Leetcode 363. Max Sum of Rectangle No Larger Than K(HARD)
    POJ 2104 HDU 2665 主席树 解决区间第K大
  • 原文地址:https://www.cnblogs.com/moveofgod/p/12586122.html
Copyright © 2011-2022 走看看