Zeichnen in Java

Ok, ich glaub OOP mag mich nicht:what:. Wenn ich das Programm in folgender Form ausführe, dann bekomme ich laufzeitfehler sowohl wenn eine eingabe stattfindet als auch wenn das Fenster wegen Größenänderung neu gezeichnet wird. Wenn ich das richtig verstehe kennen die Methoden die speicheradresse von meinen Objekten nicht, oder?
Ich poste hier mal das komplette Programm:

Klasse Kugel:
Code:
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.Random;

public class Kugel {
	
	//Variablen
	private int[] fenster;
	private int[] position;
	private int[] geschwindigkeit;
	private int radius;
	private int aufprall;
		
	//Zufallsgenerator erstellen
	private static Random zufallsgenerator = new Random();
	
	private void KollisionY()
	{
		if(position[1] + geschwindigkeit[1] >= fenster[1] || position[1] + geschwindigkeit[1] <= 0)
		{
			geschwindigkeit[1] *= -1;
		}		
	}	
	private int Geschwindigkeit()
	{
		if(aufprall >= 5)
		{
			aufprall = 0;
			return(2);
		}
		return(1);
	}
	public Kugel(int fensterx, int fenstery, int radius)
	{
		//Fenstergröße festlegen
		fenster = new int[2];
		fenster[0] = fensterx;
		fenster[1] = fenstery;
		
		//Geschwindigkeiten per Zufall festlegen
		geschwindigkeit = new int[2];
		geschwindigkeit[0] = zufallsgenerator.nextInt(5);
		geschwindigkeit[1] = zufallsgenerator.nextInt(5);
		
		//Ball soll in der Mitte des Fensters starten
		position = new int[2];
		position[0] = fenster[0]/2;
		position[1] = fenster[1]/2;
		
		//Ballradius festlegen
		this.radius = radius;
		
		//Geschwindigkeit resetten
		aufprall = 0;
	}
	public void Bewegung()
	{		
		position[0] += geschwindigkeit[0]*Geschwindigkeit();
		position[1] += geschwindigkeit[1]*Geschwindigkeit();
		
		//Ball auf Kollision prüfen 
		KollisionY();				
	}
	public void Slider()
	{
		geschwindigkeit[0] *= -1;
		aufprall++;
	} 
	public boolean KollisionX()
	{
		if(position[0] >= fenster[0] || position[0] <= 0)
		{
			return(true);
		}
		else
		{
			return(false);
		}
	}	
	public int PositionX()
	{
		return(position[0]);
	}	
	public int GeschwindigkeitX()
	{
		return(geschwindigkeit[0]);
	}
	public int Radius()
	{
		return(this.radius);
	}
	public void Zeichnen(Graphics2D graphics)
	{
		graphics.setColor(Color.red);
		graphics.fillArc(position[0], position[1], this.radius, this.radius, 0, 360);
	}
}

Klasse Balken:

Code:
import java.awt.Color;
import java.awt.Graphics2D;


public class Balken {

	private int[] punktoben;
	private int[] punktunten;
	
	public Balken(int linienabstandx, int fenstery)
	{
		//Linienpunkte  initialisieren
		punktoben = new int[2];		
		punktoben[0] = linienabstandx;
		punktoben[1] = (int)(fenstery*0.5 - fenstery/8);
		punktunten = new int[2];
		punktunten[0] = linienabstandx;
		punktunten[1] = (int)(fenstery*0.5 + fenstery/8);
	}
	public void MoveUp()
	{
		if(punktoben[1] > 0)
		{
			punktoben[1] -= 1;
			punktunten[1] -= 1;
		}
	}
	public void MoveDown()
	{
		if(punktunten[1] < 500)
		{
			punktoben[1] += 1;
			punktunten[1] += 1;
		}
	}
	public int ObenX()
	{
		return(punktoben[0]);
	}
	public int ObenY()
	{
		return(punktoben[1]);
	}
	public int UntenX()
	{
		return(punktunten[0]);
	}
	public int UntenY()
	{
		return(punktunten[1]);
	}
	public void Zeichnen(Graphics2D graphics)
	{
		graphics.setColor(Color.blue);	    
	    graphics.drawLine(punktoben[0], punktoben[1], punktunten[0], punktunten[1]);
	}
}

Main Klasse Mpspiel:

Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.*;

public class Mpspiel extends JPanel {
	private static Kugel Ball;
	private static Balken SliderL;
	private static Balken SliderR;
	
