Java Swing模拟水波纹扩散效果动画

简介: Java Swing模拟水波纹扩散效果动画

基于Java语言模拟水波纹运动效果,分为两种方法,一种采用简单的叠加计算

不使用sine函数模拟水波纹,好处是计算量小,另外一种采用sine函数来计算

水波纹扩展,计算量大,但是效果比较真实。

第一种简单的叠加效果水波模拟,是很多简单的2D游戏中会用的,关键是计

算水波的迁移,然后剩以能量衰减因子。Java实现的代码如下:

for(y=1; y<waveWidth-1; y++)
{
  for(x= 1; x<waveHeight-1; x++)
  {
    int n = (waveMapData[current][y-1][x] + 
        waveMapData[current][y+1][x] + 
        waveMapData[current][y][x+1] +
        waveMapData[current][y][x-1])/2 -
        waveMapData[before][y][x];
    // energy lost
    n = n - n / damp;
    /*if(Math.abs(n) >= Math.abs(-100))
    {
      n = n % Math.abs(-100);
    }*/
    waveMapData[next][y][x] = n;
    // System.out.print(" " + n);
  }
  // System.out.println();
}

解释:

主要是利用前两个水波的位置,计算出下一个将要出现的水波的位置,因此非常


重要的是保存前两个水波的迁移信息,然后实现波的叠加计算,在加上能量损失


因子,即可获得下个波的位置。


这种方法模拟出来的水波不是很圆,如果想得到那种很圆的水波效果,建议看


这里:http://blog.csdn.net/jia20003/article/details/13159535


解释完水波模拟的原理部分,下面来说说JAVA Swing的动画原理


主要是利用一个线程来计算水波的新位置,然后重新绘制图片,图片绘制好


以后调用repaint()方法,自动触发Swing重绘机制,实现刷新显示效果。

水波计算线程代码如下:

Thread animationThread = new Thread () {
    @Override
    public void run() {
      int k = 0;
       while (true) {
        startWaterWave();
        // sinWaterRipple();
        updateText("水波开展:" + k);
          repaint();  // Refresh the display
          try {
             Thread.sleep(1000 / 30); // delay and yield to other threads
          } catch (InterruptedException ex) { }
          k++;
          if(k>=100){
            break;
          }
       }
    }
 };
 animationThread.start();

最后说一下Java的一个数据Copy的函数System.arraycopy,如果在多维数据中交

换数据你可能发现由于它是指针交换,交换完以后对新的数组赋值会同时跑到旧的

里面去。这个是我在debug程序的时候才发现。惭愧啊!程序效果如下:

UI加水波计算的完全源代码如下:

package com.gloomyfish.water.ripple.study;
 
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
 
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
 
import com.gloomyfish.filter.study.WaterFilter;
 
public class MyDemoUI extends JFrame implements ActionListener {
 
  /**
   * 
   */
  private static final long serialVersionUID = -3702707300863726479L;
  private BufferedImage textureImg = null;
  private BufferedImage resultImg = null;
  private int waveWidth = 40;
  private int waveHeight = 40;
  private int damp = 16;
  private int[][][] waveMapData;
  private int before = 0, current = 1, next = 2;
  private int x = 1, y = 1 /*n = 0*/;
  private WaterPixelRender imageRender;
  private WaterFilter filter;
  private JButton runBtn;
  private JLabel txtLabel;
  private boolean text = false;
  public MyDemoUI()
  {
    super("Water Ripple");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
        getContentPane().setLayout(new BorderLayout());  
          
        // Display the window.  
        getContentPane().add(createWaterPanel(), BorderLayout.CENTER);  
        runBtn = new JButton("Run it");
        runBtn.addActionListener(this);
        
        txtLabel = new JLabel();
        txtLabel.setText("水纹:0");
        getContentPane().add(runBtn, BorderLayout.SOUTH);
        getContentPane().add(txtLabel, BorderLayout.NORTH);
        setPreferredSize(new Dimension(300 + 25,500));  
        pack();  
        setVisible(true);  
  }
  
  public void updateText(String textContent)
  {
    txtLabel.setText(textContent);
    this.invalidate();
  }
 
