Entwicklung eines 2D Computerspiels in Java

Werbung
Entwicklung eines 2D
Computerspiels in Java
Motivation
•
Arbeiten mit grösseren Programmen!
•
Lesen und manipulieren von Programmen Anderer.
Alle Beispielprogramme sind aus dem Buch:
"Developing Games in Java." von David Brackeen
http://www.brackeen.com/javagamebook
Das Buch wird für die Vorlesung und die Übungen NICHT benötigt,
der Programmcode für Kapitel 2 und 3 sollte übers Web geladen
werden.
1
Themenübersicht
•
•
2D Grafik und Animation
–
Darstellungsmodi
–
Vollbildschirmdarstellung
–
Bilder
–
Animation
–
Sprites
Interaktivität
–
•
Eingabe per Tastatur und Maus
Ein 2D-Spiel
–
Spielfelddesign und Scrolling
–
Spieler, Gegner und andere Objekte
–
Kollisionsdetektion
2D Grafik und Animation
2
Raster Displays
Die Bildausgabe besitzt meist einen eigenen Speicher.
Da Veränderungen im Speicher sofort beim nächsten Bildaufbau sichtbar
werden, kommt es bei Animationen zu störenden Artefakten.
True-Color Frame Buffer
Für jeden Bildpunkt benötigt mindestens 3 Byte.
 2^24 Farben
