Анимация в аплети
Има разнообразни подходи за анимация в Java.
Общото между тях е, че създават движение върху екрана чрез изчертаване на
последователни фрейми с относително висока скорост (10-20 в секунда).
Използване на
Threads
Необходимо е създаването на нова
Java thread, която да включва анимационен цикъл.
Основен шаблон:
import java.applet.* public class Example extends Applet implements Runnable { int frame; int delay; Thread animator;
/** * Initialize the applet and compute the delay between frames. */
public void init() {
String str = getParameter("fps");
int fps = (str != null) ? Integer.parseInt(str) : 10;
if(fps<=0) fps=1; // compute the delay in ms
delay = 1000 / fps;
}
/** * This method is called when the applet becomes visible on * the screen. Create a thread and start it. */
public void start() {
animator = new Thread(this);
animator.start();
}
/** * This method is called by the thread that was created in * the start method. It does the main animation. */
public void run() {
// Remember the starting time
long tm = System.currentTimeMillis();
while (Thread.currentThread() == animator) {
// Display the next frame of animation.
repaint();
// Delay depends on time loose to run thread
try {
//compute new time to paint
tm += delay; //wait the time to paint
Thread.sleep(Math.max(0, tm - System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
// Advance the frame
frame++;
}
}
/** * This method is called when the applet is no longer * visible or there is an event which will stop animation. * Set the animator variable to null so that the * thread will exit before displaying the next frame. */
public void stop() {
animator = null;
}
public void paint (Graphics g){ //paint frame }
}
|
Аплетът трябва да се извика с:
<applet code=Example.class width=200
height=200>
<param name=fps value=20>
</applet>
Пример:
import java.awt.*; import java.applet.*; public class Example extends Applet implements Runnable { int frame=0; int delay; Thread animator;
public void init() { String str = getParameter("fps"); int fps = (str != null) ? Integer.parseInt(str) : 10; if(fps<=0) fps=1; // compute the delay in ms
delay = 1000 / fps;
setFont(new Font("TimesRoman", Font.BOLD, 20));
}
public void start() {
animator = new Thread(this);
animator.start();
}
public void run() {
// Remember the starting time
long tm = System.currentTimeMillis();
while (Thread.currentThread() == animator) {
// Display the next frame of animation.
repaint();
try {
tm += delay; //wait the time to paint
Thread.sleep(Math.max(0, tm - System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
// Advance the frame
if(++frame == 200)frame=0;
}
}
public void stop() {
animator = null;
}
public void paint (Graphics g){ g.drawString("@", frame, 30); }
}
|
|
1
фрейм в секунда
20 фрейма в секунда
|
Друг пример
import java.awt.*; import java.applet.*; public class AnimEx2 extends Applet implements Runnable { int frame; int delay; Thread animator;
public void init() { String str = getParameter("fps"); int fps = (str != null) ? Integer.parseInt(str) : 10; if(fps<=0) fps=1; // compute the delay in ms delay = 1000 / fps; setFont(new Font("TimesRoman", Font.BOLD, 20));
}
public void start() { animator = new Thread(this); animator.start(); }
public void run() { // Remember the starting time long tm = System.currentTimeMillis();
while (Thread.currentThread() == animator) { // Display the next frame of animation. repaint(); // Delay depends on time loose to run thread try { //compute new time to paint tm += delay; //wait the time to paint Thread.sleep(Math.max(0, tm - System.currentTimeMillis())); } catch (InterruptedException e) { break; }
// Advance the frame ++frame; } }
public void stop() { animator = null; } public void paint(Graphics g) { Dimension d = getSize(); //d.height d.width 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.07)) * h); g.drawLine(x, y1, x, y2); } } }
|
|
1 фрейм в секунда
15 фрейма в секунда
|
Намаляване на нестабилността (flashing)
Нестабилността на картината на анимацията
се дължи на две причини:
- Изчисляването и рисуването на всеки фрейм отнема много време.
- Целия фон се изчиства преди извикването на метода
paint(). Докато върви изчисляването на
фрейма се вижда изчистения фон.
В Ms Windows
това потрепване личи по-силно отколкото в X Windows
( X Windows графиката е буферирана, което прави
потрепването по-късо.
Има два начина за намаляване на
потрепването:
- Предефиниране на update()
метода.
- Използване на двойно буфериране ( backbuffer).
Предефиниране на
update() метода
Когато AWT получи заявка
repaint() той извиква update() метода на аплета, който изчиства фона и извиква
paint() метода.
В следващия пример update()
метода е предефиниран, като не изчиства целия фон а изчиства всяка линия
индивидуално преди чертането на новата.
public void paint(Graphics g) { update(g); }
public void update(Graphics g) { Color bgr = getBackground(); Color fgr = getForeground(); Dimension d = getSize(); 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.07)) * h);
if (y1 > y2) { int t = y1; y1 = y2; y2 = t; } g.setColor(bgr); g.drawLine(x, 0, x, y1); g.drawLine(x, y2, x, d.height); g.setColor(fgr); g.drawLine(x, y1, x, y2); } }
|
|
15
фрейма в секунда
|
Двойно буфериране
Това е много често използвана
техника за намаляване смущенията между анимационните фрейми.
Основният принцип е създаването на изображение вън от екрана (
offscreen image) върху което се чертае и след това се извежда
наведнъж на екрана чрез drawImage(). Това е много
по-ефективно отколкото директното чертане на екрана. Основният недостатък е
необходима много допълнителна памет при голямо поле.
import java.awt.*; import java.applet.*;
public class AnimEx5 extends Applet implements Runnable { int frame; int delay; Thread animator;
Dimension offDimension; Image offImage; Graphics offGraphics;
public void init() { String str = getParameter("fps"); int fps = (str != null) ? Integer.parseInt(str) : 10; delay = (fps > 0) ? (1000 / fps) : 100; }
/** Create a thread and start it. */ public void start() { animator = new Thread(this); animator.start(); }
/** * the main animation. */ public void run() { // Remember the starting time long tm = System.currentTimeMillis(); while (Thread.currentThread() == animator) { // Display the next frame of animation. repaint();
// Delay depending on how far we are behind. try { tm += delay; Thread.sleep(Math.max(0, tm - System.currentTimeMillis())); } catch (InterruptedException e) {break; }
// Advance the frame frame++; } }
/** * called when the applet is no longer visible. */ public void stop() { animator = null; offImage = null; offGraphics = null; }
/** * Update a frame of animation. */ public void update(Graphics g) { Color fgr=getForeground(); Dimension d = getSize();
// Create the offscreen graphics context if ((offGraphics == null) || (d.width != offDimension.width) || (d.height != offDimension.height)) { offDimension = d; offImage = createImage(d.width, d.height); offGraphics = offImage.getGraphics(); }
// Erase the previous image offGraphics.setColor(getBackground()); offGraphics.fillRect(0, 0, d.width, d.height); offGraphics.setColor(fgr);
// Paint the frame into the image paintFrame(offGraphics);
// Paint the image onto the screen g.drawImage(offImage, 0, 0, this); }
public void paint(Graphics g) { update(g); }
/** * Paint a frame of animation. */ public void paintFrame(Graphics g) { Dimension d = getSize(); 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.07)) * h); g.drawLine(x, y1, x, y2); } } }
|
|
15
фрейма в секунда
|