	@Override
	protected void paintComponent( Graphics g )  
	{ 
		//Grafikobjekt erstellen und initialisieren
		super.paint(g);
		Graphics2D graphics = (Graphics2D) g;   
		graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		
		//Zu Zeichnende Dinge
		Ball.Zeichnen(graphics);
		SliderR.Zeichnen(graphics);
		SliderL.Zeichnen(graphics);
	}
	private static void Fenster()
	{
		//Fenster erstellen
		JFrame Fenster = new JFrame("Multiplayer Spiel");
		Fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Fenster.setSize(500, 500);
		Fenster.setVisible(true);		
		Fenster.add(new Mpspiel());
		
		//Eingabe behandeln
		Fenster.addKeyListener(new KeyListener() 
		{
			 
			@Override
			public void keyTyped(KeyEvent event) 
			{
				// W(87) und S(83) Spieler 1, Pfeil hoch(38)/runter(40) Spieler 2					
			}			 
			@Override
			public void keyReleased(KeyEvent event)
			{
				//Beenden
				if(event.getKeyCode() == KeyEvent.VK_ESCAPE)
				{
					System.exit(0);
				}
			}			 
			@Override
			public void keyPressed(KeyEvent event) 
			{
				// W und S Spieler 1, Pfeil hoch/runter Spieler 2
				if(event.getKeyCode() == KeyEvent.VK_W)
				{
					SliderL.MoveUp();
				}
				if(event.getKeyCode() == KeyEvent.VK_S)
				{
					SliderL.MoveDown();
				}
				if(event.getKeyCode() == KeyEvent.VK_UP)
				{
					SliderR.MoveUp();
				}
				if(event.getKeyCode() == KeyEvent.VK_DOWN)
				{
					SliderR.MoveDown();
				}
			}
		});
	}	
	public static void main(String[] args) {
		
		//Initialisierung
		Fenster();				
				
		//Objekte erstellen
		Kugel Ball = new Kugel(500, 500, 10);
		Balken SliderL = new Balken(30, 500);
		Balken SliderR = new Balken(470, 500);
		
		//main loop
		while(true)
		{
			//Ball bewegen
			Ball.Bewegung();
			
			//Ball auf Kollision mit Slider prüfen
			//SliderR
			if((Ball.PositionX() + Ball.GeschwindigkeitX() >= 470-Ball.Radius()) && (Ball.PositionX() + Ball.Radius() <= SliderR.UntenY() && Ball.PositionX() + Ball.Radius() >= SliderR.ObenY() ))
			{
				Ball.Slider();
			}
			//SliderL
			else if((Ball.PositionX() + Ball.GeschwindigkeitX() >= 30-Ball.Radius()) && (Ball.PositionX() + Ball.Radius() <= SliderL.UntenY() && Ball.PositionX() + Ball.Radius() >= SliderL.ObenY() ))
			{
				Ball.Slider();
			}
			
			//Ball auf GameOver überprüfen
			if(Ball.KollisionX() == true)
			{
				Ball = new Kugel(500, 500, 10);
			}
			
			//Bild ausgeben
			
						
			//Warten, ca. 33 FPS
			try 
			{
				Thread.currentThread();
				Thread.sleep(30);
			} catch (InterruptedException fehler) 
			{								
			}
		}
	}
}
 
Main Klasse Mpspiel:

Code:
@Override
protected void paintComponent( Graphics g ) 
{ 
//Grafikobjekt erstellen und initialisieren
[B]super.paint(g);[/B]
Graphics2D graphics = (Graphics2D) g; 
graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 
//Zu Zeichnende Dinge
Ball.Zeichnen(graphics);
SliderR.Zeichnen(graphics);
SliderL.Zeichnen(graphics);
}

paint() solltest du eigentlich nie direkt aufrufen, so steht es zumindest in der Java Dokumentation.
Das Problem ist, dass diese Methode paintComponent() aufruft, welche du überschreibst,
welche dann wieder paint() aufruft usw. , es kommt zum Stackoverflow.
Die Zeile kannst du einfach auslassen.





Code:
public static void main(String[] args) {
 
//Initialisierung
Fenster(); 
 
//Objekte erstellen
[B]Kugel[/B] Ball = new Kugel(500, 500, 10);
[B]Balken[/B] SliderL = new Balken(30, 500);
[B]Balken[/B] SliderR = new Balken(470, 500);
 
/* ..... */
}

Das ist ein weiteres Problem, hier werden neue Objekte erzeugt, was nicht so gut ist, stattdessen solltest du die "Attributs-Objekte" verwenden.
Die in der main() eigens definierten und erzeugten Objekte kann man in der Methode Fenster() nicht nutzen bzw. auf sie zugreifen.
Um eine NullPointerException zu vermeiden müssen die "Attributs-Objekte" auch erst definiert werden, bevor sie in der Methode Fenster aufgerufen werden können.