  private JPanel createWaterPanel() {
    try {
      File file = new File("D:\\resource\\flower_001.png");
      textureImg = ImageIO.read(file);
      resultImg = textureImg;
      
    } catch (Exception e) {
      e.printStackTrace();
    }
    imageRender = new WaterPixelRender();
    filter = new WaterFilter();
    JPanel wPanel = new JPanel()
    {
      protected void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        if (resultImg != null) {
          g2.drawImage(resultImg, 0, 0, resultImg.getWidth(), resultImg.getHeight(), null);
        }
      }
    };
    return wPanel;
  }
  
  public void swapData()
  {
    for(int i=0; i<waveWidth; i++)
    {
      for(int j=0; j<waveHeight; j++)
      {
        waveMapData[before][i][j] = waveMapData[current][i][j];
        waveMapData[current][i][j] = waveMapData[next][i][j];
      }
    }
  }
  
  public void simulateWaterRipple()
  {
    x = 1;
    y = 1;
    //n = 0;
    waveMapData = new int[3][textureImg.getHeight()][textureImg.getWidth()];
    waveWidth = textureImg.getHeight();
    waveHeight = textureImg.getWidth();
    for(int i=0; i<waveWidth; i++)
    {
      for(int j=0; j<waveHeight; j++)
      {
        waveMapData[next][i][j] = 0;
        waveMapData[current][i][j] = 0;
        waveMapData[before][i][j] = 0;
      }
    }
    waveMapData[current][waveWidth/2][waveHeight/2] = -300;
    Thread animationThread = new Thread () {
        @Override
        public void run() {
          int k = 0;
           while (true) {
            startWaterWave();
            // sinWaterRipple();
            updateText("水波开展:" + k);
              repaint();  // Refresh the display
              try {
                 Thread.sleep(1000 / 30); // delay and yield to other threads
              } catch (InterruptedException ex) { }
              k++;
              if(k>=100){
                break;
              }
           }
        }
     };
     animationThread.start();
  }
    
  protected void sinWaterRipple() {
    resultImg = filter.filter(textureImg, null);
    filter.setRadius(filter.getRadius() + 5);
    if(filter.getRadius() > 150)
    {
      filter.setRadius(50);
    }
    float wl = filter.getWavelength();
    if(wl > 4)
    {
      wl = wl / 2;
    }
    else
    {
      wl = 32;
    }
    filter.setWavelength(wl);
  }
 
  protected void startWaterWave(/*int sx, int sy, Graphics2D g2*/) 
  {
    for(y=1; y<waveWidth-1; y++)
    {
      for(x= 1; x<waveHeight-1; x++)
      {
        int n = (waveMapData[current][y-1][x] + 
            waveMapData[current][y+1][x] + 
            waveMapData[current][y][x+1] +
            waveMapData[current][y][x-1])/2 -
            waveMapData[before][y][x];
        // energy lost
        n = n - n / damp;
//        if(Math.abs(n) >= Math.abs(-100))
//        {
//          n = n % Math.abs(-100);
//        }
        waveMapData[next][y][x] = n;
        // System.out.print(" " + n);
      }
      // System.out.println();
    }
    // render image pixel and display water ripple at here
    imageRender.setWaveData(waveMapData[next]);
    resultImg = imageRender.filter(textureImg, null);
    // prepare for the next water ripple
    // Java System.arraycopy give very bad result due to reference issue!!!!
    // System.arraycopy(waveMapData[next], 0, waveMapData[current], 0, waveMapData[next].length);
    swapData();
  }
 
  public static void main(String[] args)
  {
    new MyDemoUI();
  }
 
  @Override
  public void actionPerformed(ActionEvent e) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run(){
        simulateWaterRipple();    
      }
    });   
  }
}

绘制水波像素的生产新图片的代码如下:

package com.gloomyfish.water.ripple.study;
 
import java.awt.image.BufferedImage;
 
import com.gloomyfish.filter.study.AbstractBufferedImageOp;
 
 
public class WaterPixelRender extends AbstractBufferedImageOp {
 
  private int[][] waveData;
  private double rIndex = 2.0;
  private int counter = 0;
  public WaterPixelRender()
  {
    
  }
  
