# Spieler springen lassen



## Javaman91 (1. Mai 2016)

Hallo,

ich versuche gerade bei meinem Spiel die Spielfigur springen zu lassen.
Sie soll, wenn die Taste W gedrückt wird einfach hoch springen und wieder runter kommen.
Habe das leider nicht hinbekommen.

Hier ist der aktuelle Sourcecode:


```
public class PongGame extends BasicGame {

   public SpriteSheet spriteSheet;
   public Animation spriteAnimation;   
   public SpriteSheet zigarette;
   public Animation zigaretteAnimation;   
   private public Input in;
   private int xpos =300;
   private int ypos =300;
   private int animieren;
     
    public PongGame() {

  super("Pong");
  }

  public void init(GameContainer gc) throws SlickException {
       
      spriteSheet = new SpriteSheet("spieler.png", 40, 40);
      spriteAnimation = new Animation(spriteSheet, 200);
       
      zigarette = new SpriteSheet("Rauchen.png", 40,40);
      zigaretteAnimation = new Animation(zigarette, 1);
       
      in = gc.getInput();
  }

  public void update(GameContainer gc, int delta) throws SlickException {
   
      if(in.isKeyDown(in.KEY_W)){
        ypos = ypos-1;
        animieren=2;
      }
     
      else if(in.isKeyDown(in.KEY_S)){
        ypos = ypos+1;
        animieren=1;
      }
       
      else if(in.isKeyDown(in.KEY_D)){
        xpos = xpos+1;
        animieren=1;
      }
     
      else if(in.isKeyDown(in.KEY_A)){
        xpos = xpos-1;
        animieren=1;
      }
       
      else{
        animieren=0;  
      }
  }
   
  public void render(GameContainer gc, Graphics g) throws SlickException {

      if(animieren == 2){
       
        spriteAnimation.draw(xpos, ypos);
       }
       
      else if(animieren == 1){
         
        spriteAnimation.draw(xpos, ypos);
         }
       
      else{
         
        zigaretteAnimation.draw(xpos, ypos);
      }
  }

  public static void main(String[] args) throws SlickException {

  AppGameContainer pong = new AppGameContainer(new PongGame());

  pong.setDisplayMode(600, 600, false);
  pong.setVSync(true);
  pong.setTargetFrameRate(100);
  pong.setShowFPS(true);
  pong.start();
  }
}
```

Ich habe versucht einfach nachdem die Taste W gedrückt wurde die ypos 40mal zu erhöhen.
Leider ist die Figur nicht langsam nach oben gesprungen und wieder runter, sondern extrem schnell. Danach dachte ich mir, ich brauche nur ein "Thread.sleep(ms)" um das ganze einfach langsam ablaufen zu lassen. Hat auch nicht funktioniert.

Wie kann ich das machen?


----------



## dayaftereh (2. Mai 2016)

Hey, ich würde das im normalen Fall mit einer Art Gravitation machen. Das bedeutet am Spieler wirkt kontinuierlich eine Kraft in der Positiven Y Achsen Richtung. Wenn dein Spieler springt dann mit einer Karft in der Negativen Y Achsen Richtung. Beide Kräfte Wirken dann gegeneinander über die Zeit.

Du könntest für das einfache umsetzen sagen, wenn dein Spieler springt, dann bewegt er sich mit 2 Pixeln pro Update in Richtung Negativen Y Achse und zusätzlich ziehst du 0.025 Pixel von den 2 Pixeln pro Update ab.

Das würde bewirken das der Spieler zuerst Steigt und dann fällt. Natürlich muss du einen Boden Einbau, sonst fällt der Spieler aus der Welt unb beim erneuten Springen denn werkt auf 2 zurücksetzen.

Hoffe das Hilft, denke aber es gibt genug Tutorials die ein Spiel wie Mario beschreiben


----------



## Javaman91 (2. Mai 2016)

Mein problm ist einfach, das ich nicht weiß wie ich die Spielfigur Pixel für Pixel nach oben wandern lassen kann. Bei mir ist die Figur immer ganz kurz ganz oben und danach gleich wieder am Boden.

