LayoutManager RPG Programmieren, eigenes Spielfeld grafisch generieren aus Tileset

Hag2bard

Bekanntes Mitglied
Hallo,
ich möchte für ein Spiel ein Raster haben, dass pro Zelle immer 16x16 Pixel haben soll. In den einzelnen Zellen möchte ich ebenso große Bilder einfügen, so dass sich ein Gesamtbild daraus zusammen setzt.
Das ist zumindest meine Idee, wie ich in einem RPG Spiel variable Maps generieren kann.

POKEMON_ROT_IMG_06.jpg

Dieses Spiel setzt sich (ist in Assembler geschrieben) aus verschiedenen Tiles aus einem Tileset zusammen. Ich habe ein TileSet und möchte das Spielfeld zusammensetzen.
Meine erste Idee war es eine Art Layout zu erstellen, welches ein Raster erstellt. Meinetwegen erstmal in einer Größe von 10x10 Zellen. (In Pokemon wird es Blöcke genannt) oder in Pixel 160x160.

Wie mach ich sowas?
 

Anhänge

  • tileset-advance.png
    tileset-advance.png
    207,3 KB · Aufrufe: 2
Ich würde die Frage einfach umgedreht zurück geben und fragen: Wo ist denn im Augenblick dein Problem?

Hast Du Dich schon etwas damit auseinander gesetzt, wie Du Bilder laden, erzeugen, ändern kannst? Da gibt es sehr viel online zu, daher würde ich da nicht tief drauf eingehen. Eine mögliche Basis ist BufferedImage.

Um ein solches Image zu verändern, kann man sich ein Graphics2D Objekt erstellen:

Mit der Methode drawImage lässt sich dann ein Bild in ein anderes einfügen. Wenn Du alle Tiles in einem Bild hast, dann wäre diese Methode vermutlich die, die Du brauchst:
Es wird dann sowohl ein Zielbereich als auch ein Source Bereich angegeben. (Sprich: Du musst wissen, in welchem Bereich Deine Tile ist, aber das ist ja klar denke ich mal.)

Bleibt nur noch die Funktionalität: Laden eines Bildes. Das kann z.B. mittels ImageIO erfolgen:
 
Mit den Links kann ich leider nichts anfangen. Ich weiß noch garnicht wie ich mein Problem aufteilen soll.
Ich glaube ich hab mich da auch sehr ungünstig ausgedrückt.

Nochmal zur Wiederholung. Ich möchte in JavaFX ein Bild auf dem Bildschirm generieren, dass aus anderen kleinen Bildchen zusammengesetzt wird.
Dafür habe ich ein Tileset von einem Pokemon Spiel.

Ich habe hier im Prinzip 2 Hauptprobleme:

- Definierte Bereiche aus einem Bild nutzen und
- Ein Bild aus mehreren kleinen Bildern zusammen setzen (am besten diese Bereiche)

Um mal ganz klein anzufangen möchte ich:

Ein Bild mit der Größe 32x32 Pixel erstellen.
Auf dieses Bild soll an Stelle 0,0 ein Bild mit 16x16 Pixel
An Stelle 0,16 ein Bild mit 16 Pixel
An Stelle 16,0 ein Bild mit 16 Pixel
An Stelle 16,16 ein Bild mit 16 Pixel

So habe ich ein Bild mit 4 Zellen.

Das wäre erstmal das erste Problem was ich klären wollen würde.

Also habe ich als erstes die Stage.
Dieser übergebe ich eine Scene.
Dieser übergebe ich ein Layout (z.b. HBox)

Und zum Layout müsste ich dann 4 Bilder hinzufügen und diese auf der Scene positionieren.
Ein Bild bekomme ich hinzugefügt

Java:
        Image image = new Image("test.png");   // Hier erstelle ich ein Image-Objekt und übergebe dem Konstruktor ein Bild
        height = image.getHeight();               //
        width = image.getWidth();                 //Hier hole ich die Größe des Bildes
        System.out.println("Höhe: " + height);    // Und gebe Sie hier aus
        System.out.println("Breite: " + width);
        ImageView imgView = new ImageView();      //Hier erstelle ich ein ImageView-Objekt
        imgView.setImage(image);                  //Hier setze ich das Image-Objekt in das ImageView-Objekt

        Rectangle2D viewportRect = new Rectangle2D(0, 0, width, height/2); //v=x v1=y v2=Breite v3=Höhe  Hier erstelle ich ein Viereck mit den Maßen
                                                                                //des Bildes und der Position 0,0
                                                                                //v = Bild nach links verschieben mit +
                                                                                //v1 = Bild nach oben verschieben mit +

        imgView.setViewport(viewportRect);                                      //Keine Ahnung was hier gemacht wird

  HBox box = new HBox();                                                  //Hier nutze ich das HBox Layout
        box.setPrefSize(400,100);                                        //und setze die Größe auf 400x100
        box.getChildren().add(imgView);                                         //Das Imageview-Objekt wird dem Layout hinzugefügt

