Objekt Koordinaten Probleme (Quaxli Clouds)

Hey Leute!

Ich schreibe nun schon seit knapp zwei Jahren mit Java „Plugins“ für das Spiel Minecraft und habe mich jetzt heute zum ersten Mal an einem eigenem Game versucht.

Die Basics kenne ich zwar aber mit Klassen wie JFrame oder JPanel habe ich noch nie vorher gearbeitet.

Nach einer kurzen Suche habe ich Quaxlis Tutorial gefunden. Einfach der Hammer!! Es ist ausführlich, detailiert und perfekt für Beginner. Vielen Dank dafür Quaxli :kiss: (no homo)

Ich habe mich bis zu Seite 40 vorgearbeitet, bin dort allerdings auf ein Problem gestoßen…
Ich habe schon alles Mögliche probiert aber das will einfach nicht funktionieren…

Meine Klassen sehen im Moment so aus:

GamePanel
[spoiler]

package org.black_ixx.firstgame;
 
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.Random;
import java.util.Vector;
 
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
 
public class GamePanel extends JPanel implements Runnable, KeyListener{
 
    private static final long serialVersionUID = 5989649846550855813L;
    boolean game_running = true;
    boolean started=false;
    boolean once = false;
 
    long delta = 0;
    long last = 0;
    long fps = 0;
 
    boolean up = false;
    boolean down = false;
    boolean left = false;
    boolean right = false;
    int speed = 50;
 
    Heli copter;
    Vector<Sprite> actors;
 
 
    public static void main(String[] args){
        new GamePanel(800, 600);
    }
 
 
    public GamePanel(int w, int h){
        this.setPreferredSize(new Dimension(w, h));
        this.setBackground(Color.cyan);
        JFrame frame = new JFrame("GameDemo");
        frame.setLocation(100, 100);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addKeyListener(this);
        frame.add((this));
        frame.pack();
        frame.setVisible(true);
        doInitializations();
    }
 
    private void doInitializations(){
 
        last = 0;
 
        actors = new Vector<Sprite>();
        BufferedImage[] heli = this.loadPics("org/black_ixx/firstgame/pics/heli.gif", 4);
        copter = new Heli(heli, 400, 300, 100, this);
        actors.add(copter);
 
        createClouds();
 
 
        if (!once){
            once=true;
            Thread t = new Thread(this);
            t.start();
        }
 
    }
 
    @Override
    public void run() {
 
        while (game_running){
 
            if (isStarted()){
                computeDelta();
                checkKeys();
                doLogic();
                moveObjects();
            }
 
            repaint();
 
            try{
                Thread.sleep(10);
            }catch (InterruptedException e){
 
 
            }
        }
    }
 
 
    private void computeDelta(){
 
        delta = System.nanoTime()- last;
        last = System.nanoTime();
        fps = ((long) 1e9/delta);
 
    }
 
 
 
    @Override
    public void paintComponent(Graphics g){
        super.paintComponents(g);
 
        g.setColor(Color.red);
        g.drawString("FPS: "+Long.toString(fps), 20, 10);
 
        if (!isStarted()){
            return;
        }
 
        if (actors!=null){
            for (Drawable draw : actors){
                draw.drawObjects(g);
            }
        }
 
 
 
    }
 
 
    private BufferedImage[] loadPics(String path, int pics ){
 
        BufferedImage[] anim = new BufferedImage[pics];
        BufferedImage source = null;
 
        URL pic_url = getClass().getClassLoader().getResource(path);
 
        try{
            source = ImageIO.read(pic_url);
        }catch (IOException e){
        }
 
 
        for (int x = 0; x <pics; x++){
            anim[x] = source.getSubimage(x*source.getWidth()/pics, 0,  source.getWidth()/pics, source.getHeight());
        }
 
        return anim;
 
    }
 
 
 
    private void doLogic(){
        for(Moveable mov : actors){
            mov.doLogic(delta);
        }
    }
 
    private void moveObjects(){
        for (Moveable mov : actors){
            mov.move(delta);
        }
    }
 
    @Override
    public void keyPressed(KeyEvent e) {
 
        if (e.getKeyCode() == KeyEvent.VK_DOWN){
            down = true;
        }      
 
        if (e.getKeyCode() == KeyEvent.VK_UP){
            up = true;
        }
 
        if (e.getKeyCode() == KeyEvent.VK_LEFT){
            left = true;
        }
 
        if (e.getKeyCode() == KeyEvent.VK_RIGHT){
            right = true;
        }
 
    }
 
