# RPG - Spielfigur soll sich nicht frei bewegen können. Anregugen wären gerne gesehen^^



## Fival (17. Jul 2010)

Hallo liebe Java Progammierer,
ich progammiere seit einiger Zeit an einem RPG-Spiel und habe auch schon einiges geschafft daran,
nun ja, jetzt bin ich jedenfalls auf ein Problem gestoßen wo ich alleine nicht weiter weiß.
Ok , es ist nicht wirklich ein Problem, eher eine Frage des Aufwands.
Also: ich habe ein Panel mit einer Bilddatei als Hintergrund und eine Figur die man mit nem KeyListener auch darauf bewegen kann, funktioniert auch wunderbar alles. Jedoch ist mein Problem jetzt, dass sich diese Spielfigur natürlich über alles bewegen kann, sprich Häuser, Bäume etc.
Meine erste (wahrscheinlich primitive Idee) war es nun, für jede Map(also für jedes Bild) eine java datei zu schreiben die nur aus gaaaaaaaanz vielen ifs besteht wo ständig bei der Bewegung überprüft wird ob die neue Position noch gültig ist. zB so: 

```
public static boolean couldRun(int[] position) {
        int y = position[1];
        int x = position[0];
        boolean couldRun = true;
        if (y <= 30) {
            couldRun = false;
        }
        if ((y >= 70) && (y <= 116)) {
            if (x >= 357 && x <= 414) {
                couldRun = false;
            }

        }
        return couldRun;
    }
```
Das könnte natürlich funktionieren, ist aber seeeeeeehr aufwendig und die Leistung leidet natürlich auch tierisch darunter. Meine andere Idee wäre einfach ein boolean aray zu machen wo jedes Pixel mit true/oder falls abgespeichert ist. Das wäre für die Leistung natürlich besser , aber immer noch schrecklich zu schreiben.
Meine Frage ist also nun , wie man das vllt viel einfacher und besser machen könnte? Hat dazu einer eine Anregung oder Idee und ist mein Ansatz die Map einfach als Hintergrund zu nehmen(was ja erstma einfach erschien) schon grunsätzlich falsch?
Sollte ich vllt jedes Haus, Baum etc als eigenes Label machen? Das war eine Überlegung, aber irgendwie weiter brachte mich das nicht.
Ich würde mich freuen wenn mir hier jemand einen nützlichen Tipp geben könnte dazu :toll:


----------



## Landei (17. Jul 2010)

Du arbeitest doch sicher mit Tiles, oder? Dann mach für jedes Tile ein gleichgroßes schwarz-weißes Image, wo die "verbotenen" Bereiche schwarz sind. Wenn deine Spielfigur im jeweiligen Tile ist, prüftst du vor jeder Bewegung ab, ob ein undurchsichtiger Pixel der Spielfigur über einem schwarzen Pixel des Tiles zu liegen käme, und blockiere dann die Bewegung.

Hier mehr zu tile-basierten Games Chapter 13. An Isometric Tile Game


----------



## Fival (17. Jul 2010)

Danke für deine Antwort Landei;
nein bisher arbeite ich nicht mit Tiles, werde ich mir aber mal angucken:rtfm:, was ich beim kurzen Angucken aber gesehen  habe, entspricht diese Methode ja praktisch einer Rasterung des Spielfeldes. Bisher ist das nur ein Hintergrundbild(die Karte an sich) und die Spielfigur kann sich pixelgenau darauf bewegen, das wollte ich aber auch so lassen, also nicht wie bei CivII, dass sich die Spielfigur ein ganzes Feld immer bewegt.Ich werde mich damit mal auseinader setzten, aber gibt es noch andere Möglichkeiten? Danke schonmal im vorraus


----------



## Ruzmanz (17. Jul 2010)

Hast du nur ein Bild auf deinem Panel oder mehrere? Bei einem Bild kannst du nichts machen, da du nicht sagen kannst ob du nun auf dem Weg stehst oder in einem Baum (Zumindest wenn es sehr viele Farben besitzt). Wenn du es einfach und schnell machen willst, dann solltest du eine zweite Karte mit einem Bildbearbeitungsprogramm erstellen. Bereiche, die man nich betreten kann schwarz anmalen und den Rest weiß. Damit kannst du dann die Positionen überprüfen und gegebenfalls blockieren. Etwas leichteres ist mir nicht bekannt.


----------



## babuschka (17. Jul 2010)

Hallo,

