分享
 
 
 

在Java Applet中的动画编程【转载】

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

基本技术:

在Java中实现动画有很多种办法,但它们实现的基本原理是一样的,即在屏幕上画出一系列的帧来造成运动的感觉。我们先构造一个程序的框架,再慢慢扩展,使之功能比较齐备。

使用线程:

为了每秒中多次更新屏幕,必须创建一个线程来实现动画的循环,这个循环要跟踪当前帧并响应周期性的屏幕更新要求。实现线程的方法有两种,你可以创建一个类Thread的派生类,或附和在一个Runnable的界面上。一个容易犯的错误是将动画循环放在paint()中,这样占据了主AWT线程,而主线程将负责所有的绘图和事件处理。一个框架applet如下:

public class Animator1 extends java.applet.Applet implements Runnable

{

int frame;

int delay;

Thread animator;

public void init()

{

String str = getParameter("fps");

int fps = (str != null) ? Integer.parseInt(str) : 10;

delay = (fps > 0) ? (1000 / fps) : 100;

}

public vois start()

{

animator = new Thread(this);

animator.start();

}

public void run()

{

while (Thread.currentThread() == animator)

{

repaint();

try

{

Thread.sleep(delay);

}

catch (InterruptedException e)

{

break;

}

frame++;

}

}

public void stop()

{

animator = null;

}

}

在你的HTML文件中这样引用:

<applet code="Animator1.class" width="200" height="200">

<param name="fps" value="200">

上面的参数fps表示每秒的帧数,

保持恒定的帧速度:

上例中,applet只是在每两帧之间休眠一个固定的时间,但这有些缺点,有时你会等很长时间,为了每秒显示十帧图象,不应该休眠100毫秒,因为在运行当中也耗费了时间。

这里有一个简单的补救方法:

public void run()

{

long tm = System.currentTimeMillis();

while (Thread.currentThread() == animator)

{

repaint();

try

{

tm += delay;

Thread.sleep(Math.max(0,tm - System.currentTimeMillis()));

}

catch (InterruptedException e)

{

break;

}

frame++;

}

}

画出每一帧:

剩下的就是将每一帧图象绘出。在上例中调用了applet的repaint()来绘出每一帧图象。

public void paint(Graphics g)

{

g.setColor(Color.black);

g.drawString("Frame " + frame, 0, 30);

}

生成图形:

现在我们来画一些稍微困难的东西。下例画了一个正弦曲线的组合,对于每一个x,画一条短的垂直线,所有这些线组成了一个图形,并且每帧变化。但不幸有些闪动,在以后我们将解释为什么闪以及怎样避免。

public void paint(Graphics g)

{

Dimension d = size();

int h = d.height / 2;

for (int x = 0 ; x <d.width; x++)

{

int y1 = (int)((1.0 + Math.sin((x - frame)*0.05))*h);

int y2 = (int)((1.0 + math.sin((x + frame)*0.05))*h);

g.DrawLine(x, y1, x, y2);

}

}

避免闪烁:

上例中的闪烁有两个原因:绘制每一帧花费的时间太长(因为重绘时要求的计算量大),二是在每次调用pait()前整个背景被清除,当在进行下一帧的计算时,用户看到的是背景。清除背景和绘制图形间的短暂时间被用户看见,就是闪烁。在有些平台如PC机上闪烁比在X Window上明显,这是因为X Window的图象被缓存过,使得闪烁的时间比较短。

有两种办法可以明显地减弱闪烁:重载update()或使用双缓冲。

1.重载update()

当AWT接收到一个applet的重绘请求时,它就调用applet的update()。缺省地,update()清除applet的背景,然后调用paint()。重载update(),将以前在paint()中的绘图代码包含在update()中,从而避免每次重绘时将整个区域清除。

既然背景不在自动清除,我们需要自己在update()中完成。我们在绘制新的线之前独自将竖线擦除,完全消除了闪烁。

public void paint(Graphics g)

{

update(g);

}

public void update(Graphics g)

