重要申明:此程序来自《Java 图形与动画编程实例》这本书。
今天在整理电脑时候发现大学时写的代码,然后就统统看了一遍,就发现了一个比较有趣的程序,水波动画,感觉挺好玩于是整理整理放出来给大家看看,确实没什么实际的用处,刚学java swing的可以看看,了解下swing的paint和repaint机制,如何键盘控制物体在上面运动等,自己可以尝试做做看。
首先我们来看下面的效果图,点击任何一个地方就出现一个水波或者鼠标拖拽经过的路径都有水波。
编程思路
1、装载背景图片,如果没有背景图片或者图片所有点的像素都一样的话,将看不出水波特效。
2、鼠标点击或者拖拽,每经过的点都会产生一个水波源,石头形状大小范围的数组值修改为-128.
3、定义spread方法,该方法主要作用是根据数组buf1的值来计算buf2的值,计算公式第4步所叙,计算完毕后还需要衰减计算。
4、定义render方法,首先计算出两点的波幅之差,然后根据这个值计算出像素点的偏移位置,即可得到新的图像像素数组。
5、在paint方法中绘制图像,每绘制完一幅图之后需要将buf2的值附给buf1,再重复计算和渲染,渲染完成之后在进行绘制。
代码:
// 需要用到一张图片大小是640*640的
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.ImageProducer;
import java.awt.image.MemoryImageSource;
import java.awt.image.PixelGrabber;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
@SuppressWarnings("serial")
public class ImageWater extends JFrame {
int w, h;
int[] pixels;
int[] p;
Image im, im2;
int[] buf1, buf2, buftemp;
int i, j, k, x, y, posx, posy;
// 石头大小,越大水波越大
int stoneSize = 10;
int s = 10;
public ImageWater() throws Exception {
MediaTracker tracker = new MediaTracker(this);
im = new ImageIcon(getClass().getResource("11.png")).getImage();
tracker.waitForID(0);
w = im.getWidth(this);
h = im.getHeight(this);
pixels = new int[w * h];
p = new int[w * h];
buf1 = new int[w * h];
buf2 = new int[w * h];
buftemp = new int[w * h];
PixelGrabber pg = new PixelGrabber(im, 0, 0, w, h, pixels, 0, w);
pg.grabPixels();
for (k = 0; k < w * h - 2 * w; k++) {
buf1[k] = 0;
buf2[k] = 0;
}
// 鼠标单机事件,每点击一下,出现一个水波
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
wave(e.getX(), e.getY());
}
});
// 鼠标拖拽事件,拖拽经过的路径都有水波
this.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
wave(e.getX(), e.getY());
}
});
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(640, 640);
this.setResizable(false);
this.setVisible(true);
}
// 根据buf1的值计算buf2的值并返回
public int[] spread(int[] buf1) {
for (i = w; i < w * h - 2 * w; i++) {
buf2[i] = ((buf1[i - 1] + buf1[i + 1] + buf1[i - w] + buf1[i + w]) >> 1) - buf2[i];
buf2[i] -= buf2[i] >> 5;
}
return buf2;
}
// 对于任意一点,首先计算相邻两点的波幅只差,然后根据这个值计算像素点的位置偏移,根据这个偏移,从数组pixels中获得一个新的数组平且返回p
public int[] render(int[] buf1) {
int xoff, yoff;
int n = w, z;
for (j = 1; j < (h - 2) * w; j++) {
xoff = buf1[n - 1] - buf1[n + 1];
yoff = buf1[n - w] - buf1[n + w];
z = j + w * yoff + xoff;
if (z > 0 && z < w * h) {
p[j] = pixels[z];
}
n++;
}
return p;
}
/**
* 产生一个水波源
* @param x0
* @param y0
*/
private void wave(int x0, int y0) {
x = x0;
y = y0;
for (posx = x - stoneSize; posx < x + stoneSize; posx++) {
for (posy = y - stoneSize; posy < y + stoneSize; posy++) {
if ((posx - x) * (posx - x) + (posy - y) * (posy - y) < stoneSize * stoneSize) {
buf1[w * posy + posx] = -128;
}
}
}
}
/**
* 先调用spread方法,计算出新的buf2数组,然后将buf2赋给buf1,然后调用render方法,
* 根据buf1的值计算p,用p产生图像im2,形成新的一帧动画,重复上面的步骤,便连续播放动画
*/
@Override
public void paint(Graphics g) {
buf2 = spread(buf1);
for (i = 0; i < w * h; i++) {
buftemp[i] = buf1[i];
buf1[i] = buf2[i];
buf2[i] = buftemp[i];
}
p = render(buf1);
ImageProducer ip = new MemoryImageSource(w, h, p, 0, w);
im2 = createImage(ip);
g.drawImage(im2, 0, 0, this);
repaint();
}
public static void main(String[] args) throws Exception {
final ImageWater iw = new ImageWater();
final Random ran = new Random();
// new Thread(new Runnable() {
//
// @Override
// public void run() {
// while (true) {
// try {
// Thread.sleep(1);
// } catch (InterruptedException e) {
// }
// iw.stoneSize = ran.nextInt(10) + 1;
// iw.wave(ran.nextInt(600) + 20, ran.nextInt(600) + 20);
// }
// }
// }).start();
}
}