Kleiner Tipp, das Fenster sollte am besten auch noch ein Attribut der Klasse sein, da du auch in der while() - Schleife darauf zugreifen solltest (es neu zeichnen)
 
Zuletzt bearbeitet:
So, große Freude - mein erstes Java Projekt läuft!!!!
Jetzt hab ich noch 2 Schönheitsfragen:
-Wie mache ich es, dass beide Spieler gleichzeitig ihre Slider bewegen können?
-Wie behebe ich das Problem, dass links und unten der Bildschirmrand nicht stimmt?

Ich poste hier die main, im Anhang ist noch die ausführbare .jar Datei im .zip Ordner :daumen:

Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.*;

public class Mpspiel extends JPanel {
	
	//Objekte erstellen
	private static Kugel Ball = new Kugel(500, 500, 10);
	private static Balken SliderL = new Balken(30, 500);
	private static Balken SliderR = new Balken(470, 500);
	private static JFrame Fenster = new JFrame("Multiplayer Spiel");
	
	@Override
	protected void paintComponent( Graphics g )  
	{ 
		//Grafikobjekt erstellen und initialisieren		
		Graphics2D graphics = (Graphics2D) g;   
		graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		
		//Zu Zeichnende Dinge
		Ball.Zeichnen(graphics);
		SliderR.Zeichnen(graphics);
		SliderL.Zeichnen(graphics);
	}
	private static void Fenster()
	{
		//Fenster initialisieren		
		Fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Fenster.setSize(500, 500);
		Fenster.setVisible(true);		
		Fenster.add(new Mpspiel());
		
		//Eingabe behandeln
		Fenster.addKeyListener(new KeyListener() 
		{
			 
			@Override
			public void keyTyped(KeyEvent event) 
			{				
			}			 
			@Override
			public void keyReleased(KeyEvent event)
			{
				//Beenden
				if(event.getKeyCode() == KeyEvent.VK_ESCAPE)
				{
					System.exit(0);
				}
			}			 
			@Override
			public void keyPressed(KeyEvent event) 
			{
				// W und S Spieler 1, Pfeil hoch/runter Spieler 2
				if(event.getKeyCode() == KeyEvent.VK_W)
				{
					SliderL.MoveUp();
				}
				if(event.getKeyCode() == KeyEvent.VK_S)
				{
					SliderL.MoveDown();
				}
				if(event.getKeyCode() == KeyEvent.VK_UP)
				{
					SliderR.MoveUp();
				}
				if(event.getKeyCode() == KeyEvent.VK_DOWN)
				{
					SliderR.MoveDown();
				}
			}
		});
	}	
	public static void main(String[] args) {
		
		//Initialisierung
		Fenster();				
				
		//main loop
		while(true)
		{
			//Ball bewegen
			Ball.Bewegung();
			
			//Ball auf Kollision mit Slider prüfen
			//SliderR
			if(Ball.PositionX() + Ball.GeschwindigkeitX() >= 470-Ball.Radius() && Ball.PositionY() + Ball.Radius() <= SliderR.UntenY() && Ball.PositionY() + Ball.Radius() >= SliderR.ObenY())
			{
				Ball.Slider();
			}
			//SliderL
			else if(Ball.PositionX() + Ball.GeschwindigkeitX() <= 30+Ball.Radius() && Ball.PositionY() + Ball.Radius() <= SliderL.UntenY() && Ball.PositionY() + Ball.Radius() >= SliderL.ObenY())
			{
				Ball.Slider();
			}
			
			//Ball auf GameOver überprüfen
			if(Ball.GameOver(30, 470) == true)
			{
				Ball = new Kugel(500, 500, 10);
			}
			
			//Bild ausgeben
			Fenster.repaint();
						
			//Warten, ca. 30 FPS
			try 
			{
				Thread.currentThread();
				Thread.sleep(34);
			} catch (InterruptedException fehler) 
			{								
			}
		}
	}
}
 

Anhänge

-Wie mache ich es, dass beide Spieler gleichzeitig ihre Slider bewegen können?
Das beste ware da meiner Meinung nach das Event zu cachen, du machst bspw. vier Variablen für die vier Tasten (W,S, UP, DOWN),
statt den Slider gleich zu bewegen kann man hier dann einfach die Variable auf true oder false setzen, jenachdem ob KeyReleased oder KeyPressed
für die jeweilige Taste aufgerufen wurde. In der Hauptschleife kannst du diese Variablen dann überprüfen und dementsprechend die Slider bewegen.


