Zum Inhalt springen

Java Applet - Pong-Spiel


dappdada

Empfohlene Beiträge

Servus,

ich habe gestern angefangen mich ein wenig mit der Applet-Programmierung in Java zu beschäftigen. Habe dazu ein Tutorial gemacht in dem man ein Spiel mit einem Ball programmieren sollte. Ich hab das Programm etwas ausgebaut und wollte damit ein PingPong-Spiel basteln.

Das Spiel funktioniert an sich sehr gut. Das einzige Problem ist, dass die Funktion bounds() (in der Klasse ball) sehr zufällige Ergebnisse, beim berechnen der neuen Flugrichtung ergibt. Beim einen mal funktioniert es tadellos bei zweiten mal bleibt der Ball in der Wand hängen...

Kann jemand meinen Fehler entdecken???

Vielen Dank schon mal.

Anbei mein Code:

Main Klasse:


import java.applet.*;

import java.awt.*;

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;



@SuppressWarnings("serial")

public class Main  extends Applet implements Runnable, KeyListener, MouseListener, MouseMotionListener{


	// Instanzvariablen 

	private int speed;				// Threadgeschwindigkeit

	public int mausYPosition;


	boolean isStoped = true;		// Zeigt an, ob das Spiel gestopt ist (true) oder läuft (false)


	// Thread

	Thread th;						// Thread in dem das Spiel läuft


	// Audiodateien

	AudioClip shotnoise;	// Speichert die Wav - Datei Gun, die nach Schuss abgespielt wird

	AudioClip hitnoise;		// Speichert die Wav - Datei hit, die nach einem Treffer abgespielt wird

	AudioClip outnoise;		// Speichert die Wav - Datei error, die nach einem Treffer abgespielt wird


	// Neue Schrift

    Font f = new Font ("Serif", Font.BOLD, 20);


    // Variablen für die Doppelpufferung

	private Image dbImage;

	private Graphics dbg;


	// Deklaration der Objektreferenzen

	Player player1;

	Player player2;

	Ball ball;



	// init - Methode wird beim Laden des Applets aufgerufen

	public void init(){


		this.setSize(800, 800);


		// Setzen der Hintergrundfarbe

		setBackground (Color.black);


		// Setzten der Schrift

		setFont (f);


		// Speed wird von Parameter speed des Applets bestimmt

		if (getParameter ("speed") != null){


			speed = Integer.parseInt(getParameter("speed"));

		}

		else speed = 15;


		// Laden der Bilder und Audiodateien und einmaliges Abspielen, um längere Ladezeiten während des Spiels zu vermeiden

		hitnoise = getAudioClip (getCodeBase() , "gun.au");

		hitnoise.play();

		hitnoise.stop();

		shotnoise = getAudioClip (getCodeBase() , "miss.au");

		shotnoise.play();

		shotnoise.stop();

		outnoise = getAudioClip (getCodeBase() , "error.au");

		outnoise.play();

		outnoise.stop();


		player1 = new Player("Player1", 20, this.getHeight()/2, 20, 80, Color.GREEN);

		player2 = new Player("Player2", this.getWidth()-40,this.getHeight()/2,20,80,Color.ORANGE);

		ball = new Ball(10, this.getWidth()/2, this.getHeight()/2, 4, Color.YELLOW, outnoise, player1, player2);


		addKeyListener(this);

		addMouseListener(this);

		addMouseMotionListener(this);


	}


	// Start - Methode, hier beginnt das Applet zu laufen

	public void start (){


		// Schaffen eines neuen Threads, in dem das Spiel läuft

		th = new Thread (this);

		th.start ();

	}


	// Stop - Methode, hier wird das Applet gestopt

	public void stop (){


		th.interrupt();

	}


	// Destroy - Methode wird beim endgültigen Verlassen der Internetseite aufgerufen

	public void destroy() { }


	/** Paint - Methode dient dem Zeichnen von Elementen im Applet und wird z.B. bei Bewegung des Browserfensters und durch den Aufruf repaint() aufgerufen */

