Wie kann ich ein Sprite-Sheet-basiertes Animationssystem erstellen? [geschlossen]


7

Ich möchte Animationen basierend auf einem Sprite-Blatt erstellen, aber ich bin nicht in der Lage, es zu implementieren.

Die wichtigsten Dinge, für die ich meinen Code tun muss, sind:

  • Spielen Sie eine Reihe von Bildern einmal durch (z. B. eine Sprunganimation).
  • Durchlaufen Sie fortlaufend eine Reihe von Bildern (z. B. eine Laufanimation).
  • Ändern Sie die Geschwindigkeit einer Animation

Wie kann ich ein solches System implementieren?

Antworten:


18

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.

Geben Sie hier die Bildbeschreibung ein

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 :)

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.