-Wie behebe ich das Problem, dass links und unten der Bildschirmrand nicht stimmt?
Dafür das "Spielfeld" nach unten um ein paar Pixel begrenzen, da der Fensterrad noch zu den 500px gehört.

Gruß b14ckj4ck
 
also wenn das wie bei C läuft, dann is das mit den Keys ned ganz so einfach *glaub* spieler A drückt W und die variable für sliderA.up wird true (oder wie auch immer). nu drückt, während A W noch gedrückt hält B aber DOWN zum bsp und dann wir ja W ned mehr registriert oder?

ahso, jetz versteh ich dich erst mit keyPresses/released. Auch wenn B DOWN drückt ist das event "W.KeyReleased" noch nich eingetreten und sliderA.up bleibt weiterhin true. aha aha ^^ klingt plausibel :)
 
also wenn das wie bei C läuft, dann is das mit den Keys ned ganz so einfach *glaub* spieler A drückt W und die variable für sliderA.up wird true (oder wie auch immer). nu drückt, während A W noch gedrückt hält B aber DOWN zum bsp und dann wir ja W ned mehr registriert oder?
Genauso ist es auch bei Java mit den KeyPressed()/KeyReleased() Methoden, weshalb man so die Taste "tippen" müsste um das Event immer neu auszulösen, oder man speichert selbst ob die Taste gedrückt/losgeslassen wird :)


bspw so:
Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
 
 
public class Mpspiel extends JPanel {
 /* .... */
 private static boolean vkDOWNPressed = false;
 private static boolean vkUPPressed = false;
 private static boolean vkWPressed = false;
 private static boolean vkSPressed = false;
 
 /* .... */
 
 private static void Fenster()
 {
   /* .... */
 
  //Eingabe behandeln
  Fenster.addKeyListener(new KeyListener() 
  {
 
   @Override
   public void keyTyped(KeyEvent event) 
   {
   }    
   @Override
   public void keyReleased(KeyEvent event)
   {
    //Beenden
    if(event.getKeyCode() == KeyEvent.VK_ESCAPE)
    {
     System.exit(0);
    }
    // W und S Spieler 1, Pfeil hoch/runter Spieler 2
    if(event.getKeyCode() == KeyEvent.VK_W)
    {
     vkWPressed = false;
    }
    if(event.getKeyCode() == KeyEvent.VK_S)
    {
     vkSPressed = false;
    }
    if(event.getKeyCode() == KeyEvent.VK_UP)
    {
     vkUPPressed = false;
    }
    if(event.getKeyCode() == KeyEvent.VK_DOWN)
    {
     vkDOWNPressed = false;     
    }
   }    
   @Override
   public void keyPressed(KeyEvent event) 
   {
    // W und S Spieler 1, Pfeil hoch/runter Spieler 2
    if(event.getKeyCode() == KeyEvent.VK_W)
    {
     vkWPressed = true;
    }
    if(event.getKeyCode() == KeyEvent.VK_S)
    {
     vkSPressed = true;
    }
    if(event.getKeyCode() == KeyEvent.VK_UP)
    {
     vkUPPressed = true;
    }
    if(event.getKeyCode() == KeyEvent.VK_DOWN)
    {
     vkDOWNPressed = true;    
    }
   }
  });
 } 
 
 
 public static void main(String[] args) {
 /* .... */
  //main loop
  while(true)
  {
   if(vkWPressed) SliderL.MoveUp();
   if(vkSPressed) SliderL.MoveDown();
   if(vkUPPressed) SliderR.MoveUp();
   if(vkDOWNPressed) SliderR.MoveDown();
 
 
   /* .... */  
 }
}
 
So, jetzt funktioniert alles. Danke an alle, besonders am b14ckj4ck fürs Helfen.
Ich kann mich aber mit OOP immer noch nicht so richtig anfreunden, da muss ich wahrscheinlich noch ein paar Programme produzieren :). Aber 4 Tage find ich für den Anfang gar nicht mal so schlecht, auch wenn ich das jetzt wahrscheinlich in einer Stunde hinbekommen würde;).
 
Edit: Ich hab mal den code aktualisiert, das zeichnen geht soweit, nur habe ich das Problem, dass oben und links jeeils Balken sind, an denen das Fenster aufhört. Kennt ihr da eine Lösung?

Code:
import java.awt.*;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.*;

public class BallSimulation extends JPanel{
	