    @Override
    public void keyReleased(KeyEvent e) {
 
 
        if (e.getKeyCode() == KeyEvent.VK_DOWN){
            down = false;
        }      
 
        if (e.getKeyCode() == KeyEvent.VK_UP){
            up = false;
        }
 
        if (e.getKeyCode() == KeyEvent.VK_LEFT){
            left = false;
        }
 
        if (e.getKeyCode() == KeyEvent.VK_RIGHT){
            right = false;
        }
 
        if (!up&!down){
            copter.setVerticalSpeed(0);
        }
        if (!left&!right){
            copter.setHorizontalSpeed(0);
        }
 
 
        if (e.getKeyCode() == KeyEvent.VK_ENTER){
            if (!isStarted()){
                doInitializations();
                setStarted(true);
            }
        }
 
 
        if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
            if (isStarted()){
                setStarted(false);
            }else{
                setStarted(false);
                System.exit(0);
            }
        }
 
    }
 
    @Override
    public void keyTyped(KeyEvent arg0) {
        // TODO Auto-generated method stub
 
    }
 
 
    private void checkKeys(){
 
        if (up){
            copter.setVerticalSpeed(-speed);
        }
        if (down){
            copter.setVerticalSpeed(speed);
        }
        if (left){
            copter.setHorizontalSpeed(-speed);
        }
        if (right){
            copter.setHorizontalSpeed(speed);
        }
 
 
    }
 
 
    public boolean isStarted(){
        return started;
    }
 
    public void setStarted(boolean started){
        this.started=started;
    }
 
    private void createClouds(){
        BufferedImage[] ci = this.loadPics("org/black_ixx/firstgame/pics/todeswolke.png", 1);
        for (int y=10; y < getHeight(); y+= 50)
        {
            int x = (int)(Math.random()*getWidth());
            Cloud cloud = new Cloud(ci, x, y,1000, this);
            actors.add(cloud);
        }
 
    }
 
 
 
 
}[/spoiler]

Sprite
[spoiler]
package org.black_ixx.firstgame;

import java.awt.Graphics;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
 
public abstract class Sprite extends Rectangle2D.Double implements Moveable, Drawable {
 
    long delay;
   
    GamePanel parent;
    long animation = 0;
    BufferedImage[] pics;
    int currentpic = 0;
   
    protected double dx;
    protected double dy;
   
    public Sprite(BufferedImage[] i, double x, double y, long delay, GamePanel p){
       
        pics = i;
        this.x=x;
        this.y=y;
        this.delay=delay;
        this.width=pics[0].getWidth();
        this.height=pics[0].getHeight();
        parent = p;
       
       
    }
   
   
   
    @Override
    public void drawObjects(Graphics g) {
        g.drawImage(pics[currentpic], (int)x, (int)y,null);
    }
 
    @Override
    public void doLogic(long delta) {
       
        animation+=(delta/1000000);
        if (animation > delay){
            animation = 0;
            computeAnimation();        
        }
    }
 
    @Override
    public void move(long delta) {
 
        if (dx!=0){
            x+=dx*(delta/1e9);
        }
       
        if (dy!=0){
            y += dy*(delta/1e9);
        }
    }
   
   
   
    private void computeAnimation(){
       
        currentpic++;
       
        if (currentpic>=pics.length){
            currentpic=0;
        }
    }
   
   
    public void setVerticalSpeed(double d){
        dy=d;
    }
   
    public void setHorizontalSpeed(double d){
        dx=d;
    }
   
    public double getVerticalSpeed(){
        return dy;
    }
   
    public double getHorizontalSpeed(){
        return dx;
    }
   
    public void setX(double i){
        x=i;
    }
   
    public void setY(double i){
        y=i;
    }
   
   
}[/spoiler]

Heli
[spoiler]
package org.black_ixx.firstgame;

import java.awt.image.BufferedImage;
 
public class Heli extends Sprite{
 
    public Heli(BufferedImage[] i, double x, double y, long delay, GamePanel p) {
        super(i, x, y, delay, p);
    }
   
    @Override
    public void doLogic(long delta){
        super.doLogic(delta);
       
        if (getX()<0){
            setHorizontalSpeed(0);
            setX(0);
        }
       
        if (getX()+getWidth() > parent.getWidth()){
            setX((parent.getWidth()-getWidth()));
            setHorizontalSpeed(0);         
        }
       
        if (getY()<0){
            setY(0);
            setVerticalSpeed(0);
        }
       
        if (getY()+getHeight()>parent.getHeight()){
            setY(parent.getHeight()-getHeight());
            setVerticalSpeed(0);
        }
       
       
    }
 
}[/spoiler]

