Ich mache 2D-Animationen wie folgt:
Sprite Retrieval Class
Diese Klasse kann entweder eine Bilddatei oder ein Sprite aus einem Sprite-Blatt laden. Es ist sehr selbsterklärend.
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Sprite {
private static BufferedImage spriteSheet;
private static final int TILE_SIZE = 32;
public static BufferedImage loadSprite(String file) {
BufferedImage sprite = null;
try {
sprite = ImageIO.read(new File("res/" + file + ".png"));
} catch (IOException e) {
e.printStackTrace();
}
return sprite;
}
public static BufferedImage getSprite(int xGrid, int yGrid) {
if (spriteSheet == null) {
spriteSheet = loadSprite("AnimationSpriteSheet");
}
return spriteSheet.getSubimage(xGrid * TILE_SIZE, yGrid * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
Animationsklasse
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
public class Animation {
private int frameCount; // Counts ticks for change
private int frameDelay; // frame delay 1-12 (You will have to play around with this)
private int currentFrame; // animations current frame
private int animationDirection; // animation direction (i.e counting forward or backward)
private int totalFrames; // total amount of frames for your animation
private boolean stopped; // has animations stopped
private List<Frame> frames = new ArrayList<Frame>(); // Arraylist of frames
public Animation(BufferedImage[] frames, int frameDelay) {
this.frameDelay = frameDelay;
this.stopped = true;
for (int i = 0; i < frames.length; i++) {
addFrame(frames[i], frameDelay);
}
this.frameCount = 0;
this.frameDelay = frameDelay;
this.currentFrame = 0;
this.animationDirection = 1;
this.totalFrames = this.frames.size();
}
public void start() {
if (!stopped) {
return;
}
if (frames.size() == 0) {
return;
}
stopped = false;
}
public void stop() {
if (frames.size() == 0) {
return;
}
stopped = true;
}
public void restart() {
if (frames.size() == 0) {
return;
}
stopped = false;
currentFrame = 0;
}
public void reset() {
this.stopped = true;
this.frameCount = 0;
this.currentFrame = 0;
}
private void addFrame(BufferedImage frame, int duration) {
if (duration <= 0) {
System.err.println("Invalid duration: " + duration);
throw new RuntimeException("Invalid duration: " + duration);
}
frames.add(new Frame(frame, duration));
currentFrame = 0;
}
public BufferedImage getSprite() {
return frames.get(currentFrame).getFrame();
}
public void update() {
if (!stopped) {
frameCount++;
if (frameCount > frameDelay) {
frameCount = 0;
currentFrame += animationDirection;
if (currentFrame > totalFrames - 1) {
currentFrame = 0;
}
else if (currentFrame < 0) {
currentFrame = totalFrames - 1;
}
}
}
}
}
Die Animationsklasse muss gestartet, gestoppt, neu gestartet und zurückgesetzt werden, um die Animation zu steuern. Sie können leicht einen Booleschen Wert hinzufügen, um zu testen, ob eine Schleife ausgeführt werden soll oder nicht. Ich werde das nicht für dich hinzufügen.
Rahmenklasse
import java.awt.image.BufferedImage;
public class Frame {
private BufferedImage frame;
private int duration;
public Frame(BufferedImage frame, int duration) {
this.frame = frame;
this.duration = duration;
}
public BufferedImage getFrame() {
return frame;
}
public void setFrame(BufferedImage frame) {
this.frame = frame;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
}
Die Frame-Klasse enthält ein Bild und eine diesem Bild zugeordnete Dauer.
Implementierung
Ich implementiere die Animation folgendermaßen:
Player.java
// Images for each animation
private BufferedImage[] walkingLeft = {Sprite.getSprite(0, 1), Sprite.getSprite(2, 1}; // Gets the upper left images of my sprite sheet
private BufferedImage[] walkingRight = {Sprite.getSprite(0, 2), Sprite.getSprite(2, 2};
private BufferedImage[] standing = {Sprite.getSprite(1, 0)};
// These are animation states
private Animation walkLeft = new Animation(walkingLeft, 10);
private Animation walkRight = new Animation(walkingRight, 10);
private Animation standing = new Animation(standing, 10);
// This is the actual animation
private Animation animation = standing;
In Ihrer Update- oder Tick-Methode haben Sie:
animation.update();
In Ihrer Render- oder Zeichenmethode haben Sie:
g.drawImage(animation.getSprite(), x, y, null);
Nehmen wir an, Sie haben MouseListener implementiert
public void mousePressed(MouseEvent e) {
animation = walkLeft;
animation.start();
}
public void mouseReleased(MouseEvent e) {
animation.stop();
animation.reset();
animation = standing;
}
Schlussbemerkungen
Hier ist ein Screenshot des Layouts eines Sprite-Blatts.
Jede Kachel ist 32 x 32 Pixel groß.
Normalerweise würde ich nicht so viel Code schreiben. Ich erinnere mich jedoch, als ich mich bemühte, das herauszufinden, und es ärgerte mich ... Also los geht's!
Viel Glück und ich hoffe, Sie können es Ihren Bedürfnissen entsprechend umgestalten :)