	public void paint (Graphics g){


		// Wenn noch Leben übrig sind

		if (player1.getLives() >= 1 && player2.getLives() >= 1){


			//Ränder malen

			g.setColor(Color.gray);

				//oben

				g.fill3DRect(0, 0, this.getWidth(), 20, true);

				//unten

				g.fill3DRect(0, this.getHeight()-20, this.getWidth(), 20, true);

				//links

				g.fill3DRect(0, 0, 20, this.getHeight(), true);

				//rechts

				g.fill3DRect(this.getWidth()-20, 0, 20, this.getHeight(), true);


			//Tore malen

			g.setColor(getBackground());

				//links

				g.fillRect(0, this.getHeight()/2-(this.getHeight()/2/2), 20, this.getHeight()/2);

				//rechts

				g.fillRect(this.getWidth()-20, this.getHeight()/2-(this.getHeight()/2/2), 20, this.getHeight()/2);


			// Punktestand und übrige Leben

			g.setColor (Color.yellow);

				//Spieler 1

				g.drawString (player1.getName() + "Score: " + player1.getScore(),100, 17);

				g.drawString ("Lives: " + player1.getLives(),250, 17);

				player1.DrawPlayer(g);


				//Spieler 2

				g.drawString (player2.getName() + "Score: " + player2.getScore(),500, 17);

				g.drawString ("Lives: " + player2.getLives(),650, 17);

				player2.DrawPlayer(g);


				ball.DrawBall(g);

		}

		// Wenn alle Leben verbraucht sind

		else if (player1.getLives() <= 0 || player2.getLives() <= 0){


			g.setColor (Color.yellow);


			// Erreichte Punkte und game over

			if(player1.getLives() > player2.getLives()){


				g.drawString ("Game over!", 130, 100);

				g.drawString ("You won", 90, 140);

			} else{


				g.drawString ("Game over!", 130, 100);

				g.drawString ("You loose", 90, 140);

			}



			g.drawString ("Doubleclick on the Applet, to play again!", 20, 220);


			isStoped = true;	// Zurücksetzen der isStoped Variablen, um wieder neu beginnen zu können

		}

	}


	@Override

	public void run() {


		// Erniedrigen der ThreadPriority um zeichnen zu erleichtern

		Thread.currentThread().setPriority(Thread.MIN_PRIORITY);


		while (true){


			if ((player1.getLives() >= 1 && player2.getLives() >= 1) && !isStoped){


				player1.setPosition(mausYPosition);

				ball.move();

				ball.printBallPosition();

			}


			repaint();


			try{


				// Stoppen des Threads für 10 Millisekunden

				Thread.sleep (speed);

			} catch (InterruptedException ex){

				// do nothing

			}


			// Zurücksetzen der ThreadPriority auf Maximalwert

			Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

		}

	}


	// Update - Methode, Realisierung der Doppelpufferung zur Reduzierung des Bildschirmflackerns

	public void update (Graphics g){


		// Initialisierung des DoubleBuffers

		if (dbImage == null){


			dbImage = createImage (this.getSize().width, this.getSize().height);

			dbg = dbImage.getGraphics ();

		}


		// Bildschirm im Hintergrund löschen

		dbg.setColor (getBackground ());

		dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);


		// Auf gelöschten Hintergrund Vordergrund zeichnen

		dbg.setColor (getForeground());

		paint (dbg);


		// Nun fertig gezeichnetes Bild Offscreen auf dem richtigen Bildschirm anzeigen

		g.drawImage (dbImage, 0, 0, this);

	}


	@Override

	public void keyPressed(KeyEvent ev1) {



	}


	@Override

	public void keyReleased(KeyEvent ev2) {



	}


	@Override

	public void keyTyped(KeyEvent ev3) {



	}


	@Override

	public void mouseClicked(MouseEvent arg0) {


		// Spiel läuft

		if (!isStoped)

		{

			// machnix

		}

		// Wenn Spiel noch nicht gestartet ist, oder wieder gestartet wird

		else if (isStoped)

		{

		    // Alle wichtigen Werte zurücksetzen

			isStoped = false;

			init ();

		}

	}


	@Override

	public void mouseEntered(MouseEvent arg0) {



	}


	@Override

	public void mouseExited(MouseEvent arg0) {



	}


	@Override

	public void mousePressed(MouseEvent arg0) {



	}


	@Override

	public void mouseReleased(MouseEvent arg0) {



	}


	@Override

	public void mouseDragged(MouseEvent arg0) {



	}


	@Override

	public void mouseMoved(MouseEvent evt) {


		mausYPosition = evt.getY();

	}

}

Klasse Player:

import java.awt.Color;

import java.awt.Graphics;



public class Player {


	// Deklaration der Variablen

	private int pos_x;				// Variable für die X - Position des Balles

	private int pos_y; 				// Variable für die Y - Position des Balles

	private int breite;

	private int hoehe;

	private int score;				// Punkte des Spielers

	private int lives;				// Leben des Spielers

	private String name;			// Name des Players


	// Farbe des Spielers

	Color color;


	/* Konstruktor */

