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 等格式.

原文地址:https://www.cnblogs.com/moveofgod/p/12586122.html