ich habe in verschiedenen Spielen auch schon gesehen, dass dort mit "ForbiddenAreas" beziehungsweise "Shapes" gearbeitet wurde - das sind spezielle Objekte, die frei auf der Karte positionieren werden können. Befindet sich ein Spieler darin, so weiß man, dass er dort nicht hin darf oder stellt je nach Art des Shapes fest, was getan werden soll (z.B. in Verbindung mit Triggern). Allerdings ist das ein sehr aufwändiges System...

Wenn Du allerdings eine TileMap verwendest, sollte die angesprochene "Schattenkarte" wohl die effizienteste Lösung sein, da Du hier bereits ein vorgefertigtes Tileset hast und dazu nur die "Schattentiles" noch entwerfen musst. In der Schattenkarte kannst Du dann mit verschiedenen Farben verschiedene Bereiche markieren und darin verschiedenste Events auslösen. Auch spricht in einer TileMap nichts dagegen, dass Du dich pixelgenau bewegen kannst - alles eine Frage der Umsetzung. Schau Dir am Besten einmal das verlinkte Tutorial an (sehr gute Seite!) oder das von Quaxli (einfach suchen).

Gruß,

JohnMcLane


----------



## Fival (17. Jul 2010)

Vielen Dank für eure Hilfe,
mit der "Schattenkarte" funktioniert das auch wundbar und ist einfach zu implementieren.
Jedoch hab ich dadurch, wie auch bei meiner ersten Lösung mit den ifs ein arges Performance Problem.
Bewegt man sich duch antippen der Tasten an eine Wand, kann man auch sofort wieder weg gehen,
läuft man jedoch mit gedrückter Taste gegen eine Wand, bleibt das Spiel, sekundenlang hängen, oder die Spielfigur springt(ruckeln in der art^^). Das liegt wohl am KeyListener denk ich mal, der bestimmt einen Buffer nutzen wird und n mal meine couldMove Methode öffnet. Kann man den Buffer verringern? Liegt das überhaupt daran?

Ich hatte schon versucht die alte Position zu speichern und wenn wieder die selbe Position angesteuert werden soll, soll einfach nichts gemacht, werden, brachte auch etwas, aber immer noch nicht das gewünschte Resultat.

Falls mal jemand Lust hat die besagten Methoden die für die Bewegung bisher zuständig sind sich anzugucken:
- Die Kommentare sind vllt nicht gut geschrieben, mein Englisch ist aber auch nicht sehr gut.^^
- Das Ganze arbeitet in einem Gui bzw in einem Panel.

Die couldMove Methode // um zu überprüfen ob man an die neue Position gehen kann

```
private boolean couldMove(int[] position) {
        int rgb = imageToCheckMovement.getRGB(position[0], position[1]);
        Color c = new Color(rgb);
        if (c.equals(Color.BLACK)) {
            return false;
        }
        else return true;
    }
```

Die movePlayer Methode - Die eigentliche Methode die den Spieler bzw. dessen Label bewegt.
Manches davon ist nur zu testzwecken drinn, ist aber auch kommentiert

```
/**
     * change the position to the direction one times the
     * key listener use the method.
     * @param direction
     */
    private void movePlayer(String direction) {
        int[] position = this.chara.getPosition();
        int[] oldPosition = new int[2];
        if (direction.equals("right")) {
            position[0] = position[0] + 1;
        }
        if (direction.equals("left")) {
            position[0] = position[0] - 1;
        }
        if (direction.equals("down")) {
            position[1] = position[1] + 1;
        }
        if (direction.equals("up")) {
            position[1] = position[1] - 1;
        }
        if (direction.equals("right-down")) {
            position[1]++;
            position[0]++;
        }
        if (direction.equals("left-down")) {
            position[0]--;
            position[1]++;
        }
        if (direction.equals("right-up")) {
            position[0]++;
            position[1]--;
        }
        if (direction.equals("left-up")) {
            position[0]--;
            position[1]--;
        }
        //if the position do not change by movement of couldMove do nothig
        //to improve perfomance.
        if ((oldPosition[0] != position[0])
                && (oldPosition[1] != position[1])) {
            oldPosition[0] = position[0];
            oldPosition[1] = oldPosition[1];

            if (couldMove(position)) {
                map.removeAll();
                this.chara.setPosition(position);
                ImageIcon playerPic = new ImageIcon("pics/player.gif");
                JLabel player = new JLabel(playerPic);
                player.setBounds(this.chara.getPosition()[0], this.chara.getPosition()[1], 30, 40);
                map.add(player);
                //testings
                int rgb = imageToCheckMovement.getRGB(position[0], position[1]);
                Color c = new Color(rgb);
                test.setText("" + position[0] + "/" + position[1] + c);
                map.updateUI();
            }
        }
    }
```

