Mousepicking bei Iso-Heightmap/Slopes

Status
Nicht offen für weitere Antworten.

mantax

Mitglied
Hallo
Ich bin gerade dran ein isometrisches Spiel mit unterscheidlichen Höhen zu schreiben. Dargestellt wird auch alles schon ganz gut, jdeoch habe ich probleme beim Mousepicking. Dh ich möchte anhand der Mausposition das Tile darunter bestimmen können.
Bei einer flachen Isomap bekomm ich das locker hin, mit den Schrägen aber gibt es Tiles die quasi "aus dem Raster ragen" und andere Felder überschneiden.
Ich habe schon dieses Tutorial gefunden, komme aber nicht so ganz dahinter.. :-/

Beispiel:


Kann mir jemand helfen?

Vielen Dank schon mal :D
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Ggf. Müßtest du die Frage präzisieren. Wenn ich das richtig an-überflogen habe, wird in dem Tutorial der "Trick" verwendet, dass jedes Tile seine eigene Farbe bekommt, und man dann Anhand der angeklickten Farbe das Tile rausfinden kann... ganz grob und halb im Pseudocode

Malt tiles mit unterschiedlichen Farben
Code:
class TilePanel extends JPanel
{
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.setColor(Color.RED);
        drawTile(0,0);
        g.setColor(Color.GREEN);
        drawTile(0,1);
    }
}

An das TilePanel kann man einen MouseListener hängen, und dort dann sowas wie
Code:
void mouseClicked(...)
{
    BufferedImage image = new BufferedImage(...so groß wie TilePanel...)
    tilePanel.paintComponent(image.getGraphics());
    int pixel = image.getRGB(event.getX(), event.getY());
    Color c = new Color(pixel);
    
    if (c.equals(Color.RED)) clickedTile(0,0);
    if (c.equals(Color.GREEN)) clickedTile(0,1);
}
(wirklich nur GANZ grob - beschreib' ggf. das Problem genauer...)
 

mantax

Mitglied
Hallo Marco
erstmal vielen Dank für deine Antwort!
Ich glaube nicht, dass jedes Tile eine andere Farbe hat, sondern er nur diese Schablone oder "mousemap", wie der Verfasser sie nennt, als Hilfe benutzt um zu ermitteln, ob der Klick innerhalb des Tiles oder nicht erfolgt. Ergibt die Abfrage beispielsweise, dass der Cursor auf einem türkisfarbenen Feld steht, muss das Tile unten drunter angesprochen werden.

Ich verstehe jedoch nicht, wann ich welche Mousemap überprüfen muss und wie sich das bei beliebig vielen Schichten von Tiles verhält. Anscheinend hat bisher noch niemand so etwas implementiert?
 

Marco13

Top Contributor
Hm. Unten schreibt er ja, dass das bei Höhe 2 Probleme geben kann. Man müßte sich mal die TileWalk-Methode ansehen. Aber ehrlich gesagt glaube ich, dass das 1. nicht besonders robust ist was größere Höhenunterschiede angeht, und 2. das ggf. einfacher wäre, wenn man entweder die angesprochene Farb-Map oder gleich eine Methode zur Berechnung des Schnittpunktes zwischen Dreiecken und Picking-Strahl verwenden würde - aber ich kann mich auch täsuchen. Mal schauen, was Quaxli et al. noch dazu sagen :)
 

mantax

Mitglied
Hmm nach reichlicher Überlegung und noch viel mehr Rumprobieren glaube ich jetzt endlich auf nem ganz guten Weg zu sein:

Dazu sei vorweggenommen, dass ich ein 3-dimensionales Array als Map verwende.

Ich ermittle zuerst mit einer Mousemap das Tile der "Grundebene" auf dem es liegen würde, wenn es nicht überdeckt wäre. Dann überprüfe ich rekursiv von der obersten Ebene nach unten gehend, jeweils die 3 Tiles unter dem bereits ermittelten (links unten, mitte unten, rechts unten) mit einem negativen y-Achsen Verschub entsprechend der Z-Ebene, ob diese leer sind oder nicht und prüfe gegebenenfalls wieder mit einer Mousemap. Sobald ich ein Tile habe, steige ich aus der Funktion aus und habe das entsprechende Tile :D