	//Variablen
	private static int blänge;
	private static int bbreite;	
	private static int ballradius;
	private static boolean escape;
	private static boolean safe;
		
	//Objekte
	private static GraphicsEnvironment Grafikumgebung;
	private static GraphicsDevice Grafikgerät;
	private static JFrame Fenster;
	private static Dimension Auflösung;
	private static Ballverwaltung Container = new Ballverwaltung(5000);
		
	@Override
	protected void paintComponent( Graphics g )  
	{ 
		//Grafikkontext erstellen und initialisieren		
		Graphics2D grafik = (Graphics2D) g;   
		grafik.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		
		//Zu zeichnende Dinge		
		Container.DrawBalls(grafik);	
		
		//Test
		grafik.fillOval(50, 50, 50, 50);
	}
	private static void Initialisierung()
	{
		//Grafikhardware initialisieren
		Grafikumgebung = GraphicsEnvironment.getLocalGraphicsEnvironment();
		Grafikgerät = Grafikumgebung.getDefaultScreenDevice();
		
		//Auflösung auslesen			
		Auflösung = new Dimension();
		Auflösung = Toolkit.getDefaultToolkit().getScreenSize();
		
		//Variablen setzen
		blänge = Auflösung.width;
		bbreite = Auflösung.height;		
		ballradius = 50;
		escape = false;
		safe = true;
				
		//Fenster erstellen und initialisieren
		Fenster = new JFrame("Ball Simulation");	
		Fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);						
		Fenster.setSize(blänge, bbreite);
		Fenster.add(new BallSimulation());
		Fenster.setUndecorated(true);
		Fenster.setBackground(Color.black);
		
				
		//Wenn unterstützt wird der Vollbildmodus aktiviert, sonst kommt ein Fenster
		if(Grafikgerät.isFullScreenSupported())
		{
			Fenster.setVisible(false);
			Grafikgerät.setFullScreenWindow(Fenster);			
		}
		else
		{
			Fenster.setVisible(true);
		}
		
		//Eingabe behandeln
	    //Tastatur
	    Fenster.addKeyListener(
	    new KeyListener()
	    {
			@Override
			public void keyPressed(KeyEvent event) 
			{		
				safe = false;		
				if(event.getKeyCode() == KeyEvent.VK_H)
				{
					if(ballradius > 1){ballradius++;};
				}
				else if(event.getKeyCode() == KeyEvent.VK_N)
				{
					if(ballradius > 1){ballradius--;};
				}
				else if(event.getKeyCode() == KeyEvent.VK_B)
				{						
					Container.AddBall(blänge, bbreite);
				}
				else if(event.getKeyCode() == KeyEvent.VK_L)
				{
					Container.DeleteBall();
				}				
			}

			@Override
			public void keyReleased(KeyEvent event)
			{					
				if(event.getKeyCode() == KeyEvent.VK_ESCAPE)
				{
					escape = true;								
				}
				safe = true;
			}

			@Override
			public void keyTyped(KeyEvent event) 
			{				
			}			    	
	    });
	    
	    //Maustasten
	    Fenster.addMouseListener(
	    new MouseListener()
	    {

			@Override
			public void mouseClicked(MouseEvent event)
			{				
				
			}

			@Override
			public void mouseEntered(MouseEvent event)
			{				
				
			}

			@Override
			public void mouseExited(MouseEvent event) 
			{
								
			}

			@Override
			public void mousePressed(MouseEvent event)
			{
								
			}

			@Override
			public void mouseReleased(MouseEvent event)
			{
								
			}
	    	
	    });
	    
	    //Mausbewegung
	    Fenster.addMouseMotionListener(
	    new MouseMotionListener()
	    {

			@Override
			public void mouseDragged(MouseEvent event) 
			{				
				
			}

			@Override
			public void mouseMoved(MouseEvent event)
			{
								
			}
	    	
	    });	    
	}
	private static void Ende()
	{
		Grafikgerät.setFullScreenWindow(null);
		System.exit(0);
	}
	public static void main(String[] args) {
		
		//Fenster und Eingabe handhaben
		Initialisierung();
				
		//main-Loop
		while(escape == false)
		{
			//Warten bis alle eingaben getätigt sind, da sonst nicht Thread-Safe
			if(safe == true)
			{
				//Bälle bewegen
				Container.Bewegung();
			
				//Kollision
				Container.KollisionBall();
				Container.KollisionRand(blänge, bbreite);
			
				//Bild ausgeben
				Fenster.repaint();
			}
		}
		
		//Beenden
		Ende();				
	}
}
 
Zuletzt bearbeitet:
Zurück