java基础 --- 简单的动画
这篇主要是写在Swing程序中如何制作动画的效果。我们都知道游戏动画主要是一些连续的图片通过快速播放,由于这些连续的图片变化不是很大,因此使我们的眼睛产生影象的残留,这样就能造成动画的效果。一般以每秒24帧的速度播放图片,那么我们就感觉不出图片的停顿。
在GUI的程序中,我们可以通过线程来控制播放的速度,通过绘制不同的图片来产生动画的效果。比如:
while(播放==true)
{
绘制当前图像;
暂停时间;
if(当前图像==最后一张图像)
当前图像 = 第一张图像;
else
当前图像 = 下一张图像
}
这种方法有时候并不是最好的解决方法,因为需要很多张的图片,加载的时候就会很耗内存。特别是在游戏中,速度应该是是首先考虑的因素。那么我们还有另外一种方式,只需要1张图片,而这张图片将一些连续的动作绘制在里一起。我们仅需要不停的改变这张图片的某个位置就可以达到动画的效果。比如下面这张图片:
Graphics类中有个重载过的drawImage()方法,里面有很多的参数:
public abstract boolean drawImage(Image img,
int dx1,
int dy1,
int dx2,
int dy2,
int sx1,
int sy1,
int sx2,
int sy2,
ImageObserver observer)
首尾两个参数和其他的drawImage方法一样,这里就不再做介绍。主要是中间这8个参数。
dx1,dy1,dx2,dy2表示需要绘制到窗口的某个区域,其中dx1,dy1表示该区域的左上角坐标。dx2,dy2表示该区域的右下角坐标。
sx1,sy1,sx2,sy2表示需要绘制图片上哪个区域到窗口上,其中sx1,sy1表示该区域的左上角坐标。sx2,sy2表示该区域的右下角坐标。
下面我们就可以使用这个方法来让上面这张图片动起来。
private Image image = new ImageIcon( " Images/SERIAL.gif " ).getImage();
private int index;
public EyesNext() {
init();
}
void init() {
this .setUndecorated( true );
setSize( 250 , 250 );
createContent();
setVisible( true );
}
private void createContent() {
MediaTracker tracker = new MediaTracker( this );
tracker.addImage(image, 0 );
try
{
tracker.waitForID( 0 );
}
catch (InterruptedException e1)
{
e1.printStackTrace();
}
JPanel jpanelForm = new JPanel() {
protected void paintComponent(Graphics g) {
g.drawImage(image, 0 , 0 , 250 , 250 , index, 0 , index + 250 , 250 ,
this );
}
};
this .getContentPane().add(jpanelForm);
}
public static void main(String [] args) {
final EyesNext eyes = new EyesNext();
Thread thread = new Thread() {
public void run() {
while ( true )
{
try
{
Thread.sleep( 100 );
}
catch (InterruptedException e)
{
e.printStackTrace();
}
eyes.index = eyes.index + 250 ;
if (eyes.index >= 2000 )
{
eyes.index = 0 ;
eyes.repaint();
}
else
{
eyes.repaint();
}
}
}
};
thread.start();
}
}
不过这种方法也只适合绘制一些简单点的图片。比如象上面那张,只有8个图象,并且是单行的。但是如果是多行的图象,那么有时候控制起来就不是那么容易了,比如象下面这张图片:
那么我们就需要使用图片分割方法来对这张图片进行分割,然后保存在数组里面,这样我们仅需要控制数组的下标就方便多了。
java基础类库提供了这样的方法,我们需要使用到三个类ImageProduce,CropImageFilter,FilteredImageSource。我们可以通过下面的操作步骤来完成图象的分割:
Image serial = getImage(URL);//获取联系图片
//剪切连续图片
ImageProducer src = serial .getSource();
//设置剪切区域,一维状态,改变y值构成二维
ImageFilter cut = new CropImageFilter( i*250,0,250,250);
FilteredImageSource fs = new FilteredImageSource( src , cut );
Image[ i] ani = createImage(fs);
下面我们通过一段代码来将上面的图片播放出来
Insets insets;
Image images [][] = new Image[ 5 ][ 5 ];
int row;
int colum;
TestCutImage(){
// 获得图片的Image对象
Image image = Toolkit.getDefaultToolkit().getImage( " Images/Cg0430.jpg " );
// 获取需要剪切的图片资源
ImageProducer porducer = image.getSource();
// 下面通过两个循环分割图片,保存在一个Image类型的二维数组里
for ( int i = 0 ; i < images.length; i ++ )
{
for ( int j = 0 ; j < images[i].length; j ++ )
{
// CropImageFilter构造函数中的第一个参数是分割图片的左上角x坐标
// 第二个参数是分割图片的左上角y坐标
// 第三个参数是分割图片的宽
// 第四个参数是分割图片的高
FilteredImageSource source = new FilteredImageSource(porducer, new CropImageFilter(j * 128 ,i * 96 , 128 , 96 ));
images[i][j] = createImage(source);
}
}
MediaTracker tracker = new MediaTracker( this );
for ( int i = 0 ; i < images.length; i ++ )
{
for ( int j = 0 ; j < images[i].length; j ++ )
{
tracker.addImage(images[i][j], 0 );
}
}
try
{
tracker.waitForID( 0 );
}
catch (InterruptedException e)
{
e.printStackTrace();
}
JPanel panel = new JPanel(){
protected void paintComponent(Graphics g) {
g.drawImage(images[row][colum], 0 , 0 , 340 , 240 , this );
}
};
this .getContentPane().add(panel);
setUndecorated( true );
setSize( 340 , 240 );
setVisible( true );
Thread thread = new Thread( this );
thread.start();
}
public static void main(String [] args) {
new TestCutImage();
}
public void run() {
while ( true )
{
for ( int i = 0 ; i < images.length; i ++ )
{
row = i;
for ( int j = 0 ; j < images[i].length; j ++ )
{
colum = j;
try
{
Thread.sleep( 50 );
}
catch (InterruptedException e)
{
e.printStackTrace();
}
repaint();
}
}
}
}
protected void processWindowEvent(WindowEvent e) {
if (e.getID() == WindowEvent.WINDOW_CLOSING)
{
System.exit( 0 );
}
}
}
分割图片后,只需要对Image数组进行操作,就比操作图片里的坐标方便多了,也更准确!