Ich möchte ja, wenn die "W" Taste einmal gedrückt wird, das der Spieler springt.
Nun habe ich mir gedacht ich lasse in der update-Methode eine For-Schleife laufen die die Ypos immer um 1 erhöht. Hat die For-Schleife ihr maximum erreicht, so wird eine zweite For-Schleife aufgerufen die dann wieder Ypos um 1 verkleinert.
Hier habe ich aber das Problem, das zuerst die beiden For-Schleifen in der update-Methode ausgeführt werden. Erst wenn diese zwei Schleifen fertig sind wird die render-Mathode aufgerufen. Dann entspricht der neue Wert von Ypos dem alten Wert den Ypos hatte bevor "W" gedrückt wurde. Also tut die Spielfigur nichts.

Ich weiß einfach nicht, wie ich Ypos Schritt für Schritt erhöhen kann ohne die Taste "W" mehrmals drücken zu müssen?


----------



## dayaftereh (3. Mai 2016)

Hey, der Ablauf ist ja der, zuerst wird deine Update aufgerufen und dann wieder Render aufgerufen. Zusammen erzeugen sie einen Frame. Das Spiel sagen wir mal hat 30 Frames pro Sekunde.

Damit dein Spieler Springen kann muss du eine Logik Einbau die über mehrere Frames hin Weg funktioniert und nicht alles in einem Frame Update macht.

Normalerweise hat ein Spiel eine Physik, das heißt jedes Objekt besitzt einen Geschwindigkeits Vektor, der bei jedem Frame auf die Position des Objekts addiert wird. Zusätzlich wirkt auf jedes Objekt ein Gravitationsvektor der zusätzlich von der Objekte Position abgezogen wird.

So änlich muss du das in deinem Spiel umsetzten. Du benötigst jetzt keine Vektoren, sondern du kannst deinem Spieler einfach eine Variable geben. Zum Beispiel VerktialSpeed die in jedem Update um VerkialGravity verringert wird und dann auf die Y Position deines Spielers addiert wird.


```
Player player = ....
int vertikalGravity = 1;

public void update(double deltaTime){
  // deltaTime ist die vergangene Zeit zwischen dem Frame und dem Letzten in Sekunden
  // Update Spieler Position
  player.vertikalSpeed -= vertikalGravity * deltaTime;
  player.y += player.vertikalSpeed * deltaTime;
  // ...
}

public void jump(){
  player.vertikalSpeed = 40;
}
```

Das wäre jetzt mein Entwurf. Sehr wahrscheinlich musst du die Rechnung für Y Position umdrehen, da du direkt mit Pixeln rechnest. Die Werte habe ich mir ausgedacht, musst selbst mit denenen mal spielen, welche die besten sind.

Und du musst dem Spieler bewahren das er nicht aus dem Spielfeld fällt.


----------



## Javaman91 (4. Mai 2016)

Das muss man erst einmal verstehen, was mir leider noch nicht gelungen ist.

Als erstes erzeugst du eine player nach der Player-Klasse.
Danach erstelltst du eine feste int Variable vertikalGravity = 1.

Dan sagtst du: 

```
player.vertikalSpeed -= vertikalGravity * deltaTime;
```

Bei "player.vertikalSpeed" bekommt der "player" den Wert von "vertikalSpeed" übergeben, dieser ist 40. Danach ziehst du "vertikalGravity * deltaTime" von "player.vertikalSpeed" ab mit "-=".

"player.vertikalSpeed" ist vermutlich die Geschwindigkeit, mit der der player nach oben springt.
Das heißt der player wander 40 Pixel hoch.

Hier weiß ich nicht mehr weiter: "vertikalGravity * deltaTime"??
"vertikalGravity" ist doch immer 1, was ergibt das für einen Sinn?
Wenn "deltaTime" z.B. 10 ist und ich es mal 1 rechne ändert sich ja nichts.
Wozu dann "vertikalGravity"?

Und welchen Wert hat "deltaTime"?

MfG


----------



## dayaftereh (4. Mai 2016)