Und der eigentliche KeyListener der auf die Eingaben im Panel reagiert


```
class KeyMovement implements KeyListener {

        private boolean right = false;
        private boolean left = false;
        private boolean down = false;
        private boolean up = false;

        public void keyTyped(KeyEvent e) {
        }

        /**
         * key listener for right, down, up and left key.
         * The key listener moves the player to the input direction.
         * Also the right-down movement etc.
         * @param e
         */
        public void keyPressed(KeyEvent e) {
            if ((right == true) && (down == true)) {
                movePlayer("right-down");
            }
            if ((left == true) && (down == true)) {
                movePlayer("left-down");
            }
            if ((left == true) && (up == true)) {
                movePlayer("left-up");
            }
            if ((right == true) && (up == true)) {
                movePlayer("right-up");
            }
            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                right = true;
                movePlayer("right");
            }
            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                left = true;
                movePlayer("left");
            }
            if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                down = true;
                movePlayer("down");
            }
            if (e.getKeyCode() == KeyEvent.VK_UP) {
                up = true;
                movePlayer("up");
            }
        }
        // if a key released set the flag for the key of false.

        public void keyReleased(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                right = false;
            }
            if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                down = false;
            }
            if (e.getKeyCode() == KeyEvent.VK_UP) {
                up = false;
            }
            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                left = false;
            }
        }
    }
}
```


----------



## Marco13 (17. Jul 2010)

Ein paar Punkte...: WAS genau da gemacht wird oder werden sollte, ist schwer nachzuvollziehen, aber...

```
if ((right == true) && (down == true)) {
    movePlayer("right-down");
}
```
kann (und sollte) man schreiben als

```
if (right && down) {
    movePlayer("right-down");
}
```
Ob diese Bewegungen unbedingt als Strings übergeben werden müssen ... naja, es geht, aber ... man könnte ein enum verwenden, oder gleich die boolean-Werte, was dann evtl. auch die movePlayer-Methode vereinfachen würde, z.B. mit sowas wie

```
void movePlayer(boolean up, boolean down, boolean left, boolean right)
{
...
}
```
so dass dann in der  keyPressed gar keine Abfragen mehr gemacht, sondern einfach die werte weitergereicht werden:

```
movePlayer(up, down, left, right);
```
aber das muss man sich nochmal genauer ansehen.

In der movePlayer gibt's auch einige :autsch:'s:

```
ImageIcon playerPic = new ImageIcon("pics/player.gif");
                JLabel player = new JLabel(playerPic);
                player.setBounds(this.chara.getPosition()[0], this.chara.getPosition()[1], 30, 40);
                map.add(player);
```
Bei jeder Bewegung ein neues ImageIcon zu laden und hinzuzufügen sieht sehr schräg aus. Das sollte nur EINmal erstellt werden, und danach wird es nur noch bewegt. Genaugenommen sollte auch nicht das ImageIcon bewegt werden, sondern nur der Spieler (das ImageIcon würde sich eher "passiv mitbewegen", z.B. über einen Listener oder so)


```
map.updateUI();
```
Schau die mal in der API-Doku an, wozu diese Methode gut ist. Vermutlich wolltest du nur ein validate() und/oder repaint() ...


----------



## Landei (17. Jul 2010)

Fival hat gesagt.:


> Danke für deine Antwort Landei;
> nein bisher arbeite ich nicht mit Tiles, werde ich mir aber mal angucken:rtfm:, was ich beim kurzen Angucken aber gesehen  habe, entspricht diese Methode ja praktisch einer Rasterung des Spielfeldes. Bisher ist das nur ein Hintergrundbild(die Karte an sich) und die Spielfigur kann sich pixelgenau darauf bewegen, das wollte ich aber auch so lassen, also nicht wie bei CivII, dass sich die Spielfigur ein ganzes Feld immer bewegt.Ich werde mich damit mal auseinader setzten, aber gibt es noch andere Möglichkeiten? Danke schonmal im vorraus



Tilebasiert heißt erst mal nur, dass das Spielfeld resourcenschonend aus einzelnen Tiles zusammengesetzt wird. In den meisten Spielen kann man sich trotzdem frei bewegen.


----------



## Fival (17. Jul 2010)