	public Player(String name, int pos_x, int pos_y, int breite, int hoehe, Color color){


		lives = 10;

		score = 0;

		this.pos_x = pos_x;

		this.pos_y = pos_y;

		this.breite = breite;

		this.hoehe = hoehe;

		this.color = color;

		this.name = name;

	}


	/* Liefert den Namen zurück */

	public String getName(){


		return name;

	}


	/* Liefert die Punkte zurück */

	public int getScore (){


		return score;

	}


	/* Liefert die Leben zurück */

	public int getLives (){


		return lives;

	}


	/* Fügt Punkte hinzu */

	public void addScore (int plus){


		score += plus;

	}


	/* zieht Leben ab */

	public void looseLife (){


		lives --;

	}


	/* Liefert den x_wert zurück */

	public int getX(){


		return pos_x;

	}


	/* Liefert den y_wert zurück */

	public int getY(){


		return pos_y;

	}


	/* Setzt die Position */

	public void setPosition(int y){


		this.pos_y = y;

	}


	// Diese Methode zeichnet den Ball in das Spielfeld

	public void DrawPlayer (Graphics g){


		g.setColor (color);

		g.fill3DRect(pos_x, pos_y, breite, hoehe, true);

	}

}

Klasse Ball:

import java.applet.AudioClip;

import java.awt.Color;

import java.awt.Graphics;

import java.awt.Point;

import java.util.Random;


public class Ball {


	/** Diese Klasse enthält alle Funktionen, die für die Bewegung, Konstruktion,

	Positionsbestimmung, Graphik und Abschiesen des Ball - Objektes von Bedeutung sind. */


	// Deklaration der Variablen

	private Point koord;			// Variable für die X und Y - Position des Balles

	private int radius;				// Radius des Balles

	private double richtung;


	private int first_x;			// Start x - Position

	private int first_y;			// Start y - Position


	private int maxspeed;			// Gibt den Maximalen Speed des Balles an


	// Deklaration der Konstanten (Grenzen des Applets bei einer Gesamtgröße von 380 x 380)

	private final int x_leftout = 30;

	private final int x_rightout = 770;

	private final int y_upout = 30;

	private final int y_downout = 770;


	// Farbe des Balles

	Color color;


	// AudioClip out

	AudioClip out;


	// Refferenz auf das Playerobjekt des Spiels

	Player player1;

	Player player2;


	// Erzeugen des zum Erzeugen von Zufallszahlen nötigen Objektes

	Random rnd = new Random ();


	// Construktor

	public Ball (int radius, int x, int y, int ms, Color color, AudioClip out, Player player1, Player player2){

		// Initialisierung der Variablen

		this.radius = radius;

		koord = new Point(x,y);

		first_x = x;

		first_y = y;

		maxspeed = ms;

		richtung = richtung();

		this.color = color;

		this.out = out;

		this.player1 = player1;

		this.player2 = player2;

	}


	private void bounds(boolean horizontal){


		if(horizontal == true) {


			richtung = (Math.PI * 2) - richtung; 

		}

		if(horizontal == false) {


			richtung = Math.PI - richtung; 

		}


		while(richtung > (2 * Math.PI)){


			richtung = richtung - (2 * Math.PI);

		}


		while(richtung < 0) {


			richtung = richtung + (2 * Math.PI);

		}

	}


	// Move - Methode, berechnet die Bewegung des Balls

	public void move (){


		kiMove();


		koord.x = (int) (koord.x + (maxspeed * Math.sin(Math.PI - richtung - (Math.PI / 2.0) / Math.sin((Math.PI / 2.0)))));

		koord.y = (int) (koord.y + (maxspeed * Math.sin(richtung) / Math.sin((Math.PI / 2.0))));


		isOut();

	}


	private double richtung(){

		richtung = Math.random() * (Math.PI * 2);

		while((richtung > 1 && richtung < 2) || (richtung > 4 && richtung < 5) ||

				(richtung > 2.5 && richtung < 3.5) || (richtung > 5.5 && richtung < 0.5)) 

					richtung = Math.random() * (Math.PI * 2);

		return richtung;

	}


	public void ballStart(){


		richtung = richtung();

		koord.x = first_x;

		koord.y = first_y;

	}