Hey,

meine Idee ist. Der Spieler hat seine X,Y Position und seinen Vertikale Geschwindigkeit. Da Geschwindigkeit Strecke / Zeit ist, würde in deinem Fall folgendes sein Pixel / Sekunde. Das heißt die VertikalGravity hat 1 Pixeln pro Sekunde. Wenn der Spieler springt dann hat er am Anfang 40 Pixel pro Sekunde. Da aber pro Sekunde die Gravity wirk. Hat er nach 3 Sekunde nur noch 37 Pixel pro Sekunde nach oben.

Natürlich sind die Werte hier doof gewählt. Ihrgend wann bewegt der Spieler sich nicht mehr nach oben bzw die vertikale Geschwindigkeit des Spielers ist -1 Pixel pro Sekunde und so weiter. Damit fällt der Spieler wieder zu Boden.

Sagen wir mal deine Methode Update wird alle 33,3 ms aufgerufen, aber deine Geschwindigkeit ist pro Sekunde also müssen wir umrechnen.


```
Player player = ....
// eine Geschwindigkeit die gemessen ist mit 1 Pixel pro Sekunde
float vertikalGravity = 1;

public void update(double deltaTime){
  // deltaTime ist die vergangene Zeit zwischen Letzten und diesem Update in Sekunden, deltaTime = 0,033 Sekunde

  // berechnet die aktuelle Gravity für diesen Update. Da von dem letzten bis zu diesem Update nicht eine Sekunde vergang ist
  float currentGravity = vertikalGravity * deltaTime;
  // nun ziehen wir currentGravity von unserem vertikale Geschwindigkeit ab, da immer eine Gravitation an unserem Spieler wirkt
  player.vertikalSpeed -= currentGravity;

  // jetzt rechnen wir aus wie weit der Spieler zwischen dem Letzten und diesem Update gekommen ist. Dazu müssen wir unsere vertikale Geschwindigkeit nach der Zeit ableiten. Also (Pixel / Sekunde) * Sekunde, das heißt deltaY ist die Strecke die der Spieler sich für diesen Update auf der Y Achse bewegt
  float deltaY = player.vertikalSpeed * deltaTime;
  // nun addieren wir die Strecke auf unsere Position, bzw verschieben den Spieler
  player.Y += deltaY;
}

public void jump(){
  // setzt die vertikale Geschwindigkeit zurück auf 40 Pixel pro Sekunde
  player.vertikalSpeed = 40;
}
```

Hoffe das Hilft...


----------



## Hellosager (7. Mai 2016)

Vielleicht hilft dir das weiter:


----------



## Javaman91 (7. Mai 2016)

Habe meinen Sprung nun wie auch im Video erklärt ganz einfach gemacht.
Position hochzählen danach wieder runter.


----------



## Javaman91 (7. Mai 2016)

Habe nun eine anderes Problem, ich habe einen Leveleditor gemacht.
Dieser befindet sich in einer eigenen Klasse.
Nun möchte ich diese Klasse in der init-Methode von BasicGame (Slick2D) aufrufen.
Wie kann ich das machen?

Hier die Klasse die ich gerne aufrufen möchte:


```
package map2;

import java.awt.Color;
import java.awt.Graphics;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Editor {
   
   private FileReader filereader;
   private BufferedReader bufferreader;
   private char array[][] = new char[20][20];
   
   public Editor(){
     
   }
   
   public void map(Graphics g) throws IOException{
     
     String temp;
     
     filereader = new FileReader("Level.txt");
     bufferreader = new BufferedReader(filereader);
     
     for(int i=0; i<20; i++){
     
     temp = bufferreader.readLine();
     char tempArray[] = temp.toCharArray();
     
       for(int j =0; j<20; j++){
         
         array[i][j] = tempArray[j];
       }
     }
     
     for(int i=0; i<20; i++){
       for(int j=0; j<20; j++){
         
         if(array[i][j] == 'h'){ //Himmel
             g.setColor(Color.pink);
             g.drawRect(25*j, 25*i, 25, 25);
             }
         else if (array[i][j] == 'g'){ //Gras
             g.setColor(Color.green);   
             g.drawRect(25*j, 25*i, 25, 25);
             }
         else if (array[i][j] == 'w'){ //Wasser
             g.setColor(Color.blue);
             g.drawRect(25*j, 25*i, 25, 25);
             }
         else if (array[i][j] == 'e'){ //Erde
             g.setColor(Color.DARK_GRAY);
             g.drawRect(25*j, 25*i, 25, 25);
             }   
           }
         }
       }
     }
```

