awt绘图_弹球小游戏
原理:小球和球拍不断重绘repain,重绘的频次足够高就变成了动画。标准来说就是间隔一定的时间就不断重新调用组件的repain方法
Java也可以用来开发一些动画。所谓动画,就是间隔一定的时间(通常小于0.1秒)重新绘制的图形,两次绘制的图像之间差异比较小,肉眼看起来就成为所谓的动画
为了实现间隔一定的时间就不断重新调用组件的repain方法,可以借助Swing提供的Timer类,Timer类是一个定时器,它有如下一个构造器:
Timer(int delay,ActionListener):每间隔delay毫秒,系统自动触发ActionLinster监听器的事件处理器的方法,在方法内部我们就可以调用组件的repaint方法,完成组件重新绘制
xxxxxxxxxx
package ch29;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class a_7_2awt_绘图_弹球小游戏 {
private Frame frame = new Frame("弹球游戏");
//注意下面的几个被Final修饰的成员变量是常量,不是变量
//设置桌面宽度
private final int TABLE_WIDTH = 300;//中间画布,即小球的活动窗口的宽度
//设置桌面高度
private final int TABLE_HEIGHT = 400;//中间画布,即小球的活动窗口的高度
//球拍的高度和宽度
private final int RACKET_WIDTH = 100;//球拍是矩形的,即定义了底部移动的球拍的宽度
private final int RACKET_HEIGHT = 10;//球拍是矩形的,即定义了底部移动的球拍的高度
//小球的大小
private final int BALL_SIZE = 16;//小球的直径是16px
//定义变量,记录小球的坐标。注意0,0的点是在整个Frame视图的左上角。坐标的单位是像素
private int ballX = 120;
private int ballY = 20;
//x和y就能形成一个坐标,即是小球的初始位置。后续会由这两个变量来记录小球的新位置
//定义变量,记录小球在x和y方向上分别移动的速度
private int speedY = 10;//每次移动10px
private int speedX = 5;//每次移动5px
//小球在x方向和y方向的和速度就是小球的移动速度
//定义变量,记录球拍的坐标
private int racketX = 120;
private final int racketY = 340;//注意这个变量是被Final修饰的,即表示是常量,常量是不可改变的,也就是球拍只能左右移动不能上下移动
//x和y就能形成一个坐标,即是球拍的初始位置。后续会由这两个变量来记录球拍的新位置
//定义变量,标识当前游戏是否已结束
private boolean isOver = false;//默认是未结束的,即默认是可以玩的
//声明一个定时器
private Timer timer;
//小球和球拍是在画布上的。如何定义画布,即自定义一个类,继承canvas,就可以有画布了
private class Mycanvas extends Canvas {
//记得重写paint
public void paint(Graphics g) {//重写paint方法,作用是在画布里面绘制内容
//TODO 在这里绘制内容
if (isOver) {
//游戏结束的内容
g.setColor(Color.RED);//设置字体颜色为红色
g.setFont(new Font("Times", Font.BOLD, 30));//标题,字体、字号
g.drawString("游戏结束!", 75, 200);
} else {
//游戏进行的内容
//绘制小球
g.setColor(Color.BLUE);//小球的填充颜色
g.fillOval(ballX, ballY, BALL_SIZE, BALL_SIZE);
//标、绘制球拍
g.setColor(Color.PINK);
g.fillRect(racketX, racketY, RACKET_WIDTH, RACKET_HEIGHT);//x坐标、y坐、宽、高
}
}
}
//创建我们上面自定义类的对象,作用是创建绘画区域
Mycanvas drawArea = new Mycanvas();
//组件组装方法
public void init() {
//组装视图,以及游戏逻辑的控制
//1、球拍的控制逻辑
//2、小球的控制逻辑
//完成球拍坐标的变化,包含创建监听器、按键事件、键盘事件
KeyListener listener = new KeyAdapter() {
public void keyPressed(KeyEvent e) {
//获取当前按下的键
int keyCode = e.getKeyCode();//我们的键盘每一个键位都对应一个整数keycode
if (keyCode == KeyEvent.VK_LEFT) {//VK_LEFT是左箭头
//应该向左移动
if (racketX > 0) {//大于0,即当向左移动时,不能移除到左边框外
racketX -= 30;
}
}
if (keyCode == KeyEvent.VK_RIGHT) {//VK_RIGHT是右箭头
//应该向右移动
if (racketX < (TABLE_WIDTH - RACKET_WIDTH)) {//左移的距离<桌面宽减球拍宽。即当向右移动时,不能移除到右边框外
racketX += 30;
}
}
}
};
//给frame和drawArea注册监听器
frame.addKeyListener(listener);
drawArea.addKeyListener(listener);
//小球坐标的控制
ActionListener task = new ActionListener() {
//更新小球的坐标,重绘界面
public void actionPerformed(ActionEvent e) {
//根据边界范围,修正速度
//----->修正x轴弹出碰到边界的方向
if (ballX <= 0 || ballX >= (TABLE_WIDTH - BALL_SIZE)) {//如果球的x位置超出左边框|超出有边框,就反方向运动
speedX = -speedX;//x方向上做反方向运动
}
//----->修正y轴弹出碰到边界的方向。当y速度超出上边框|球y位置大于球拍y位置&&球的x位置大于球拍的x位置&&球的x位置小于球拍
if (ballY <= 0 || ballY > racketY - BALL_SIZE && ballX > racketX && ballX < racketX + RACKET_WIDTH) {
speedY = -speedY;
}
//当前小球超出了球拍范围。游戏结束
if (ballY > racketY - BALL_SIZE && (ballX < racketX || ballX > racketX + RACKET_WIDTH)) {
//停止定时器
timer.stop();
//修改游戏是否结束标记
isOver = true;//游戏结束的标记
//重新绘制界面
drawArea.repaint();
}
ballX += speedX;
ballY += speedY;
//重新绘制界面
drawArea.repaint();
}
};
//这里需要手动导包import javax.swing.Timer.*;
timer = new Timer(80,task);//多少毫秒执行一次task,task是小球坐标的控制
timer.start();//启动定时器
drawArea.setPreferredSize(new Dimension(TABLE_WIDTH, TABLE_HEIGHT));//给画布设置一个大小
frame.add(drawArea);//将画布添加到Frame容器里面
//frame窗口关闭
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
//frame最佳大小
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new a_7_2awt_绘图_弹球小游戏().init();
}
}