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);
}
}