Wenn ich nun in der init-Methode ein Objekt der Klasse: "Editor" erzeuge und versuche mit "editor.map(null);" die Methode aufzurufen, dann bekomme ich einen Fehler.

Wie kann ich das machen?


----------



## dayaftereh (8. Mai 2016)

Na ja, wenn du die Methode Editor.map mit null als Graphics aufrufen und dann in der Methode auf dein Graphics Objekt zugreifst wird natürlich eine Nullpointer geworfen...


----------



## Javaman91 (8. Mai 2016)

Wie kann ich das machen?

Er verlangt als Übergabeparameter etwas.


----------



## dayaftereh (8. Mai 2016)

Ok, ich kenne deine Programm Struktur nicht. Aber du könntest eine BufferedImage erzeugen. Vom BufferedImage  könntest du das Graphics Objekt holen und dann die Map in das Bild zeichnen. Das Bild kannst du dann wiederum anzeigen.


----------



## Javaman91 (8. Mai 2016)

Wieso kann ich nicht einfach wie auch bei anderen Methoden mit: "editor.map();" die paint-Methode aufrufen?

Wieso soll ich einen Parameter übergeben.
Wenn die paint-Methode z.B. eine Position wie ypos benötigen würde, damit sie das Objekt jedes mal wo anders hin zeichnet, dann würde ich das Verstehen. Nur ich will Sie nur ausführen und sonst nichts?  

Wie bereits erwähnt, "Graphics" ist ja die Klasse, "g" das Objekt der Klasse.
Der Compiler weiß doch, das er sich das Grafik-Objekt "g" nach der Klasse "Graphics" die ich importiert habe bauen muss.


----------



## Hellosager (8. Mai 2016)

Du musst das ganze aber mit editor.map(beliebigesGraphicsObjekt) aufrufen um keine NullPointerException zu erhalten. In dem Fall musst du das Graphicsobjekt von dem übergeben worauf du zeichen willst.


----------



## Javaman91 (9. Mai 2016)

Wie gesagt, ich arbeite mit Slick2D und möchte dies in der init-Methode aufrufen.

Habe keinen Ahnung wie, Sorry.


----------



## Javaman91 (9. Mai 2016)

Hat jemand ein kleines Beispiel für mich?

MfG


----------



## dayaftereh (9. Mai 2016)

Gerade ist mir ein Licht auf gegangen. Du willst mit der Editor Klasse eine Karte laden und diese dann Zeichen.

Bzw. Das versucht du in der Editor#map() Methode. Vielleicht solltest du es um bauen. Zu erst die Map Methode in load umbenennen und dann alles was Graphics Objekt angeht in eine neue Render Methode auslagern. Render kannst du dann in der Render Methode von Slick2D aufrufen und load in der init.

Dann würde ich die Editor Klasse umbenennen in MapLoader, wobei die load Methode eine eigene GameMap Klasse zurück gibt. Auf der GameMap Klasse kannst du dann render(Graphics) aufrufen.


----------



## Javaman91 (9. Mai 2016)

O.K.

Also ich soll die aktuelle map-Methode in load umbennen.
Was ist dann anders?
Ob die Methode map oder load heißt ist doch eigentlich egal, oder?
Dann erstelle ich in selben Klasse eine neue Methode mit der bezeichnung render, dort kommt alles rein was mit Graphics zu tun hat.

Das vestehe ich nicht ganz:

"wobei die load Methode eine eigene GameMap Klasse zurück gibt. Auf der GameMap Klasse kannst du dann render(Graphics) aufrufen."

MfG


----------