Bin mit der Umsetzung noch nicht ganz fertig, sollte aber funktionieren. Ich hoffe mal, dass meine Methode nicht gerade die rechenintensivste ist, ist nunmal abhängig von der Anzahl der übereinandergelegten Ebenen... wird sich zeigen.

Ich bedanke mich nochmal bei allen die mir geholfen haben! :)

PS: Hehe ich glaube das wird keiner beim ersten Lesen verstehen xD Bei Interresse erkläre ichs nochmal ausführlich und vllt mit ein paar Schaubildern ;)
 

mantax

Mitglied
Ich habe es nun endlich geschafft. Vielen dank für eure Hilfe. Wer an der Lösung interessiert ist, kann sich ja nochmal mit meinem voherigen Post beschäftigen oder mich nochmal anschreiben! :D
 

Quaxli

Top Contributor
Könntest Du wenigstens skizzieren wie Du es machst? So wie oben beschrieben oder anders?
Könnte ja sein, daß jemand mal das gleiche Problem hat und der findet dann hier nur eine unvollständige Hilfe... ;(
 

mantax

Mitglied
Okay.. dazu muss ich aber erst mal vorwegnehmen, dass meine Tiles eine Größe von 48x35 Pixel haben und ich ein 3d Array verwende.

1.Schritt:
Man muss zuerst herausfinden, auf welchem Tile die Maus läge, wenn die Map aus einer Ebene bestünde.
Dazu unterteile ich die Map in Rechtecke von 48x35 und findet heraus in welchem die Maus liegt.

Die Tilekoordinaten errechnen sich nun folgendermaßen
(recx*2/recy)
Da auf diese Weise jedoch nur Tiles mit einer geraden X-Koordinate bestimmen lassen, müssen wir diese Methode überdenken.
Jetzt kommt diese Mousemap ins Spiel:

mask.gif


Finde heraus auf welcher Farbe die Maus liegt.
Jeder Farbe hat eine Bedeutung.
Grün: TileX-1 / TileY-1
Gelb: TileX+1 / TileY-1
Rot: TileX+1
Blau: TileX-1

2.Schritt:
Wir wissen nun welche die Koordinaten für die unterste Ebene dem Mausklick entspricht. Was aber nun wenn Das Tile verdeckt wird?
Dazu legen wir uns nun eine for-Schleife an die z (Anzahl der Ebenen) bis z > 0 herunterzählt. In jedem durchlauf werden nun 4 Tiles der jeweiligen zEbene per Mousemap abgefragt. Undzwar das Tile direkt unten drunter, linksunten, rechtsunten und oben (in dieser Reihenfolge!), weil diese Tiles die einzigen sind, die unser bereits ermitteltes Tile überdecken können. Lästig ist nur, dass wir für jede Form die ein Tile annehmen kann, eine Mousemap für Unten, Links und Rechts anlegen müssen, das macht man ja aber auch nur einmal.

Mousemaps sind ne klasse sache. Ich habe die ganze Zeit mit Berechnungen rumgespielt, aber es ist meiner Meinung nach fast nicht möglich damit was gescheites (pixelgenaus!) hinzubekommen, da die Tiles eben diese Schrägen haben (2 auf 1 Pixel).

Hier mal zum besseren Verständnis meine Tiles und die dazugehörigen Mousemaps.



So und zu guter letzt und ein bissl Code dazu :D

Java:
public int[] getTile(int[] mouse) {
    // welches rechteck?
    int recx = (int) Math.round(mouse[0] / 48);
    int recy = (int) Math.round(mouse[1] / 23);

    int[] pos = new int[3];

    if ((mask.getRGB(mouse[0] - recx * 48, mouse[1] - recy * 23) == -1)) {
      // weiss
      pos[0] = recx * 2;
      pos[1] = recy;
    } else if ((mask.getRGB(mouse[0] - recx * 48, mouse[1] - recy * 23) == -16711936)) {
      // grün
      pos[0] = recx * 2 - 1;
      pos[1] = recy - 1;
    } else if ((mask.getRGB(mouse[0] - recx * 48, mouse[1] - recy * 23) == -256)) {
      // gelb
      pos[0] = recx * 2 + 1;
      pos[1] = recy - 1;
    } else if ((mask.getRGB(mouse[0] - recx * 48, mouse[1] - recy * 23) == -65536)) {
      // rot
      pos[0] = recx * 2 + 1;
      pos[1] = recy;
    } else if ((mask.getRGB(mouse[0] - recx * 48, mouse[1] - recy * 23) == -16776961)) {
      // blau
      pos[0] = recx * 2 - 1;
      pos[1] = recy;
    }


      pos = overlappingTiles(pos, new int[]{mouse[0] - recx * 48, mouse[1] - recy * 23});
    
    return pos;
  }

  private int[] overlappingTiles(int[] pos, int[] mouse) {

    for (int z = this.map.length - 1; z > 0; z--) {
      // X gerade oder ungerade? -> Tile versetzt oder nicht?
      if (pos[0] % 2 == 0) {
        // U
        if (pos[1] + z < this.map[0].length) {
          if (!this.map[z][pos[1] + z][pos[0]].startsWith("-,-")) {
            if (mask2.getRGB(mouse[0] + 48 * Integer.parseInt(this.map[z][pos[1] + z][pos[0]].split(",")[1]), mouse[1]) == -1) {
              return new int[]{pos[0], pos[1] + z, z};
            }
          }
        }
        // R
        if (pos[1] + z - 1 < this.map[0].length && pos[0] + 1 < this.map[0][0].length) {
          if (!this.map[z][pos[1] + z - 1][pos[0] + 1].startsWith("-,-")) {
            if (mask2.getRGB(mouse[0] + 48 * Integer.parseInt(this.map[z][pos[1] + z - 1][pos[0] + 1].split(",")[1]), mouse[1] + 23) == -1) {
              return new int[]{pos[0] + 1, pos[1] + z - 1, z};
            }
          }
        }
        // L
        if (pos[1] + z - 1 < this.map[0].length && 0 <= pos[0] - 1) {
          if (!this.map[z][pos[1] + z - 1][pos[0] - 1].startsWith("-,-")) {
            if (mask2.getRGB(mouse[0] + 48 * Integer.parseInt(this.map[z][pos[1] + z - 1][pos[0] - 1].split(",")[1]), mouse[1] + 46) == -1) {
              return new int[]{pos[0] - 1, pos[1] + z, z};
            }
          }
        }
        // O
        if (pos[1] + z - 1 < this.map[0].length) {
          if (!this.map[z][pos[1] + z - 1][pos[0]].startsWith("-,-")) {
            return new int[]{pos[0], pos[1] + z - 1, z};
          }
        }
      } else {
        // U
        if (pos[1] + z < this.map[0].length) {
          if (!this.map[z][pos[1] + z][pos[0]].startsWith("-,-")) {
            if (mask2.getRGB(mouse[0] + 48 * Integer.parseInt(this.map[z][pos[1] + z][pos[0]].split(",")[1]), mouse[1]) == -1) {
              return new int[]{pos[0], pos[1] + z, z};
            }
          }
        }
        // R
        if (pos[1] + z < this.map[0].length && pos[0] + 1 < this.map[0][0].length) {
          if (!this.map[z][pos[1] + z][pos[0] + 1].startsWith("-,-")) {
            if (mask2.getRGB(mouse[0] + 48 * Integer.parseInt(this.map[z][pos[1] + z][pos[0] + 1].split(",")[1]), mouse[1] + 23) == -1) {
              return new int[]{pos[0] + 1, pos[1] + z, z};
            }
          }
        }
        // L
        if (pos[1] + z < this.map[0].length && 0 <= pos[0] - 1) {
          if (!this.map[z][pos[1] + z][pos[0] - 1].startsWith("-,-")) {
            if (mask2.getRGB(mouse[0] + 48 * Integer.parseInt(this.map[z][pos[1] + z][pos[0] - 1].split(",")[1]), mouse[1] + 46) == -1) {
              return new int[]{pos[0] - 1, pos[1] + z, z};
            }
          }
        }
        // O
        if (pos[1] + z - 1 < this.map[0].length) {
          if (!this.map[z][pos[1] + z - 1][pos[0]].startsWith("-,-")) {
            return new int[]{pos[0], pos[1] + z - 1, z};
          }
        }
      }
    }
    // nicht überdeckt
    return pos;
  }

PS: Ach ja diese Dinger hier "...startsWith("-,-")) {.." sollen keine Smilys sein, sondern überpüfen ob das Tile leer ist :D
 
Zuletzt bearbeitet:
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
W Heightmap -> Landscape Spiele- und Multimedia-Programmierung 3

Ähnliche Java Themen

Neue Themen


Oben