	private boolean isOut (){


		// Ball trifft Player1

		if(koord.x <= 50 && (koord.y > player1.getY() && koord.y < player1.getY() + 80)){


			bounds(false);

			return true;

		}

		// Ball trifft Player2

		else if(koord.x >= 750 && (koord.y > player2.getY() && koord.y < player2.getY() + 80)){


			bounds(false);

			return true;

		}

		// Tor für Player2

		else if(koord.x <= 30 && (koord.y > 800/2/2 && koord.y < 800-(800/2/2))){


			// Leben verlieren

			player1.looseLife();

			ballStart();

			return true;

		}

		// Tor für Player1

		else if(koord.x >= 770 && (koord.y > 800/2/2 && koord.y < 800-(800/2/2))){


			// Leben verlieren

			player2.looseLife();

			ballStart();

			return true;

		}

		// Ball im Linken Aus

		else if (koord.x < x_leftout){


			bounds(false);

			out.play();

			return true;

		}

		// Ball im rechten Aus

		else if (koord.x > x_rightout){


			bounds(false);

			// Abspielen des Audioclips

			out.play();

			return true;

		}

		// Ball im oberen Aus

		else if (koord.y < y_upout){


			bounds(true);

			// Abspielen des Audioclips

			out.play();

			return true;

		}

		// Ball im unteren Aus

		else if (koord.y > y_downout){


			bounds(true);

			// Abspielen des Audioclips

			out.play();

			return true;

		}

		else return false;

	}


	private void kiMove(){


		int kiSpeed = 2;

		if((richtung > 3.93 && richtung < 4.71) || (richtung > 1.57 && richtung < 2.37)) kiSpeed += 1;


		if((koord.y) < player2.getY()+40)  player2.setPosition(player2.getY() - kiSpeed); else player2.setPosition(player2.getY() + kiSpeed);

	}


	public void printBallPosition(){


		System.out.println("Ball -> X: " + koord.x + " Y: " + koord.y);

		System.out.println("Play -> x: " + player1.getX() + " y: " + player1.getY());

	}


	// Diese Methode zeichnet den Ball in das Spielfeld

	public void DrawBall (Graphics g){


		g.setColor (color);

		g.fillOval (koord.x - radius, koord.y - radius, 2 * radius, 2 * radius);

	}

}

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ohne das jetzt im Detail zu prüfen:

Es reicht nicht, bei der Erkennung einer Kollision die Richtung zu ändern. Du musst auch die Position neu berechnen.

Zur Berechnung: Math.sin((Math.PI / 2.0)) ist eine umständliche Schreibweise für 1, deine Koordinatenberechnung ließe sich vereinfachen zu

		koord.x = (int) (koord.x + maxspeed * Math.cos(richtung));
koord.y = (int) (koord.y + maxspeed * Math.sin(richtung));
[/code]

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ah ich glaube ich habe jetzt verstanden wie du das meinst.

Es müsste doch auch funktionieren wenn ich in der Kollisionskontrolle sicherstelle, dass die Kollision die gerade aufgetreten ist (also zum Beispiel Kollision oben) erst wieder auftreten darf sobald eine andere Kollision aufgetreten ist.

D.h. das Problem ist wenn der Ball zu weit in den Rand rutsch die Kollisionskontrolle unkontrolliert öfters hintereinander ausgeführt wird und der Ball so ins Abseits rutscht oder?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Oke ich habe die Methode isOut() (die Kollisionskontrolle) angepasst jetzt läuft es schon um einiges runder.

Vielen Dank für den Denkanstoß.

Falls jemand lust zu zoggen hat einfach die Klasse Ball im obigen Code ersetzten:


import java.applet.AudioClip;

import java.awt.Color;

import java.awt.Graphics;

import java.awt.Point;

import java.util.Random;


public class Ball {


	/** Diese Klasse enthält alle Funktionen, die für die Bewegung, Konstruktion,

	Positionsbestimmung, Graphik und Abschiesen des Ball - Objektes von Bedeutung sind. */


	// Deklaration der Variablen

	private Point koord;			// Variable für die X und Y - Position des Balles

	private int radius;				// Radius des Balles

	private double richtung;


	private int first_x;			// Start x - Position

	private int first_y;			// Start y - Position


	private int maxspeed;			// Gibt den Maximalen Speed des Balles an

	private String lastEvt = "";	// Speichert das zuletzt aufgetretene Event


	// Deklaration der Konstanten (Grenzen des Applets bei einer Gesamtgröße von 380 x 380)

	private final int x_leftout = 30;

	private final int x_rightout = 770;

	private final int y_upout = 30;

	private final int y_downout = 770;


	// Farbe des Balles

	Color color;


	// AudioClip out

	AudioClip out;


	// Refferenz auf das Playerobjekt des Spiels

	Player player1;

	Player player2;


	// Erzeugen des zum Erzeugen von Zufallszahlen nötigen Objektes

	Random rnd = new Random ();


	// Construktor

