I kolejny mój problem. Znowu okaże się, że jestem ślepy i głupi, ale cóż, dopiero się uczę.
Mój program miał za zadanie wyświetlenie postaci podobnej do pacmana, ale z zamkniętymi ustami i mrugającymi oczami. Sam pacman miał poruszać się ze stałą prędkością, odbijając się od krawędzi ekranu. Kod pliku "SpriteTest" przedstawia się następująco:
import graphic.Animation;
import graphic.ScreenManager;
import graphic.Sprite;
import java.awt.DisplayMode;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
public class SpriteTest {
public static void main(String[] args) {
SpriteTest test = new SpriteTest();
test.run();
}
private static final DisplayMode POSSIBLE_MODES[] = {
new DisplayMode(1600, 900, 32, 75),
new DisplayMode(1600, 900, 16, 75),
new DisplayMode(800, 600, 32, 75),
new DisplayMode(800, 600, 16, 75),
new DisplayMode(640, 480, 32, 75),
new DisplayMode(640, 480, 16, 75)
};
private static final long DEMO_TIME = 10000;
private ScreenManager screen;
private Image bgImage;
private Sprite sprite;
public void loadImages() {
//ładowanie rysunków
bgImage = loadImage("images/background.jpg");
Image player1 = loadImage("images/player1.png");
Image player2 = loadImage("images/player2.png");
Image player3 = loadImage("images/player3.png");
//tworzenie sprite
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);
sprite = new Sprite(anim);
//uruchomienie sprite poruszającego się w dół i w prawo
sprite.setVelocityX(0.2f);
sprite.setVelocityY(0.2f);
}
private Image loadImage(String fileName) {
return new ImageIcon(fileName).getImage();
}
public void run() {
screen = new ScreenManager();
try {
DisplayMode displayMode = screen.findFirstCompatibleMode(POSSIBLE_MODES);
screen.setFullScreen(displayMode);
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;
//aktualizacja sprite
update(elapsedTime);
//narysowanie i aktualizacja zawartości ekranu
Graphics2D g = screen.getGraphics();
draw(g);
g.dispose();
screen.update();
//chwila przerwy
try {
Thread.sleep(20);
}
catch (InterruptedException ex) {}
}
}
public void update(long elapsedTime) {
//sprawdzenie granic sprite
if (sprite.getX() < 0) sprite.setVelocityX(Math.abs(sprite.getVelocityX()));
else if (sprite.getX() + sprite.getWidth() >= screen.getWidth()) sprite.setVelocityX(-Math.abs(sprite.getVelocityX()));
if (sprite.getY() < 0) sprite.setVelocityY(Math.abs(sprite.getVelocityY()));
else if (sprite.getY() + sprite.getHeight() >= screen.getHeight()) sprite.setVelocityY(-Math.abs(sprite.getVelocityY()));
//aktualizacja duszka
sprite.update(elapsedTime);
}
public void draw(Graphics g) {
//Narysowanie tła
g.drawImage(bgImage, 0, 0, null);
//rysowanie sprite
g.drawImage(sprite.getImage(), Math.round(sprite.getX()), Math.round(sprite.getY()), null);
}
}
W imporcie na początku:
import graphic.Animation;
import graphic.ScreenManager;
import graphic.Sprite;
Prowadzi do moich plików. ScreenManager:
package graphic;
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JFrame;
public class ScreenManager
{
private GraphicsDevice device;
public ScreenManager()
{
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
device = environment.getDefaultScreenDevice();
}
public DisplayMode[] getCompatibleModes() {
return device.getDisplayModes();
}
public DisplayMode findFirstCompatibleMode(DisplayMode modes[]) {
DisplayMode goodModes[] = device.getDisplayModes();
for (int i = 0; i < modes.length; i++) {
for (int j = 0; j <goodModes.length; j++) {
if (displayModesMatch(modes[i], goodModes[j])) {
return modes[i];
}
}
}
return null;
}
public DisplayMode getCurrentDisplayMode() {
return device.getDisplayMode();
}
public boolean displayModesMatch(DisplayMode mode1, DisplayMode mode2) {
if (mode1.getWidth() != mode2.getWidth() || mode1.getHeight() != mode2.getHeight()) return false;
if (mode1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI && mode2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI && mode1.getBitDepth() != mode2.getBitDepth()) return false;
if (mode1.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN && mode2.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN && mode1.getRefreshRate() != mode2.getRefreshRate()) return false;
return true;
}
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(frame);
if (displayMode != null && device.isDisplayChangeSupported()) {
try {
device.setDisplayMode(displayMode);
}
catch (IllegalArgumentException ex) {}
//poprawka dla systemu MacOS X:
frame.setSize(displayMode.getWidth(), displayMode.getHeight());
}
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
frame.createBufferStrategy(2);
}
});
}
catch (InterruptedException | InvocationTargetException ex) {
//ignorowanie
}
}
public Graphics2D getGraphics() {
Window window = device.getFullScreenWindow();
if (window != null) {
BufferStrategy strategy = window.getBufferStrategy();
return (Graphics2D)strategy.getDrawGraphics();
}
else return null;
}
public void update() {
Window window = device.getFullScreenWindow();
if (window != null) {
BufferStrategy strategy = window.getBufferStrategy();
if (!strategy.contentsLost()) strategy.show();
}
//naprawa problemu z kolejką zdarzeń w Linux
Toolkit.getDefaultToolkit().sync();
}
public JFrame getFullScreenWindow() {
return (JFrame)device.getFullScreenWindow();
}
public int getWidth() {
Window window = device.getFullScreenWindow();
if (window != null) return window.getWidth();
else return 0;
}
public int getHeight() {
Window window = device.getFullScreenWindow();
if (window != null) return window.getHeight();
else return 0;
}
public BufferedImage createCompatibleImage(int w, int h, int transparancy) {
Window window = device.getFullScreenWindow();
if (window != null) {
GraphicsConfiguration gc = window.getGraphicsConfiguration();
return gc.createCompatibleImage(w, h, transparancy);
}
return null;
}
public void restoreScreen() {
Window window = device.getFullScreenWindow();
if (window != null) window.dispose();
device.setFullScreenWindow(null);
}
}
Animation:
package graphic;
import java.awt.Image;
import java.util.ArrayList;
/*
* Klasa Animation - zarządzanie serią rysunków (ramek) oraz czasem ich wyświetlania
*/
public final class Animation {
private ArrayList frames;
private int currFrameIndex;
private long animTime;
private long totalDuration;
public Animation() {
frames = new ArrayList();
totalDuration = 0;
start();
}
//dodaje do animacji rysunek o określonym czasie wyświetlania
public synchronized void addFrame(Image image, long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}
//uruchamia animację od początku
public synchronized void start() {
animTime = 0;
currFrameIndex = 0;
}
//modyfikuje bieżącą ramkę animacji
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;
if (animTime >= totalDuration) {
animTime = animTime % totalDuration;
currFrameIndex = 0;
}
while (animTime > getFrame(currFrameIndex).endTime) currFrameIndex++;
}
}
//pobiera bieżący rysunek z animacji. Zwraca null, jeżeli animacja nie zawiera rysunków
public synchronized Image getImage() {
if (frames.isEmpty()) return null;
else return getFrame(currFrameIndex).image;
}
private AnimFrame getFrame(int i) {
return (AnimFrame)frames.get(i);
}
private class AnimFrame {
Image image;
long endTime;
public AnimFrame(Image image, long endTime) {
this.image = image;
this.endTime = endTime;
}
}
}
Sprite:
package graphic;
import java.awt.Image;
public class Sprite {
private Animation anim;
//pozycja w pikselach
private float x;
private float y;
//prędkość w pikselach na milisekundę
private float dx;
private float dy;
//nowy obiekt Sprite z obiektem Animation
public Sprite(Animation anim) {
this.anim = anim;
}
//aktualizuje Animation dla bieżącego Sprite oraz swoją pozycję na podstawie prędkości
public void update(long elapsedTime) {
x += dx * elapsedTime;
y += dy * elapsedTime;
}
//zwracanie współrzędnych x i y obiektu Sprite
public float getX() {
return x;
}
public float getY() {
return y;
}
//ustawianie współrzędnych x i y obiektu Sprite
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
//zwracanie szerokości i wysokości obiektu Sprite na podstawie rozmiaru bieżącego rysunku
public int getWidth() {
return anim.getImage().getWidth(null);
}
public int getHeight() {
return anim.getImage().getHeight(null);
}
//zwraca prędkość w poziomie i w pionie obiektu Sprite w pikselach na milisekundę
public float getVelocityX() {
return dx;
}
public float getVelocityY() {
return dy;
}
//ustawia prędkość w poziomie i wpionie obiektu Sprite w pikselach na milisekundę
public void setVelocityX(float dx) {
this.dx = dx;
}
public void setVelocityY(float dY) {
this.dy = dy;
}
//zwraca bieżący rysunek dla tego obiektu Sprite
public Image getImage() {
return anim.getImage();
}
}
Reszta importów pochodzi ze standardowych bibliotek Javy.
Wynik działania:
1.
bgImage = loadImage("images/background.jpg");
...
g.drawImage(bgImage, 0, 0, null);
nie rysuje tła
2. Migotanie obrazu. Nie powinno się tak dziać, ponieważ w pliku ScreenManager mamy
public Graphics2D getGraphics() {
Window window = device.getFullScreenWindow();
if (window != null) {
BufferStrategy strategy = window.getBufferStrategy();
return (Graphics2D)strategy.getDrawGraphics();
}
else return null;
}
- Nie widać animacji, nie ma obrazków, nic się nie przesuwa
Innymi słowy, kupa. Program, z którego korzystam, to NetBeans, nie wyświetla on żadnych błędów.
Książka, z której się uczę: "Java. Tworzenie gier", wyd. Helion. Co ciekawe, gdy książka była wydana, była jeszcze poprzednia wersja Javy, a ja swoją wersję pobrałem po tym, gdy wydano książkę, więc posiadam tą samą wersję lub nowszą, czyli to nie jest wina wersji Javy.