## dayaftereh (9. Mai 2016)

Ok, mein Fehler. Habe zu viele Schritt auf einmal versucht zu beschreiben. Hier meine Editor Klasse


```
public class Editor {
   private FileReader filereader;
   private BufferedReader bufferreader;
   private char array[][] = new char[20][20];

   public Editor(){
  
   }

   public void load(String file) throws IOException{
  
     String temp;
  
     filereader = new FileReader(file);
     bufferreader = new BufferedReader(filereader);
  
     for(int i=0; i<20; i++){
  
     temp = bufferreader.readLine();
     char tempArray[] = temp.toCharArray();
  
       for(int j =0; j<20; j++){
      
         array[i][j] = tempArray[j];
       }
     }
   }
  
   public void render(Graphics g) {
     for(int i=0; i<20; i++){
       for(int j=0; j<20; j++){
      
         if(array[i][j] == 'h'){ //Himmel
             g.setColor(Color.pink);
             g.drawRect(25*j, 25*i, 25, 25);
             }
         else if (array[i][j] == 'g'){ //Gras
             g.setColor(Color.green);
             g.drawRect(25*j, 25*i, 25, 25);
             }
         else if (array[i][j] == 'w'){ //Wasser
             g.setColor(Color.blue);
             g.drawRect(25*j, 25*i, 25, 25);
             }
         else if (array[i][j] == 'e'){ //Erde
             g.setColor(Color.DARK_GRAY);
             g.drawRect(25*j, 25*i, 25, 25);
             }
           }
         }
}
```

Aus dem Game und Slick2D zugreifen:

```
public class Game extends BasicGame {

  private Editor editor = null;

  //...
  @Override
  public void init(GameContainer container) throws SlickException {
    // ...
    this.editor = new Editor ();
    this.editor. load("Level.txt");
  }

  // ...
  @Override
  public void render(GameContainer container, Graphics g) throws SlickException {
    //...
    this.editor.render(g);
  }
}
```


----------



## Javaman91 (9. Mai 2016)

Nein, deine Erklärung war super!

Ich habe nur Deinen letzten Satz nicht ganz Verstanden.

So wie Du mir jetzt die Editor-Klasse geschrieben hast, so hatte ich meine Editor-Klasse bevor ich hier meinen Code gezeigt habe.

Bei mir war dieser Teil vom Sourcecode auch in einer eigenen Methode:

```
for(int i=0; i<20; i++){
       for(int j=0; j<20; j++){
        
         if(array[i][j] == 'h'){ //Himmel
             g.setColor(Color.pink);
             g.drawRect(25*j, 25*i, 25, 25);
             }
         else if (array[i][j] == 'g'){ //Gras
             g.setColor(Color.green);  
             g.drawRect(25*j, 25*i, 25, 25);
             }
         else if (array[i][j] == 'w'){ //Wasser
             g.setColor(Color.blue);
             g.drawRect(25*j, 25*i, 25, 25);
             }
         else if (array[i][j] == 'e'){ //Erde
             g.setColor(Color.DARK_GRAY);
             g.drawRect(25*j, 25*i, 25, 25);
             }  
           }
         }
       }
     }
```

Danach dachte ich mir, ich kann doch alles in eine Methode machen.
Der gedanke war der, das ich nur eine Methode aufrufen muss, wo alles passiert.

In der init hatte ich ebenfalls "editor = new Editor();" nur halt ohne "this".
Ich hatte dann aber in der init-Methode noch: editor.map(g); die hast du in der render.
Ich habe das in die init getan da ich nicht wollte, das die map ständig neu gezeichnet wird, sondern nur einmal beim start.

Da es aber mit "editor.map(g);" nicht funktioniert hat, habe ich einfach mal "null" geschrieben.
"null" bedeutet ja, das nicht übergeben wird, ich wollte ja auch nichts übergeben sondern nur aufrufen.

MfG


----------



## Javaman91 (9. Mai 2016)

Ich habe es so versucht wie du es gemacht hast, doch leider habe ich den gleichen Fehler.