	public Ball (int radius, int x, int y, int ms, Color color, AudioClip out, Player player1, Player player2){

		// Initialisierung der Variablen

		this.radius = radius;

		koord = new Point(x,y);

		first_x = x;

		first_y = y;

		maxspeed = ms;

		richtung = richtung();

		this.color = color;

		this.out = out;

		this.player1 = player1;

		this.player2 = player2;

	}


	private void bounds(boolean horizontal){


		if(horizontal == true) {


			richtung = (Math.PI * 2) - richtung; 

		}

		if(horizontal == false) {


			richtung = Math.PI - richtung; 

		}


		while(richtung > (2 * Math.PI)){


			richtung = richtung - (2 * Math.PI);

		}


		while(richtung < 0) {


			richtung = richtung + (2 * Math.PI);

		}

	}


	// Move - Methode, berechnet die Bewegung des Balls

	public void move (){


		kiMove();


		koord.x = (int) (koord.x + maxspeed * Math.cos(richtung));

		koord.y = (int) (koord.y + maxspeed * Math.sin(richtung));


		isOut();

	}


	private double richtung(){

		richtung = Math.random() * (Math.PI * 2);

		while((richtung > 1 && richtung < 2) || (richtung > 4 && richtung < 5) ||

				(richtung > 2.5 && richtung < 3.5) || (richtung > 5.5 && richtung < 0.5)) 

					richtung = Math.random() * (Math.PI * 2);

		return richtung;

	}


	public void ballStart(){


		richtung = richtung();

		koord.x = first_x;

		koord.y = first_y;

	}


	private boolean isOut (){


		// Ball trifft Player1 (evt = HPL)

		if(koord.x <= 50 && (koord.y > player1.getY() && koord.y < player1.getY() + 80) && !lastEvt.equals("HPL")){


			lastEvt = "HPL";

			bounds(false);

			return true;

		}

		// Ball trifft Player2 (evt = HPR)

		else if(koord.x >= 750 && (koord.y > player2.getY() && koord.y < player2.getY() + 80) && !lastEvt.equals("HPR")){


			lastEvt = "HPR";

			bounds(false);

			return true;

		}

		// Tor für Player1 (evt = GPL)

		else if(koord.x <= 30 && (koord.y > 800/2/2 && koord.y < 800-(800/2/2)) && !lastEvt.equals("GPL")){


			lastEvt = "GPL";

			// Leben verlieren

			player1.looseLife();

			ballStart();

			return true;

		}

		// Tor für Player2 (evt = GPR)

		else if(koord.x >= 770 && (koord.y > 800/2/2 && koord.y < 800-(800/2/2)) && !lastEvt.equals("GPR")){


			lastEvt = "GPR";

			// Leben verlieren

			player2.looseLife();

			ballStart();

			return true;

		}

		// Ball im Linken Aus (evt = BLEFT)

		else if (koord.x < x_leftout && !lastEvt.equals("BLEFT")){


			lastEvt = "BLEFT";

			bounds(false);

			out.play();

			return true;

		}

		// Ball im rechten Aus (evt = BRIGHT)

		else if (koord.x > x_rightout && !lastEvt.equals("BRIGHT")){


			lastEvt = "BRIGHT";

			bounds(false);

			// Abspielen des Audioclips

			out.play();

			return true;

		}

		// Ball im oberen Aus (evt = BTOP)

		else if (koord.y < y_upout && !lastEvt.equals("BTOP")){


			lastEvt = "BTOP";

			bounds(true);

			// Abspielen des Audioclips

			out.play();

			return true;

		}

		// Ball im unteren Aus (evt = BBOTTOM)

		else if (koord.y > y_downout && !lastEvt.equals("BBOTTOM")){


			lastEvt = "BBOTTOM";

			bounds(true);

			// Abspielen des Audioclips

			out.play();

			return true;

		}

		else return false;

	}


	private void kiMove(){


		int kiSpeed = 2;

		if((richtung > 3.93 && richtung < 4.71) || (richtung > 1.57 && richtung < 2.37)) kiSpeed += 1;


		if((koord.y) < player2.getY()+40)  player2.setPosition(player2.getY() - kiSpeed); else player2.setPosition(player2.getY() + kiSpeed);

	}


	public void printBallPosition(){


		System.out.println("Ball -> X: " + koord.x + " Y: " + koord.y);

		System.out.println("Play -> x: " + player1.getX() + " y: " + player1.getY());

	}


	// Diese Methode zeichnet den Ball in das Spielfeld

	public void DrawBall (Graphics g){


		g.setColor (color);

		g.fillOval (koord.x - radius, koord.y - radius, 2 * radius, 2 * radius);

	}

}

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...