{

Color bg = getBackground();

Dimension d = size();

int h = d.height / 2;

for (int x = 0; x %lt;d.width; x++)

{

int y1 = (int)((1.0 + Math.sin((x - frame)*0.05))*h);

int y2 = (int)((1.0 + Math.sin((x + frame)*0.05))*h);

if (y1 > y2)

{

int t = y1;

y1 = y2;

y2 = t;

}

g.setColor(bg);

g.drawLine(x, 0, x, y1);

g.drawLine(x, y2, x, d.height);

g.setColor(Color.black);

g.drawLine(x, y1, x, y2);

}

}

2.双缓冲技术:

另一种减小帧之间的闪烁的方法是使用双缓冲,它在许多动画applet中被使用。主要原理是创建一个后台图象,将一帧画入图象,然后调用drawImage()将整个图象一次画到屏幕上去。好处是大部分绘制是离屏的。将离屏图象一次绘至屏幕上比直接在屏幕上绘制要有效得多。

双缓冲可以使动画平滑,但有一个缺点,要分配一张后台图象,如果图象相当大,这将需要很大一块内存。当你使用双缓冲技术时,应重载update()。

Dimension offDimension;

Image offImage;

Graphics offGraphics;

public void update(Graphics g)

{

Dimension d = size();

if ((offGraphics == null) || (d.width != offDimension.width)

|| (d.height != offDimension.height))

{

offDimension = d;

offImage = createImage(d.width, d.height);

offGraphics = offImage.getGraphics();

}

offGraphics.setColor(getBackground());

offGraphics.fillRect(0, 0, d.width, d.height);

offGraphics.setColor(Color.Black);

paintFrame(offGraphics);

g.drawImage(offImage, 0, 0, null);

}

public void paint(Graphics g)

{

if (offImage != null)

{

g.drawImage(offImage, 0, 0, null);

}

}

public void paintFrame(Graphics g)

{

Dimension d = size();

int h = d.height / 2;

for (int x = 0; x <d.width; x++)

{

int y1 = (int)((1.0 + Math.sin((x - frame) * 0.05)) + h);

int y2 = (int)((1.0 + Math.sin((x + frame) * 0.05)) + h);

g.drawLine(x, y1, x, y2);

}

}

使用图象:

现在,我们将重写paintFrame()来使图象动起来。这也就带来一些问题,图象往往相当大,被一点点调入,将图象全部画出将花费很多时间,尤其是通过一个较慢的连接,这也就是为什么drawImage带四个参数的原因,其中第四个参数为一个ImageObserver对象。通过调用getImage()得到图象。

在屏幕上移动一幅图象:

world.gif作为背景,car.gif作为移动物体,且被绘制了两次,造成一个两辆车比赛的场景。

Image world;

Image car;

public void init()

{

String str = getParameter("fps");

int fps = (str != null) ? Integer.parseInt(str) : 10;

delay = (fps > 0) ? (1000 / fps) : 100;

world = getImage(getCodeBase(), "world.gif");

car = getImage(getCodeBase(), "car.gif");

}

public void paint(Graphics g)

{

update(g);

}

public void paintFrame(Graphics g)

{

Dimension d = size();

int w = world.getWidth(this);

int h = world.getHeight(this);

if ((w > 0) && (h > 0))

{

g.drawImage(world, (d.width - w)/2, (d.height - h)/2, this);

}

w = car.getWIdth(this);

h = car.getHeight(this);

if ((w > 0) && (h > 0))

{

w += d.width;

g.drawImage(car, d.width - ((frame * 5) % w), (d.height - h)/3, this);

g.drawImage(car, d.width - ((frame * 7) % w), (d.height - h)/2, this);

}

}

显示一系列图象:

通过每一帧显示一幅图象来创建动画。我们仍用双缓冲的方法减小闪烁。原因是我们显示的每一幅图象有一部分是透明的,因此需要在显示下一幅前擦除当前的,如果不使用双缓冲的技术将导致闪烁。

Image frames[];

public void init()

{

String str = getParameter("fps");

int fps = (str != null) ? Integer.parseInt(str) : 10;

delay = (fps > 0) ? (1000 / fps) : 100;

frames = new Image[10];

for (int i = 0; i <10; i++)

{

frames[i] = getImage(getCodeBase(), "duke/T" + i + ".gif");

}

}

public void paint(Graphics g)

{

update(g);

}

public void paintFrame(Graphics g)

{

g.drawImage(frames[frame % 10], 0, 0, null);

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有