Für "this.editor.load("Level.txt");" verlangt er ein try & catch.
Das brauch er ja, falls er die Datei nicht findet.

Bei "this.editor.render(Graphics g);" unterstreicht er "Graphics g" rot.

Das kommt in der Konsole raus:

"
Mon May 09 22:48:56 CEST 2016 INFO:Slick Build #237
Mon May 09 22:48:57 CEST 2016 INFO:LWJGL Version: 2.9.2
Mon May 09 22:48:57 CEST 2016 INFO:OriginalDisplayMode: 1366 x 768 x 32 @60Hz
Mon May 09 22:48:57 CEST 2016 INFO:TargetDisplayMode: 500 x 500 x 0 @0Hz
Mon May 09 22:48:57 CEST 2016 INFO:Starting display 500x500
Mon May 09 22:48:57 CEST 2016 INFO:Use Java PNG Loader = true
WARNING: Found unknown Windows version: Windows 7
Attempting to use default windows plug-in.
Loading: net.java.games.input.DirectAndRawInputEnvironmentPlugin
Mon May 09 22:48:58 CEST 2016 INFO:Found 0 controllers
Mon May 09 22:48:58 CEST 2016 ERROR:Unresolved compilation problems:
   Graphics cannot be resolved to a variable
   Syntax error on token "g", delete this token

java.lang.Error: Unresolved compilation problems:
   Graphics cannot be resolved to a variable
   Syntax error on token "g", delete this token

   at map2.Hauptfenster.render(Hauptfenster.java:42)
   at org.newdawn.slick.GameContainer.updateAndRender(GameContainer.java:688)
   at org.newdawn.slick.AppGameContainer.gameLoop(AppGameContainer.java:411)
   at org.newdawn.slick.AppGameContainer.start(AppGameContainer.java:321)
   at map2.Hauptfenster.main(Hauptfenster.java:52)
Mon May 09 22:48:58 CEST 2016 ERROR:Game.render() failure - check the game code.
org.newdawn.slick.SlickException: Game.render() failure - check the game code.
   at org.newdawn.slick.GameContainer.updateAndRender(GameContainer.java:691)
   at org.newdawn.slick.AppGameContainer.gameLoop(AppGameContainer.java:411)
   at org.newdawn.slick.AppGameContainer.start(AppGameContainer.java:321)
   at map2.Hauptfenster.main(Hauptfenster.java:52)
"


----------



## dayaftereh (10. Mai 2016)

Ich glaube das Problem ist das in der Editor Klasse deinen Graphics Object von java.awt.Graphics ist und nicht das von Slick2D.

Slick2D hat ein eigenes 
org.newdawn.slick.Graphics.


----------



## Javaman91 (10. Mai 2016)

Ja, so funktionierts.

Das heißt wenn ich "editor.render(g);" schreibe, dann übergebe ich ein Graphics-Objekt an die render-Methode. Da ja in der render-Methode:

```
public void render(Graphics g){
}
```
in den runden Klammern (Graphics g) ein grafik Objekt erwartet wird?


----------



## dayaftereh (10. Mai 2016)

Genau, da du ja deine GameMap auf dem Bildschirm zeichnen willst. 

Hinter dem Graphics Objekt steckt ein komplexes Konstrukt, was dir die Möglichkeit bietet Line, Kreis, Rechteck und mehr zu zeichnen.

Normalerweise wird bei einem Spiel pro Frame der Komplette Bildschirm leer gemacht und dann alles neu rein gezeichnet.

Hast du mal das Spiele Tutorial von @Quaxli durchgearbeitet ? Ich glaube das würde dir sehr Helfen, auch wenn es zum Start hin schwer verständlich ist.


----------



## Eichelhäer (2. Jun 2016)

Hallo,

für ein Jump and Run Game empfiehlt es sich eigentlich für das ganze Game eine Schwerkraft einzuführen, d.h. dass alle beweglichen Spielobjekte (Spieler,Gegner, evtl. Items,etc.) prinzipiell die Tendenz zum fallen haben, ganz so wie auf unserem wundervollen Planet Erde.


----------