Cloud
[spoiler]
package org.black_ixx.firstgame;

import java.awt.image.BufferedImage;
import java.util.Random;
 
public class Cloud extends Sprite {
 
    final int SPEED = 20;
   
   
    public Cloud(BufferedImage[] i, double x, double y, long delay, GamePanel p) {
        super(i, x, y, delay, p);
       
 
        if (new Random().nextInt(2)==0){
            setHorizontalSpeed(-SPEED);
        }else{
            setHorizontalSpeed(SPEED);
        }  
    }
   
    @Override
    public void doLogic(long delta){
        super.doLogic(delta);
 
        if (getHorizontalSpeed()>0 && getX() > parent.getWidth()){
            setX(-getWidth());
        }
       
        if (getHorizontalSpeed() < 0 && (getX() + getWidth()) <0){
            setX(parent.getWidth()+getWidth());
        }
       
       
    }
   
}

[/spoiler]

Position der Wolken
Nun kommen wir zu dem Problem.
Ich konnte nirgendwo Quaxlis Wolken-Bild finden also habe ich mir einfach meine eigene Wolke zusammengebastelt.

Mit diesem Code werden die Wolken auch korrekt gespawnt:

          BufferedImage[] ci = this.loadPics("org/black_ixx/firstgame/pics/todeswolke.png", 1);
          for (int y=10; y < getHeight(); y+= 50)
          {
              int x = (int)(Math.random()*getWidth());
              Cloud cloud = new Cloud(ci, x, y,1000, this);
              actors.add(cloud);
          }
      }```


innerhalb der nächsten Millisekunden aber denkt das Spiel, dass die Wolken ausserhalb des sichtbaren Bereiches wären und bewegt sie zurück zu dem Rand des Fensters...

Ich habe stundenlang versucht das zu beheben. Ohne Erfolg

Was ich bisher herausgefunden habe (mithilfe von system.out.print(X variable / width of window):


 ``` int x = (int)(Math.random()*getWidth());
      Cloud cloud = new Cloud(ci, x, y,1000, this);```


Hier werden sie bei einer zufälligen Position gespawnt. In der Mitte des Fensters.
Ich habe auch schon

```  int x = Random.nextInt(getWidth())```

versucht aber es kommt beidesmal auf das gleiche heraus. Das Problem liegt in einer anderen Methode.


Ich habe diesen Part hier genauer untersucht:
@Override
    public void doLogic(long delta){
        super.doLogic(delta);
#p1
        if (getHorizontalSpeed()>0 && getX() > parent.getWidth()){
#p2
            setX(-getWidth());
        }
       
        if (getHorizontalSpeed() < 0 && (getX() + getWidth()) <0){
#p3
            setX(parent.getWidth()+getWidth());
        }
       
    }```

Anfangs als die Wolken gespawnt werden liegt X irgendwo zwischen 0 und 800.

Das erste Mal aber wenn doLogic ausgeführt wird ist X irgendeine riesige Zahl.
Diese Zahlen hier wurden geprintet: "684832.5869867401 685315.5869867401 685112.5869867401 684797.5869867401 685194.5869867401 685009.5869867401 684830.5869867401 685286.5869867401 "
X wird sofort wieder zurückgesetzt und von dem Moment an verhalten sich alle Wolken normal und fliegen quer über den Bildschirm.

Ich frage mich, wann und wieso X zu diesen Zahlen gesettet wird. Liegt das auch an einem Fehler in meinem Code? Ich habe den ganzen Code danach durchsucht…

Ich wäre wirklich dankbar über ein paar Antworten!

Hey, hab versucht dein Problem nachzuvollziehen. Also hab ich mir alles rüberkopiert und das mit den Bildern auskommentiert und Rechtecke gezeichnet. Leider kann ich das Problem nicht bei mir reproduzieren. Kannst du das komplette Projekt gezippt hochladen ?

Grüße

Viel Text, viel Code, und trotzdem habe ich die Frage nicht kapiert. Irgendwas mit den Positionen der Wolken…!?

Was passiert, wenn du sowas machst wie

        @Override
        public void doLogic(long delta){
            System.out.println("At 0 position is "+getX()+" "+getY());
            super.doLogic(delta);
            System.out.println("At 1 position is "+getX()+" "+getY());

            if (getHorizontalSpeed()>0 && getX() > parent.getWidth()){
                setX(-getWidth());
            }
           
            if (getHorizontalSpeed() < 0 && (getX() + getWidth()) <0){
                setX(parent.getWidth()+getWidth());
            }
           
            System.out.println("At 2 position is "+getX()+" "+getY());
        }