Bei mir hapert es schon dabei die Bilder zu positionieren
Wenn ich ein zweites ImageView erstelle, also so hier:

Java:
        ImageView imgView2 = new ImageView();
        imgView2.setImage(image);
        Rectangle2D viewportRect2 = new Rectangle2D(0,-height/2,width,height/2);
        imgView2.setViewport(viewportRect2);
        box.getChildren().add(imgView2);

Dann bekomme ich nur das erste Bild angezeigt
 
Vergiss mal JavaFX.

Ich habe hier im Prinzip 2 Hauptprobleme:

- Definierte Bereiche aus einem Bild nutzen und
- Ein Bild aus mehreren kleinen Bildern zusammen setzen (am besten diese Bereiche)

Du kannst aus einem BufferedImage via getSubimage(...) rechteckige Teilbilder erhalten.

Nehmen wir mal an, Du hast ein Tileset, das aus 6 x 4 Tiles der Größe 16x16 besteht. Dann hat jedes Tile eine Koordinate (tx,ty) wobei tx zwischen 0 und 5, ty zwischen 0 und 3 verläuft. Durch Multiplikation mit 16 erhältst Du die Pixelkoordinate.

Zum Zusammensetzen: Du holst Dir vom BufferedImage (Zielbild) den Graphics-Context und malst das Tile an die betreffende Koordinate mit drawImage.

Nachtrag:
Meine erste Idee war es eine Art Layout zu erstellen, welches ein Raster erstellt. Meinetwegen erstmal in einer Größe von 10x10 Zellen. (In Pokemon wird es Blöcke genannt) oder in Pixel 160x160.

Wie mach ich sowas?
Genau das wird im o. g. Link gemacht...
 
Zum Testen habe ich erstmal folgendes gebaut:

Java:
        BufferedImage bufferedImage = new BufferedImage(300,450,BufferedImage.TYPE_INT_RGB);
        try {
            bufferedImage = ImageIO.read(new File("test.png"));
        }
        catch (IOException ex) {
            ex.printStackTrace();
        }

Mit dem vorherigen Code konnte er auch auf die test.png zugreifen.
Nun sagt er allerdings

Code:
javax.imageio.IIOException: Can't read input file!
    at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1308)
    at pokemon.MainView.<init>(MainView.java:61)
    at pokemon.Main.start(Main.java:17)

Irgendwas stimmt hier nicht, kann das sein, dass das verbugt ist?
 
Java:
package pokemon;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;

public class MainView2 {
    BufferedImage bufferedImage = null;