Reihenfolge der Bytes ist zu beachten ( „Endians“) .
3
Double Buffering
Ein zweiter Bildspeicher verbessert Animationen.
Bildaufbau wird unsichtbar
– zwischen den Speichern wird nur zu Beginn einer Seite und
wenn der Bildaufbau abgeschlossen ist umgeschalten.
Grafik Hardware
Computergraphik ist einer der wenigen Bereiche bei dem
immer mehr spezial Hardware zum Einsatz kommt.
z.B.
1 M pixel * 60 Bilder/sec * 1 ops/ pix = 60M ops/sec
Schon einfaches verschieben einer Bildschirmseite ist
eine sehr aufwändige Operation!!
Sind es nicht gerade die Bilder, warum wir einen Computer nutzen!!
4
Darstellungsmodi
•
Der Bildschirm kann in verschiedene Darstellungsmodi geschaltet werden.
•
Die verfügbaren Modi hängen von der Grafikkarte und dem Bildschirm ab.
•
Ein Darstellungsmodus (display mode) besteht aus:
–
Bildschirmauflösung in Pixeln (z.B.: 1024 x 768, 1280 x 1024, ...)
–
Farbtiefe pro Pixel (z.B.: 8 Bit = 256 Farben, 24 Bit = 16 Mio Farben)
–
Bildwiederholfrequenz (z.B. 75Hz, 85Hz, ...)
Warum so eine Vielfalt?
Sehr viele unterschiedliche Technologien und Hersteller.
Vollbildschirmdarstellung
•
Vollbildschirmdarstellung (full screen graphics) ist für Spiele am geeignetsten.
•
Es werden 3 Objekte zur Umschaltung auf Vollbildschirmdarstellung benötigt.
•
Window-Objekt
•
–
Abstraktion, dessen was auf dem Bildschirm dargestellt wird (Leinwand).
–
Im Beispiel: JFrame
DisplayMode-Objekt
–
•
Spezifiziert Auflösung, Farbtiefe und Bildwiederholfrequenz.
GraphicsDevice-Objekt
–
Erlaubt den Darstellungsmodus zu ändern.
–
„Interface zur Grafikkarte”
–
Beschaffung über
GraphicsEnvironment-Objekt.
5
Vollbildschirmdarstellung
JFrame window = new JFrame();
DisplayMode displayMode = new DisplayMode(800, 600, 16, 75)
// get the GraphicsDevice
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = environment.getDefaultScreenDevice();
// use the Jframe as the full screen window
device.setFullScreenWindow(window);
// change the display mode
device.setDisplayMode(displayMode);
SimpleScreenManager.java
•
Die SimpleScreenManager Klasse ist nur dazu da den Bildschirm in die
Vollbildschirmdarstellung zu schalten!
–
Es wird der Rand des Fensters entfernt !
–
Die Fenstergrösse soll nicht mehr manuell verändert werden können.
6
SimpleScreenManager.java
import java.awt.*;
import javax.swing.JFrame;
/**
The SimpleScreenManager class manages initializing and
displaying full screen graphics modes.
*/
public class SimpleScreenManager {
private GraphicsDevice device;
public SimpleScreenManager() {…….}
/**
Enters full screen mode and changes the display mode.
*/
public void setFullScreen(DisplayMode displayMode, JFrame window)
/**
Returns the window currently used in full screen mode.
*/
public Window getFullScreenWindow() {………….. }
/**
Restores the screen's display mode.
*/
public void restoreScreen() {…..}
} // END SimpleScreenManager
SimpleScreenManager. setFullScreen
public class SimpleScreenManager {
……..
/**
Enters full screen mode and changes the display mode.
*/
public void setFullScreen(DisplayMode displayMode, JFrame window)
{
window.setUndecorated(true);
window.setResizable(false);
device.setFullScreenWindow(window);
}
if (displayMode != null && device.isDisplayChangeSupported()
{
try {
device.setDisplayMode(displayMode);
}
catch (IllegalArgumentException ex) {
// ignore - illegal mode for this device
}
}
)
7
SimpleScreenManager. …..
public class SimpleScreenManager {
…………………
/**
Returns the window currently used in full screen mode.
*/
public Window getFullScreenWindow() {
return device.getFullScreenWindow();
}
/**
Restores the screen's display mode.
*/
}
public void restoreScreen() {
Window window = device.getFullScreenWindow();
if (window != null) {
window.dispose();
}
device.setFullScreenWindow(null);
}
Vollbildschirmdarstellung
•
Die Funktionalität zum Umschalten auf die Vollbildschirmdarstellung wird in
die Klasse S i m p l e S c r e e n M a n a g e r gepackt.
•
Listing: SimpleScreenManager.java
•
F u l l S c r e e n T e s t ist ein Testprogramm zur Vollbildschirmdarstellung
•
–
Kommandozeilenparameter: Auflösung und Farbtiefe
–
Aufruf z.B.: java FullScreenTest 1024 768 32
–
Demo ...
Listing: FullScreenTest.java
8
Bilder
•
Transparenz
•
Dateiformate
•
Bilder einlesen
•
Hardwarebeschleunigte Bilder
•
Benchmarks
Repräsentation eines Bildes durch ‚Pixel‘
• Die Bildpunkte (Pixel) eines Bildes werden als ganze Zahlen (integer) gespeichert.
Rot
Grün
Blau
Alpha
Die 4 Byte eines 32-bit Integer Bildpunktes.
Für Alpha = 0 ist der Bildpunkt transparent,
RGBA wird bei OpenGL verwendet.
für Alpha = 255 ist er undurchsichtig.
ARGB bei Java 3D
.....
Beim Speichern und Lesen ist die Reihenfolge der Bytes der verwendeten
Hardware zu beachten (big- / little endian)!!
• Ein Bild wird in einem 1-dim Array von ints gespeichert.
pixel [127]
z.B.: Höhe = 4
Breite = 32
=> 128 pixel
pixel [0]
pixel[0]:
pixel [31]
ist bei OpenGL links unten, bei Java 3D links oben!
9
Bilder: Transparenz
•
Es gibt verschiedene Möglichkeiten Bilder darzustellen
•
Opaque
– alle Pixel sind sichtbar
– auch die Hintergrundpixel!
•
Transparent
– Pixel sind 100% sichtbar
– oder 100% unsichtbar
– z.B. unsichtbarer Hintergrund
•
Translucent
– Pixel teilweise sichtbar
– durchscheinen
Bilder: Dateiformate
•
Bilder können in verschiedenen Dateiformaten abgespeichert werden.
•
GIF
– Verlustfreie Kompression
– geeignet für Graphiken
•
PNG
– Verlustfreie Kompression
– mehr Funktionalität als GIF
•
JPEG
– Verlustbehaftete Kompression (lossy compression)
– geeignet für Photos
10
Bilder einlesen
1.
Möglichkeit: ( java.awt )
Toolkit
Image
toolkit = Toolkit.getDefaultToolkit();
image
= toolkit.getImage(filename);
Da das Laden des Bildes in einem eigenen Thread geschieht, kann es vorkommen,
dass man das Bild anzeigt obwohl es noch nicht vollständig geladen ist.
2.
Möglichkeit: ( javax.swing )
ImageIcon icon = new ImageIcon(filename);
Image
image = icon.getImage();
Hier wird das Laden überwacht und das Bild erst dargestellt, wenn es auch vorhanden
ist.
ImageTest.java
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public static void main(String[] args) {
DisplayMode displayMode;
public class ImageTest extends JFrame {
if (args.length == 3) {
displayMode = new DisplayMode(
Integer.parseInt(args[0]),
Integer.parseInt(args[1]),
Integer.parseInt(args[2]),
DisplayMode.REFRESH_RATE_UNKNOWN);
}
else {
displayMode = new DisplayMode(800, 600, 16,
DisplayMode.REFRESH_RATE_UNKNOWN);
}
public static void main(String[] args) {…….}
private static final int FONT_SIZE = 24;
private static final long DEMO_TIME = 10000;
private
private
private
private
private
private
private
SimpleScreenManager screen;
Image bgImage;
Image opaqueImage;
Image transparentImage;
Image translucentImage;
Image antiAliasedImage;
boolean imagesLoaded;
public void run(DisplayMode displayMode) {…….}
ImageTest test = new ImageTest();
test.run(displayMode);
}
public void loadImages() {…….}
public void paint(Graphics g) {…….}
}
public void drawImage(Graphics g, Image image, int x, int y, String caption) {…….}
11
ImageTest.java
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
public static void main(String[] args) {
public void run(DisplayMode displayMode) {
setBackground(Color.blue);
DisplayMode displayMode;
setForeground(Color.white);
public class ImageTest extends JFrame {
setFont(new Font("Dialog", Font.PLAIN, FONT_SIZE));
if (args.length == 3) {
imagesLoaded = false;
displayMode = new DisplayMode(
public static void main(String[] args) {…….}
Integer.parseInt(args[0]),
screen = new SimpleScreenManager();
Integer.parseInt(args[1]),
private static final int FONT_SIZE = 24;
try {
Integer.parseInt(args[2]),
private static final long DEMO_TIME = 10000;
screen.setFullScreen(displayMode, this);
DisplayMode.REFRESH_RATE_UNKNOWN);
loadImages();
}
private SimpleScreenManager screen;
try {
else {
private Image bgImage;
Thread.sleep(DEMO_TIME);
displayMode = new DisplayMode(800, 600, 16,
private Image opaqueImage;
}
DisplayMode.REFRESH_RATE_UNKNOWN);
private Image transparentImage;
catch (InterruptedException ex) { }
}
private Image translucentImage;
}
private Image antiAliasedImage;
finally {
ImageTest test = new ImageTest();
private boolean imagesLoaded;
screen.restoreScreen();
test.run(displayMode);
}
}
public void run(DisplayMode displayMode) {…….} }
public void loadImages() {…….}
public void paint(Graphics g) {…….}
public void drawImage(Graphics g, Image image, int x, int y, String caption) {…….}
}
ImageTest.java (2)
public static void main(String[] args) {
DisplayMode displayMode;
if (args.length == 3) {
displayMode = new DisplayMode(
Integer.parseInt(args[0]),
Integer.parseInt(args[1]),
Integer.parseInt(args[2]),
DisplayMode.REFRESH_RATE_UNKNOWN);
}
else {
displayMode = new DisplayMode(800, 600, 16,
DisplayMode.REFRESH_RATE_UNKNOWN);
}
public void run(DisplayMode displayMode) {
setBackground(Color.blue);
setForeground(Color.white);
setFont(new Font("Dialog", Font.PLAIN, FONT_SIZE));
imagesLoaded = false;
screen = new SimpleScreenManager();
try {
screen.setFullScreen(displayMode, this);
loadImages();
try {
Thread.sleep(DEMO_TIME);
}
catch (InterruptedException ex) { }
}
finally {
screen.restoreScreen();
}
ImageTest test = new ImageTest();
test.run(displayMode);
}
}
12
ImageTest.java (3)
public void run(DisplayMode displayMode) {
setBackground(Color.blue);
setForeground(Color.white);
setFont(new Font("Dialog", Font.PLAIN, FONT_SIZE));
imagesLoaded = false;
public void loadImages() {
bgImage
= loadImage("images/background.jpg");
screen = new SimpleScreenManager();
opaqueImage
= loadImage("images/opaque.png");
try {
transparentImage = loadImage("images/transparent.png");
screen.setFullScreen(displayMode, this);
translucentImage = loadImage("images/translucent.png");
loadImages();
try {
antiAliasedImage = loadImage("images/antialiased.png");
Thread.sleep(DEMO_TIME);
imagesLoaded
= true;
}
// signal to AWT to repaint this window
catch (InterruptedException ex) { }
repaint();
}
}
finally {
screen.restoreScreen();
}
private Image loadImage(String fileName) {
}
return new ImageIcon(fileName).getImage();
}
public void drawImage(Graphics g, Image image, int x, int y,
String caption)
{
g.drawImage(image, x, y, null);
g.drawString(caption, x + 5, y + FONT_SIZE +
image.getHeight(null) );
}
ImageTest.java (3)
public void paint(Graphics g) {
// set text anti-aliasing
if (g instanceof Graphics2D) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
// draw images
if (imagesLoaded) {
g.drawImage(bgImage, 0, 0, null);
drawImage(g, opaqueImage, 0, 0, "Opaque");
drawImage(g, transparentImage, 320, 0, "Transparent");
drawImage(g, translucentImage, 0, 300, "Translucent");
drawImage(g, antiAliasedImage, 320, 300, "Translucent (Anti-Aliased)");
}
else {
g.drawString("Loading Images...", 5, FONT_SIZE);
public void drawImage(Graphics g, Image image, int x, int y,
}
String caption)
}
{
g.drawImage(image, x, y, null);
g.drawString(caption, x + 5, y + FONT_SIZE +
image.getHeight(null) );
}
13
Spielentwicklung: was haben wir bis jetzt!
SimpleSreeenManager.java ( kein main )
Hier werden alle für den Bildschirm relevanten Einstellungen gesetzt.
Vollbildschirm, Pixelauflösung,
FullSreenTest.java ( mit main )
Testprogramm für SimpleSreenManager Klasse
ImageTest.java ( mit main )
Bilder in gängigen Formaten ( .jpg, .png ...) können in die Klasse
java.awt.Image eingelesen werden.
Bilder im java.awt.Image Format können dargestellte werden.
Bilder: Benchmarks
•
Benchmarks
14
ImageSpeedTest.java
public void run(DisplayMode displayMode) {
publicsetBackground(Color.blue);
static void main(String[] args) {
setForeground(Color.white);
import java.awt.*;
setFont(new Font("Dialog",
DisplayMode
displayMode; Font.PLAIN, FONT_SIZE));
import javax.swing.ImageIcon;
imagesLoaded = false;
import javax.swing.JFrame;
if (args.length == 3) {
screen
= new SimpleScreenManager();
displayMode
= new DisplayMode(
public class ImageTest extends JFrame {
try {
Integer.parseInt(args[0]),
this);
Integer.parseInt(args[1]),
public static void main(String[] args) { …as in ImageTest…. } screen.setFullScreen(displayMode,
synchronized
(this) {
Integer.parseInt(args[2]),
loadImages();
DisplayMode.REFRESH_RATE_UNKNOWN);
private static final int FONT_SIZE = 24;
// wait for test to complete
}
private static final long TIME_PER_IMAGE = 1500; // changed
try {
else {
wait();
displayMode
= new DisplayMode(800, 600, 16,
private SimpleScreenManager screen;
}
DisplayMode.REFRESH_RATE_UNKNOWN);
private Image bgImage;
catch (InterruptedException ex) { }
}
private Image opaqueImage;
}
private Image transparentImage;
finally {
ImageSpeedTest
test = new ImageSpeedTest();
private Image translucentImage;
screen.restoreScreen();
test.run(displayMode);
private Image antiAliasedImage;
} }
private boolean imagesLoaded;
}
public void run(DisplayMode displayMode) {… changed….}
public void loadImages() {… as in ImageTest ….}
public void paint(Graphics g) {… changed ….}
}
public void drawImage(Graphics g, Image image, int x, int y, String caption) {…changed….}
ImageSpeedTest.java (2)
public void paint(Graphics g) {
if (g instanceof Graphics2D) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
public void drawImage(Graphics g, Image image, String name) {
}
int width = screen.getFullScreenWindow().getWidth() - image.getWidth(null);
int height = screen.getFullScreenWindow().getHeight() - image.getHeight(null);
int numImages = 0;
// draw images
}
if (imagesLoaded) {
g.drawImage(bgImage, 0, 0, null);
drawImage(g, opaqueImage, "Opaque");
drawImage(g, transparentImage, "Transparent");
drawImage(g, translucentImage, "Translucent");
long startTime = System.currentTimeMillis();
drawImage(g, antiAliasedImage, "Translucent
(Anti-Aliased)");
while (System.currentTimeMillis()
- startTime < TIME_PER_IMAGE )
{
// notify that the test is complete
int x = Math.round( (float) Math.random() * width );
synchronized (this) {
int y = Math.round( (float) Math.random() * height );
notify();
g.drawImage(image, x, y, null);
}
numImages++;
}
}
else {
long time = System.currentTimeMillis() - startTime;
g.drawString("Loading Images...", 5, FONT_SIZE);
}
float speed = numImages * 1000f / time;
System.out.println(name + ": " + speed + " images/sec");
}
15
Animation
• Dynamisches verändern des Bildes!
Am einfachsten ist eine Abfolge von fertigen Teilbildern.
Bild A
Zeit
ms
Bild B
0ms
200ms
Bild C
275ms
Bild B
300ms
end
Animation
Abfolge von einzelnen Teilbildern, wobei jedes Bild für eine bestimmte Zeit
präsentiert wird.
Teilbilder :
class AnimFrame {
Image image;
long
endTime;
public AnimFrame( Image image, long endTime ) {
this.image = image;
this.endTime = endTime;
}
}
16
Animation.java
public class Animation {
java.util.ArrayList;
private
private
private
private
ArrayList frames;
int currFrameIndex;
long animTime;
long totalDuration;
/**
Creates a new, empty Animation.
public Animation() { ……… }
/**
*/
Adds an image to the animation with the specified
duration (time to display the image).
*/
public synchronized void addFrame(Image image, long duration) {……. }
/**
Starts this animation over from the beginning.
*/
/**
Updates this animation's current image (frame), if neccesary. */
/**
Gets this Animation's current image. Returns null if this
animation has no images.
*/
public synchronized void start() { ………..}
public synchronized void update(long elapsedTime) {…………. }
public synchronized Image getImage() {…….. }
private AnimFrame getFrame(int i) { }
}
private class AnimFrame {
}
Animation.java (2)
public class Animation {
private
private
private
private
ArrayList frames;
int currFrameIndex;
long animTime;
long totalDuration;
Innere Klasse
public Animation()
synchronized{void update(long elapsedTime) {
public
if (frames.size()
> 1) {
frames
= new ArrayList();
animTime +=
totalDuration
= 0;elapsedTime;
start();
if (animTime >= totalDuration) {
}
animTime = animTime % totalDuration;
currFrameIndex = 0;
}
public synchronized
void addFrame(Image image,
long duration)
while (animTime > getFrame(currFrameIndex).endTime)
{
totalDuration
+= duration;
/**
Creates a new, empty Animation.
*/
currFrameIndex++;
frames.add(new
AnimFrame(image, totalDuration));
public Animation() { ……… }
}
}
}
/**
Adds an image to the animation with the specified
}
duration (time to display the image).
*/
public synchronized void addFrame(Image image,
longsynchronized
duration) {…….
}
public
void start()
{
{
animTime = 0;
currFrameIndex = 0;
if (frames.size() == 0) {
}
return null;
/**
Updates this animation's current image (frame), if neccesary.
*/
}
public synchronized void update(long elapsedTime) {………….
}
else {
return
getFrame(currFrameIndex).image;
/**
Gets this Animation's current image. Returns null if this
}
animation has no images.
*/
public synchronized Image getImage() {…….. } }
/**
Starts this animation over from the beginning.
public synchronized void start() { ………..}
private AnimFrame getFrame(int i) { }
}
private class AnimFrame {
}
*/
public synchronized Image getImage() {
private AnimFrame getFrame(int i) {
return (AnimFrame)frames.get(i);
}
17
Animation.java (3)
public Animation() {
frames = new ArrayList();
totalDuration = 0;
start();
}
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;
if (animTime >= totalDuration) {
animTime = animTime % totalDuration;
currFrameIndex = 0;
}
public synchronized void addFrame(Image image,
long duration)
{
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}
while (animTime > getFrame(currFrameIndex).endTime)
{
currFrameIndex++;
}
}
}
public synchronized void start() {
animTime = 0;
currFrameIndex = 0;
}
public class AnimFrame {
Image image;
long endTime;
public AnimFrame( Image image, long endTime ) {
this.image = image;
this.endTime = endTime;
}
}
public synchronized Image getImage() {
if (frames.size() == 0) {
return null;
}
else {
return getFrame(currFrameIndex).image;
}
}
private AnimFrame getFrame(int i) {
return (AnimFrame) frames.get(i);
}
'Active Rendering'
Um eine Animation anzuzeigen muss der Bildschirm kontinuierlich
erneuert werden.
Bisher wurde dies von paint() übernommen, welches mit repaint()
ausgelöst wurde.
Dies kann jedoch zu kleinen Verzögerungen führen, wenn der
"AWT-Thread" gerade noch anderweitig beschäftigt ist.
AUSWEG: 'Active Rendering' , über getGraphics() von Component
erhalten wir den Graphics Context des Bildschirms.
Graphics g = screen.getFullScreenWindow().getGraphics():
draw(g);
g.dispose();
Nach dem Zeichen sollte sofort g.dispose() aufgerufen werden um
Systemressourcen frei zu geben.
18
AnimationTest1.java
Ein Animationszyklus [ animationLoop() ] hat folgende Schritte:
1.
2.
3.
4.
Alle Animationen auf die richtige Zeit setzen, update().
Alle entsprechenden Bilder anzeigen.
Eventuell warten.
Wieder zu Schritt eins.
AnimationTest1.java (2)
ein vereinfachter 'animationLoop()'
while (true) {
// update any animation
updateAnimations();
// draw to screen
Graphics g = screen.getFullScreenWindow().getGraphics():
draw(g);
g.dispose();
}
// take a nap
try {
Tread.sleep( 2000);
}
catch (InteruptException ex) {}
19
AnimationTest1.java (3)
public void run(DisplayMode displayMode) {
screen = new SimpleScreenManager();
try {
screen.setFullScreen(displayMode, new JFrame());
loadImages();
animationLoop();
public class AnimationTest1 {
}
finally {
public static void main(String args[]) { ..as usual.. }
screen.restoreScreen();
}
private static final long DEMO_TIME = 5000;
}
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
private SimpleScreenManager screen;
private Image bgImage;
private Animation anim;
public void loadImages() {
}
private Image loadImage(String fileName) { }
public void loadImages() {
// load images
bgImage = loadImage("images/background.jpg");
Image player1 = loadImage("images/player1.png");
Image player2 = loadImage("images/player2.png");
Image player3 = loadImage("images/player3.png");
// create animation
anim = new Animation();
anim.addFrame(player1, 250);
anim.addFrame(player2, 150);
anim.addFrame(player1, 150);
anim.addFrame(player2, 150);
anim.addFrame(player3, 200);
anim.addFrame(player2, 150);
public void run(DisplayMode displayMode) { }
public void animationLoop() {
}
public void draw(Graphics g) { }
}
}
AnimationTest1.java (4)
public void run(DisplayMode displayMode) {
screen = new SimpleScreenManager();
try {
screen.setFullScreen(displayMode, new JFrame());
loadImages();
animationLoop();
}
finally {
screen.restoreScreen();
}
}
public void animationLoop() {
long startTime = System.currentTimeMillis();
long currTime = startTime;
while (currTime - startTime < DEMO_TIME) {
long elapsedTime =
System.currentTimeMillis() - currTime;
currTime += elapsedTime;
// update animation
anim.update(elapsedTime);
// draw to screen
Graphics g =
screen.getFullScreenWindow().getGraphics();
draw(g);
g.dispose();
public void draw(Graphics g) {
// draw background
g.drawImage(bgImage, 0, 0, null);
// take a nap
try {
Thread.sleep(20);
}
catch (InterruptedException ex) { }
// draw image
g.drawImage(anim.getImage(), 0, 0, null);
}
}
}
20
Problem mit AnimationTest1
Die Animation flimmert!!!
Mögliche Gründe:
1.
Während der Animation werden halbfertige Bilder angezeigt.
2.
Das Bild wird angezeigt bevor alle Teilbilder der draw - Routine
gemalt sind.
3.
Das Anzeigen des neuen Bildes ist nicht mit der Bildfrequenz
synchronisiert (~75 Hz).
Problem mit AnimationTest1
Die Animation flimmert!!!
mögliche Lösungen:
1.
Bilder werden off-screen erstellt und dann als Ganzes in den Framebuffer
kopiert.
2.
Falls der Framebuffer gross genug ist werden zwei Bildbereiche angelegt.
Der eine wird vom Bildschirm ausgelesen, und im anderen wird das Bild
verändert. Danach wechseln die Rollen "page-flipping„.
Es bleibt immer noch die mögliche Interferenz mit der Bildwiederholungsfrequenz.
Die beste Strategie ist von der Hardware abhängig.
Die Optimierung der Lösung kann der BufferStrategy Klasse überlassen werden!
21
BufferStrategy Klasse
API ---> gibt guten Überblick zu den verschiedenen Möglichkeiten.
Canvas, Window
besitzen eine
BufferStrategy:
Für das Jframe möchten wir mindestens 2 Buffer:
// Create a general double-buffering strategy
frame.createBufferStrategy(2);
BufferStrategy strategy = frame.getBufferStrategy();
// Render loop
while (!done) {
Graphics g = strategy.getDrawGraphics();
// Draw to graphics
draw(g);
g.dispose();
strategy.show();
// switch to next buffer
}
Verbesserter ScreenManager
'Double Buffering' + 'Active Rendering' sollen von ScreenManager
übernommen werden.
Weitere Verbesserungen:
Screenmanager vergleicht automatisch mögliche
Displaymodi mit einer vorgegebenen internen Liste.
Da 'Active Rendering' verwendet wird, wird die
automatische paint() Methode abgeschaltet.
frame.setIgnoreRepaint(true)
ScreenManager.java soll hier nicht im Detail besprochen werden,
relativ viel Code zur Anpassung an die vielen verschieden Plattformen!
22
SimpleScreenManager
public void setFullScreen(DisplayMode displayMode,
Jframe
window
)
{
window.setUndecorated(true);
window.setResizable(false);
public void setFullScreen(DisplayMode displayMode) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.setIgnoreRepaint(true);
frame.setResizable(false);
device.setFullScreenWindow(window);
}
if (displayMode != null &&
device.isDisplayChangeSupported()
{
try {
device.setDisplayMode(displayMode);
}
catch (IllegalArgumentException ex) {
// ignore - illegal mode for this device
}
}
ScreenManager
device.setFullScreenWindow(frame);
)
}
if (displayMode != null && device.isDisplayChangeSupported())
{
try {
device.setDisplayMode(displayMode);
}
catch ( IllegalArgumentException ex ) { }
// fix for mac os x
frame.setSize(displayMode.getWidth(),
displayMode.getHeight());
}
// avoid potential deadlock in 1.4.1_02
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
frame.createBufferStrategy(2);
}
});
}
catch (InterruptedException ex)
{ // ignore
}
catch (InvocationTargetException ex) { // ignore
}
AnimationTest2.java
public class AnimationTest1 {
public static void main(String args[]) { ..changed.. }
private static final DisplayMode POSSIBLE_MODES[] = {
new DisplayMode(1024, 768, 32, 0),
new DisplayMode( 800, 600, 32, 0),
……….
};
private
private
private
private
static final long DEMO_TIME = 10000;
SimpleScreenManager screen;
Image bgImage;
Animation anim;
public void loadImages() { ..as before }
private Image loadImage(String fileName) {..as before }
public void run(DisplayMode displayMode) { ..changed.. }
public void animationLoop() {..changed.. }
}
public void draw(Graphics g) {..as before }
23
AnimationTest1.java
public void animationLoop() {
long startTime = System.currentTimeMillis();
long currTime = startTime;
AnimationTest2.java
public void animationLoop() {
long startTime = System.currentTimeMillis();
long currTime = startTime;
while (currTime - startTime < DEMO_TIME) {
long elapsedTime =
System.currentTimeMillis() - currTime;
currTime += elapsedTime;
while (currTime - startTime < DEMO_TIME) {
long elapsedTime =
System.currentTimeMillis() - currTime;
currTime += elapsedTime;
// update animation
anim.update(elapsedTime);
// update animation
anim.update(elapsedTime);
// draw to screen
Graphics g =
screen.getFullScreenWindow().getGraphics();
draw(g);
g.dispose();
// draw and update screen
Graphics2D g = screen.getGraphics();
draw(g);
g.dispose();
screen.update();
// take a nap
try {
Thread.sleep(20);
}
catch (InterruptedException ex) { }
// take a nap
try {
Thread.sleep(20);
}
catch (InterruptedException ex) { }
}
}
}
}
AnimationTest1.java
public static void main(String args[]) {
DisplayMode displayMode;
if (args.length == 3) {displayMode = new DisplayMode(
Integer.parseInt(args[0]),
Integer.parseInt(args[1]),
Integer.parseInt(args[2]),
DisplayMode.REFRESH_RATE_UNKNOWN );
}
else { displayMode = new DisplayMode(800, 600, 16,
DisplayMode.REFRESH_RATE_UNKNOWN);
}
AnimationTest1 test = new AnimationTest1();
test.run(displayMode);
}
public void run(DisplayMode displayMode) {
screen = new SimpleScreenManager();
try {
screen.setFullScreen(displayMode, new JFrame());
loadImages();
animationLoop();
}
finally { screen.restoreScreen();
}
}
AnimationTest2.java
public static void main(String args[]) {
AnimationTest2 test = new AnimationTest2();
test.run();
}
public void run() {
screen = new ScreenManager();
try {
DisplayMode displayMode =
screen.findFirstCompatibleMode(POSSIBLE_MODES);
screen.setFullScreen(displayMode);
loadImages();
animationLoop();
}
finally {
screen.restoreScreen();
}
}
24
AnimationTest 2 .draw()
public void draw(Graphics g) {
// draw background
g.drawImage(bgImage, 0, 0, null);
// draw image
g.drawImage(anim.getImage(), 0, 0, null);
}
25
Herunterladen