Danke an Marco13 
Klar, deine erwähnten Punkte sind verständlich und habe ich auch überarbeitet.
Das Performanceproblem besteht jedoch immer noch.
Die Methoden sehen jetzt wie folgt aus:

Die movePlayer Methode 

```
/**
     * change the position to the direction one times the
     * key listener use the method.
     * @param direction
     */
    private void movePlayer(boolean up, boolean down, boolean right, boolean left) {
        //the aray of the position. position[0] = x-axis , position[1] = y-axis.
        int[] position = this.chara.getPosition();
        int[] oldPosition = new int[2];

        if (right && down) {
            //increase the x position
            position[1]++;
            //increase the y position
            position[0]++;
        }
        if (left && down) {
            //decrease the x position
            position[0]--;
            //increase the x position
            position[1]++;
        }
        if (right && up) {
            //increase the x position
            position[0]++;
            //decrease the y position
            position[1]--;
        }
        if (left && up) {
            // decrease the x position
            position[0]--;
            // decrease the y position
            position[1]--;
        }
        if (right) {
            //increase the x position
            position[0] = position[0] + 2;
        }
        if (left) {
            //decrease the x position
            position[0] = position[0] - 2;
        }
        if (down) {
            //increase the y position
            position[1] = position[1] + 2;
        }
        if (up) {
            //decrease the y position
            position[1] = position[1] - 2;
        }
        //if the position do not change by movement of couldMove do nothing
        //to improve perfomance
        if ((oldPosition[0] != position[0])
                && (oldPosition[1] != position[1])) {
            oldPosition[0] = position[0];
            oldPosition[1] = oldPosition[1];
            //cheack if the player could move into the direction
            if (couldMove(position)) {
                //save the moved position
                this.chara.setPosition(position);
                //set the player label to the new position
                player.setBounds(this.chara.getPosition()[0], this.chara.getPosition()[1], 30, 40);
                //testings
                int rgb = imageToCheckMovement.getRGB(position[0], position[1]);
                Color c = new Color(rgb);
                test.setText("" + position[0] + "/" + position[1] + c);

                map.repaint();
            }
        }
    }
```

die couldMove Methode (unverändert)


```
private boolean couldMove(int[] position) {
        //to check the postion of the feet and not left up(start position of the label)
        int[] positionsCheck = new int[2];
        positionsCheck[0] = position[0] + 15;
        positionsCheck[1] = position[1] + 30;
        int rgb = imageToCheckMovement.getRGB(positionsCheck[0], positionsCheck[1]);
        Color c = new Color(rgb);
        if (c.equals(Color.BLACK)) {
            return false;
        }
        else return true;
    }
```

der KeyListener


```
class KeyMovement implements KeyListener {

        private boolean right = false;
        private boolean left = false;
        private boolean down = false;
        private boolean up = false;

        public void keyTyped(KeyEvent e) {
        }

        /**
         * key listener for right, down, up and left key.
         * The key listener moves the player to the input direction.
         * Also the right-down movement etc.
         * @param e
         */
        public void keyPressed(KeyEvent e) {
            //if right key is pressed set right to true
            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                right = true;
            }
            //if left key is pressed set left to true
            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                left = true;
            }
            //if down key is pressed set down to true
            if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                down = true;
            }
            //if up key is presses set up to true
            if (e.getKeyCode() == KeyEvent.VK_UP) {
                up = true;
            }
            //move the player to direction
            movePlayer(up, down, right, left);
        }
        // if a key released set the flag for the key of false.

        public void keyReleased(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                right = false;
            }
            if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                down = false;
            }
            if (e.getKeyCode() == KeyEvent.VK_UP) {
                up = false;
            }
            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                left = false;
            }
        }
    }
}
```
Wieso reagiert das Spiel nach Kollision so komisch? also ruckeln oder springen der Figur...
Ist das mit den Tales wirklich effizienter? Es muss ja trotzdem eine Methode existieren die die Farben einer "Schattenkarte" sich anschaut.


----------



## Ruzmanz (17. Jul 2010)

Mal sehen ob ich das richtig interpretiere:

1. Bewegung nach Rechts
2. Um 1 nach Rachts verschieben.
3. Kollidiert mit dem Objekt? -> Ja
4. Auf alte Position zurückschieben.
5. Lässt man die Pfeiltaste gedrückt -> Endlosschleife, da es zu Punkt 1 springt. Obwohl es nur 1-2 Pixel sind, sieht man das Springen deutlich, da es mehrmals in der Sekunde wiederhohlt wird.

Gelöst habe ich das Problem auch einmal, aber da muss ich erst nachschauen 