    public MainView2() {
        bufferedImage = new BufferedImage(300, 450, BufferedImage.TYPE_INT_RGB);
        try {
            bufferedImage = ImageIO.read(getClass().getResource("test.png"));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

Ich habe versucht mit diesem Code ein Bild in ein BufferedImage zu laden.
Aber ich bekomme folgende Fehlermeldung:

Code:
Exception in thread "main" java.lang.IllegalArgumentException: input == null!
    at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1400)
    at pokemon.MainView2.<init>(MainView2.java:16)
    at pokemon.Main2.main(Main2.java:5)


Ist das BufferedImage wirklich sinnvoll? Das scheint Ausmaße anzunehmen, bevor man überhaupt mal ein Bild geladen bekommt, dass ich so langsam glaube, dass es evtl deprecated ist oder sonstiges.
Irgendwas stimmt damit nicht, wo liegt hier das Problem?
 
Das klingt danach, dass der Aufruf getClass().getResource("test.png") die Ressource nicht gefunden hat.

Wo liegt die Datei test.png? Sie muss in dem Ordner der Ressourcen sein (In eclipse ist das der src Ordner, bei Gradle/Maven wäre es src/main/resources) und dort innerhalb des Ordners pokemon.

(Wobei Code und Stacktrace nicht ganz passen - das ist ja nicht Zeile 16 sondern Zeile 14 - Du hast da also ggf. schon etwas geändert!)
 
Das stimmt, ich hab den Code formatiert.
Meine test.png liegt hier:
C:\Users\dch\IdeaProjects\Pokemon\src\test.png

Der Sourcecode liegt hier
C:\Users\dch\IdeaProjects\Pokemon\src\pokemon\MainView2.java

OK verstehe, meine test.png muss ich

C:\Users\dch\IdeaProjects\Pokemon\src\pokemon\
 

Anhänge

  • 1633702166001.png
    1633702166001.png
    155,3 KB · Aufrufe: 1
Ja, du kannst das Bild verschieben. Oder Du passt die Angabe im Code an. wenn Du ein / voran stellst, dann würde es auch geladen.
 
Ok ich habe jetzt hinbekommen das Bild zu laden und die Methode getSubimage() funktioniert auch.
Da ich heute leider krank bin, schaffe ich es nicht klar zu denken, aber der Rest sollte reine Logik sein.

Als nächstes werde ich mich damit beschäftigen, wie man ein Bild neu zusammen setzt.
 
Hallo nochmal, ich bin schon für meinen Geschmack sehr erfolgreich gewesen.
Ich kann ein BufferedImage laden und es so zerstückeln, wie ich es gerne hätte. (Also ein Teil davon holen)
Nun habe ich also ein BufferedImage, mit einem Maß von 32x32 Pixel und möchte es in ein BufferedImage der Größe 64x64 zeichnen.
Die Methode paint() habe ich allerdings nicht gefunden.

Wie beschreibe ich mein "leeres" BufferedImage der Größe 64x64 mit dem kleinen?
 
Das Malen geht über eine Graphics / Graphics2D instanz. Und die bekommst Du per createGraphics() Aufruf.

Edit: Ist auch schon in meiner Antwort in #2 mit Links und so erläutert ...
 
Und noch ein Snippet:
Java:
Graphics2D g = img.createGraphics();
g.drawImage(upperLeftTile, 0, 0, null);
g.drawImage(upperRightTile, 32, 0, null);
g.drawImage(lowerLeftTile, 0, 32, null);
g.drawImage(lowerRightTile, 32, 32, null);
g.dispose();
sobei es sich bei den xxxTile-Variablen um BufferedImages der Größe 32 x 32 handeln sollte, img ist das Zielbild.
 
Man muss übrigens nicht die einzelnen Bildchen erst ausschneiden - Wenn man das Bild, das alle Bilder enthält, hat, reicht das auch.

Der drawImage Aufruf bekommt dann einfach ein paar mehr Parameter:
Java:
g.drawImage(sourceImage,
           0, 0, 31, 31, // Destination Area
           sourceX1, sourceY1, sourceX2, sourceY1, // Area des source Images
            null); // möglicher Observer

die source Werte muss man für jedes Bild dann natürlich haben - zum Ausschneiden hattest Du die ja bereits.
 
Danke nochmal für die sehr gute Hilfe, ich bin ziemlich weit gekommen.

1634565062293.png
Das tileset welches ich verwende ist ein PNG.

Wie ihr seht, ist die untere Zeile meines Buffered Images nicht fertig bisher, deshalb ist es auch klar, dass der Rest da noch schwarz ist.
Allerdings ist dieses eine Stück vom Dach also der Block mit dem Dach mit schwarzem Hintergrund.
Ich dachte mir dann also, dann muss ich zuerst so einen Rasenblock zeichnen, wie der block links daneben auch und dann das Stück vom Dach darüber zeichnen.
Das funktioniert allerdings nicht, er ersetzt diesem Bereich des Source Images. Das schwarze Stück von dem Tile ist aber transparent.
Gibt es eine Möglichkeit in 2 Layern zu zeichnen?
Java:
g.drawImage(sourceImage,
           0, 0, 31, 31, // Destination Area
           sourceX1, sourceY1, sourceX2, sourceY1, // Area des source Images
            null); // möglicher Observer


edit: Ich habs hinbekommen, hab da ein Fehler mit meinem Array gehabt
Aber so habt ihr zumindest schonmal einen Zwischenstand
 
Zuletzt bearbeitet:
Eine Frage hätte ich noch, ich hab nicht viel Zeit meine Map zu erstellen, ist auch ein ziemlich großer Aufwand, aber sobald ich damit fertig bin, würde ich gerne eine Spielfigur über das Feld bewegen.
Ich habe überlegt dafür 2 BufferedImages zu haben. Eins mit dem Spielfeld und eine Kopie davon auf der ich mit der drawImage Methode die Spielfigur zeichne. Wenn die Spielfigur sich bewegt lade ich das originale BufferedImage in die Kopie und zeichne die Spielfigur dann auf eine neue Position.

Ist die Idee gut oder kann ich ein BufferedImage mit einer Spielfigur erstellen, welches ich dann über das andere BufferedImage bewege?
 

Zurück
Oben