(damit’s übersichtlich bleibt, am besten nur EINE Wolke verwenden)

Sorry für den unübersichtlichen Text… ich war etwas in Eile.

Kurzfassung:

Ziel:
Wolken die an zufälligen X-Koordinaten gespawnt werden und von dort aus weiterfliegen

Was passiert stattdessen?
Die Wolken werden an der richtigen Stelle gespawnt, dann aber sofort zu dem Rand des Bildschirms verschoben weil Java denkt dass die Wolke ausserhalb des Bildschirms wäre.
Mehr Infos dazu stehen in dem ersten Text.

So sieht das Spiel im Moment aus:

Project-Download-Link

Ich hoffe dass dieser Beitrag etwas hilfreicher ist.
Danke für eure Bemühungen :slight_smile:

Grüße Felix

Hey,

hab’s nun endlich gefunden: der aller erste „delta“-Wert ist viel zu groß. Du setzt last = 0;und beim ersten Schleifendurchlauf werden natürlich alle Wolken an die Ränder geklatscht.

Obwohl ich ja eigentlich nicht in Setter Logic „reinpfuschen“ würde, aber folgendes wäre eine Möglichkeit:


public void setStarted(boolean started){
		if(started){
			computeDelta();
		}
		
		this.started=started;
	}

Hab also mein Versprechen vom JFO erfüllt :stuck_out_tongue_winking_eye:

Grüße

[QUOTE=Sunshine]Hey,

hab’s nun endlich gefunden: der aller erste „delta“-Wert ist viel zu groß. Du setzt last = 0;und beim ersten Schleifendurchlauf werden natürlich alle Wolken an die Ränder geklatscht.

Obwohl ich ja eigentlich nicht in Setter Logic „reinpfuschen“ würde, aber folgendes wäre eine Möglichkeit:


public void setStarted(boolean started){
		if(started){
			computeDelta();
		}
		
		this.started=started;
	}

Hab also mein Versprechen vom JFO erfüllt :stuck_out_tongue_winking_eye:

Grüße[/QUOTE]

Es funktioniert!! Endlich :slight_smile:

Danke Danke Danke Danke :3

#Solved

In der Methode doInitializations() steht im Tutorial
last = System.nanoTime() ;
und nicht
last = 0;

[QUOTE=Quaxli]In der Methode doInitializations() steht im Tutorial
last = System.nanoTime() ;
und nicht
last = 0;[/QUOTE]
Dieses Problem hat Sunshine schon gelöst, aber trotzdem danke für die Hilfe :slight_smile:

Aber mit einer Hand-am-Arm-Lösung…

Mal eine Frage:

Wenn ihr das fertige Projekt von Quaxli kompiliert, ist der Helikopter dann auch so am ruckeln?

[QUOTE=avi]Mal eine Frage:

Wenn ihr das fertige Projekt von Quaxli kompiliert, ist der Helikopter dann auch so am ruckeln?[/QUOTE]

Also bei mir lief alles flüssig…

[QUOTE=avi]Mal eine Frage:

Wenn ihr das fertige Projekt von Quaxli kompiliert, ist der Helikopter dann auch so am ruckeln?[/QUOTE]Nein, alles flüssig… Das fertige Projekt gibt es auch relativ fehlerfrei (relativ, weil man weis ja nie ;)) als Binärdownload auf Quaxlis Seite. Lade das mal als Referenz runter und lass es laufen. Wenn das dann nicht flüssig läuft, hat sich wahrscheinlich das “Relativ” von eben bestätigt.

wo soll denn diese Seite sein?

[QUOTE=mymaksimus]wo soll denn diese Seite sein?[/QUOTE]Ooops… Gute Frage… sie ist nicht mehr da, die Links (bis auf das Tutorial) führen ins Leere. Auf der Seite von Ralf Bauer (Quaxli) konnte man darüber hinaus auch noch den Java-Klon von „Falcon Patrol II“ JFalcon finden… Ist leider auch nicht mehr da… :frowning: Wo isset jeblieben?

Als ich angefangen habe habe ich auch überall nach Quaxli gegooglet aber nichts hilfreiches gefunden (Außer einer PDF mit dem Tutorial).
Später habe ich dann die Tutorial.zip bei Quaxlis Anhang im Forum entdeckt.

Tutorial: www.ralf-bauer.org/java/tutorial/Tutorial.zip
JFalcon: www.ralf-bauer.org/java/JFalcon.jar