  public int[][] getWaveData() {
    return waveData;
  }
  public void setWaveData(int[][] waveData) {
    this.waveData = waveData;
  }
  @Override
  public BufferedImage filter(BufferedImage src, BufferedImage dest) {
    counter++;
    int width = src.getWidth();
        int height = src.getHeight();
 
        if ( dest == null )
          dest = createCompatibleDestImage( src, null );
 
        int[] inPixels = new int[width*height];
        int[] outPixels = new int[width*height];
        getRGB( src, 0, 0, width, height, inPixels );
        int index = 0;
        int nrow = 0, ncol = 0;
        for(int row=0; row<height-1; row++) {
          int ta = 0, tr = 0, tg = 0, tb = 0;
          for(int col=0; col<width-1; col++) {
            
                int xDiff = waveData[row+1][col] - waveData[row][col];
              int yDiff = waveData[row][col+1] - waveData[row][col];
              double xAngle = Math.atan(xDiff);
              double xRefraction = Math.asin( Math.sin( xAngle ) / rIndex );
              double xDisplace = Math.tan( xRefraction ) * xDiff ;
 
              double yAngle = Math.atan( yDiff );
              double yRefraction = Math.asin( Math.sin( yAngle ) / rIndex );
              double yDisplace =Math.tan(yRefraction ) * yDiff;
 
              if(xDiff < 0) 
              {
                if(yDiff < 0)
                {
                  nrow = (int)(row - xDisplace);
                  ncol = (int)(col - yDisplace);
                }
                else
                {
                  nrow = (int)(row - xDisplace);
                  ncol = (int)(col + yDisplace);
                }
              }
              else
              {
                if(yDiff < 0)
                {
                  nrow = (int)(row + xDisplace);
                  ncol = (int)(col - yDisplace);
                }
                else
                {
                  nrow = (int)(row + xDisplace);
                  ncol = (int)(col + yDisplace);
                }
              }
              if(nrow < 0 || nrow >= height)
              {
                nrow = 0;
              }
              if(ncol < 0 || ncol >= width)
              {
                ncol = 0;
              }
              
              
            index = nrow * width + ncol;
            ta = (inPixels[index] >> 24) & 0xff;
                tr = (inPixels[index] >> 16) & 0xff;
                tg = (inPixels[index] >> 8) & 0xff;
                tb = inPixels[index] & 0xff;
                index = row * width + col;
                outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;
          }
        }
        setRGB( dest, 0, 0, width, height, outPixels );
        return dest;
  }
}

复制代码以后替换图片路径即可运行,转载请注明出处

相关文章
|
8天前
|
Java 数据安全/隐私保护
利用Java图形化界面组件Swing写一个简易的登录界面
利用Java图形化界面组件Swing写一个简易的登录界面
22 1
|
15天前
|
Java Windows
基于java Swing编写扫雷游戏设计实现(已调试)
基于java Swing编写扫雷游戏设计实现(已调试)
24 1
|
22天前
|
Java
JAVA Swing自定义JScorllPanel
JAVA Swing自定义JScorllPanel
13 1
|
2天前
|
前端开发 Java UED
Java中的图形用户界面编程:Swing与JavaFX的比较与应用
Java中的图形用户界面编程:Swing与JavaFX的比较与应用
|
23天前
|
Java
【Gloomyfish】Java Swing 实现实际大小到全屏切换
【Gloomyfish】Java Swing 实现实际大小到全屏切换
13 1
|
15天前
|
存储 设计模式 前端开发
[笔记] 疯狂JAVA讲义(第3版)第12章 Swing编程
[笔记] 疯狂JAVA讲义(第3版)第12章 Swing编程
|
23天前
|
Java
Java Swing 层次面板(JLayeredPanel) Demo
Java Swing 层次面板(JLayeredPanel) Demo
12 0
|
23天前
|
缓存 Java
Java Swing 2D系统属性参数详解
Java Swing 2D系统属性参数详解
18 0
|
1月前
|
设计模式 Java 开发者
Java一分钟之-Swing组件:JTable, JTree, JTextArea
本文介绍了Java Swing的三个关键组件:`JTable`、`JTree`和`JTextArea`,用于数据展示和用户输入。`JTable`展示二维数据,如表格;`JTree`展示层次结构数据,如文件系统;`JTextArea`则用于多行文本输入和显示。每个组件都提供了示例代码,并列出常见问题及避免方法,如数据源未设置、滚动面板缺失等。理解并掌握这些组件,能帮助开发者创建高效用户界面。
40 0
|
1天前
|
监控 安全 算法
如何有效地处理Java中的多线程
如何有效地处理Java中的多线程