PS:
- Bei einer Kollision an den Seiten wurde die horizontale Geschwindigkeit auf 0 gesetzt. Erst wenn man in die entgegengesetzte Richtung laufen wollte, ist die Geschwindigkeit wieder hoch gesetzt worden. So kann die Animation weiterlaufen und man hat kein Springen mehr im Bild. Zudem ist das resourcenfreundlicher


----------



## Hans22 (18. Jul 2010)

Ruzmanz hat gesagt.:


> Mal sehen ob ich das richtig interpretiere:
> 
> 1. Bewegung nach Rechts
> 2. Um 1 nach Rachts verschieben.
> ...



Ich würde das jetzt in einer anderen Reihenfolge machen:
Erst bewegen wenn "Kollidiert mit dem Objekt" false ergibt...
Is das jetzt falsch?


----------



## Fival (19. Jul 2010)

Hi nochmal,
danke erstmal an alle die mir geholfen haben.
Mittlerweile läuft es ruckelfrei und ohne springen der Figur. Ich hatte versucht  es, wie Ruzmanz es empfohlen hatte, über eine horizontale und vertikale Geschwindikeit zu machen, habe es aber nicht so gemacht, dass bei einer Kollision die Geschwindigkeit auf null gesetzt wird, sondern das nach wie vor einfach nichts passiert. Irgendwie läuft es jetzt aber so wie ich es mir wünsche. warum? naja so 100% sicher bin ich mir nicht.
Ich habe so einiges am Code geändert. Erstmal sind es jetzt 4 Methode statt vorhher 3(was ja eigentlich schlecht ist, denn ein Methodenaufruf kostet immer Ressourcen.).
Die Methoden werden in dieser Reihenfolge aufgerufen
- Der KeyListener. Dort habe ich nichts verändet. -> wenn ein Tastendruck registriert wurde rufe auf:
- getNewPosition (hieß früher movePlayer). Die Methode verwandelt die Tastendrücke in eine horizontale und vertikale Geschwindigkeit. zB so:

```
if (right && down) {
            //increase the x position
            horizontalSpeed = 3;
            //increase the y position
            verticalSpeed = 3;
        }
```
statt vorher so:

```
if (right && down) {
            //increase the x position
            position[1]++;
            //increase the y position
            position[0]++;
        }
```
Ich denke die neue(erste Version) ist schneller weil die vSpeed und hSpeed im Register abgespeichert werden und nicht jedes mal auf den Speicher zugegriffen werden muss[Array Zugriff], obwohl das ja eigentlich auch im Cache liegen sollte, naja so ganz verstehe ich es nicht, vllt liegts auch einfach nur an der neuen Struktur aller Methoden die aufgerufen werden. ???:L
Diese Methode ruft jedenfalls immer noch checkPosition auf(früher couldMove).
- Erst hier wird jetzt die Position des Charas ausgelesen und mit Hilfe der horizontalen und vertikalen Geschwindigkeit die neue (wo man sich hinbewegen will) Position berechnet und dann überprüft ob die Position gültig ist, wenn dies so ist wird MovePlayer aufgerufen und das Label des Players wird verschoben.

Funktionieren tut es super:toll:


----------



## Landei (19. Jul 2010)

Fival hat gesagt.:


> Ich habe so einiges am Code geändert. Erstmal sind es jetzt 4 Methode statt vorhher 3(was ja eigentlich schlecht ist, denn ein Methodenaufruf kostet immer Ressourcen.)



Wer erzählt denn so einen Schmarrn? In vielen Fällen ist der Mehraufwand gleich null (dann wenn die JVM ein Inlining vornehmen kann), und selbst wenn nicht, ist der Performanceunterschied meistens vernachlässigbar. Wie immer gilt: Erst einmal Nachmessen, bevor man Behauptungen aufstellt. Und wenn das Profiling wirklich auf Methodenaufrufe als Bremse hindeutet, kann oft schon ein simples private oder final vor der Methode Wunder wirken...


----------



## Ruzmanz (19. Jul 2010)

Mit einem Profiler sieht man lediglich, dass die meisten Resourcen beim Zeichnen verschwendet werden. Spätestens bei Ergänzungen merkst du wie deine Systemleistung hoch geht, da du bei einem unsauberen Code mehr aufwand betreiben musst 



> Irgendwie läuft es jetzt aber so wie ich es mir wünsche. warum?



Ich glaube du wolltest uns noch sagen, was nicht funktioniert :bahnhof:


----------

