bouncing ball / abprallender Ball

Jxhnny.lpz

Mitglied
Hallo an die Runde,

ich habe ein Problem, das ich nicht weiß wie ich am besten einen abprallenden Ball ich mein Programm einbaue.

Der Ball soll folgende Eigenschaften haben:

  • An jeder wand/Rand oder Objekt mit Einfallswinkel = Ausfallswinkel abprallen
  • Die Geschwindigkeit soll einstellbar sein


Würde mich freuen, wen ihr mir Helfen könntet.
Ich bin für jede Hilfe/Tipp dankbar.


Java:
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import static java.awt.Color.BLACK;
import static java.awt.Color.GRAY;

public class BBG {


        static JButton button = new JButton();
        static JTextField Field = new JTextField();




    public static void main(String[] args) {

        Funktionen();

    }



    private static void Funktionen() {



        //Interface Attribute
        JFrame frame = new JFrame("Brick Breaker Game");

        frame.setBounds(30, 30, 700, 720);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setDefaultLookAndFeelDecorated(true);
        frame.setLayout(null);

        frame.setVisible(true);

        frame.add(button);
        frame.add(Field);

//SpielFeld Attribute
        Field.setBounds(50, 50, 580, 575);
        Field.setEnabled(false);
        Field.setVisible(true);

        //Ball Attribute


//Button Attribute
        button.setSize(150, 20);
        button.setLocation(260, 560);
        button.setBackground(GRAY);
        button.setBorder(new LineBorder(BLACK));
        button.doClick();
        button.setEnabled(false);
        button.setVisible(true);
        //Ende Attribute


// Anfang KeyEvent
        KeyListener keyListener = new KeyListener() {
            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyPressed(KeyEvent e) {
                System.out.println(e.getKeyCode() + ", " + KeyEvent.VK_RIGHT);

                //Button Bewegung (Pfeiltasten + A&D)
                if (e.getKeyCode() == KeyEvent.VK_RIGHT) {

                    System.out.println("klappt");

                    int xPos = button.getX();
                    System.out.println("xPos: " + button.getX());

                    if (xPos == 480) {
                        button.setLocation(480, 560);
                    } else {

                        int i = xPos + 10;
                        button.setLocation(i, 560);

                        System.out.println("bL: " + button.getLocation());
                    }

                } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {

                    System.out.println("klappt");

                    int xPos = button.getX();
                    System.out.println("xPos: " + button.getX());

                    if (xPos == 50) {
                        button.setLocation(50, 560);
                    } else {

                        int i = xPos - 10;
                        button.setLocation(i, 560);

                        System.out.println("bL: " + button.getLocation());
                    }

                }
                if (e.getKeyCode() == KeyEvent.VK_D) {

                    System.out.println("klappt");

                    int xPos = button.getX();
                    System.out.println("xPos: " + button.getX());

                    if (xPos == 480) {
                        button.setLocation(480, 560);
                    } else {

                        int i = xPos + 10;
                        button.setLocation(i, 560);

                        System.out.println("bL: " + button.getLocation());
                    }

                } else if (e.getKeyCode() == KeyEvent.VK_A) {

                    System.out.println("klappt");

                    int xPos = button.getX();
                    System.out.println("xPos: " + button.getX());

                    if (xPos == 50) {
                        button.setLocation(50, 560);
                    } else {

                        int i = xPos - 10;
                        button.setLocation(i, 560);

                        System.out.println("bL: " + button.getLocation());
                    }

                }
            }

            @Override
            public void keyReleased(KeyEvent e) {
            }



        };
        //Ende KeyEvent

        frame.addKeyListener(keyListener);


    }
}
 

Jxhnny.lpz

Mitglied
Vielen Dank für die Hilfe.
Ich habe mir dein Programm angeschaut, aber ich muss Ihnen sagen das ich davon nicht so viel verstanden habe. Deswegen möchte ich Sie hiermit fragen ob Sie mein Programm darauf anpassen können?
Oder eine Ausführliche Beschreibung zu Ihrem Programm für jede Zeile geben können.

Würde mich freuen.
 

MarvinsDepression

Bekanntes Mitglied
Ganz allgemein. Wenn es sich um senkrechte und waagrechte Prallwände handelt, ist es ganz einfach. der Ball hat einen Bewegungsvektor, der sich aus zwei Komponenten zusammensetzt: x-Richtung und y-Richtung.
Prallt der Ball an eine Senkrechte, so invertiert sich der x-Vektor, an der Waagrechten invertiert sich die y-Komponente.

Für schiefe Wände kommts Du an Winkel-Berechnug oder Matrizen-Operationen nicht vorbei.
 

Blender3D

Top Contributor
Vielen Dank für die Hilfe.
Ich habe mir dein Programm angeschaut, aber ich muss Ihnen sagen das ich davon nicht so viel verstanden habe. Deswegen möchte ich Sie hiermit fragen ob Sie mein Programm darauf anpassen können?
Oder eine Ausführliche Beschreibung zu Ihrem Programm für jede Zeile geben können.
1667950383613.png
Ich habe daraus ein kleines Demo gemacht lade die Jar zum Testen herunter. Es bewegen sich dabei Bälle innerhalb eines begrenzten Spielfeldes. Diese prallen gegenseitig und an den Wänden ab. Außer am Boden gehen sie verloren. Am Boden ist ein Schläger, der nach links oder rechts bewegt wird mittels der Tasten Pfeil links und rechts.
Die Taste s startet neue Bälle.
Es handelt sich hierbei um ein Demo. Der Code erhebt nicht den Anspruch der Bestmögliche zu sein und ist nicht entsprechend getestet.
Er soll nur einen möglichen Weg aufzeigen, wie man Grafiken bewegt und auf Kollision überprüft.
Für die Bewegung verwende ich die Klasse Vector. Sie stellt einen 2Dimensionalen Vektor dar.
Die Klasse Sprite stellt ein bewegte Grafik mit Kollisionserkennung innerhalb von Grenzen zur Verfügung.
Der Schlaeger (racket) ist vom Typ Sprite.
Die Klasse Ball erbt von Sprite und ist damit auch von diesem Typ.
Die Klasse BallGame erbt von JPanel und ist vom Typ Runnable.
Sie ist für den Gameloop , die Darstellung und die Tastatur zuständig.
Sie beinhaltet die Bälle und den Schläger und kennt die Grenzen des Spielfeldes.
Die Klasse StartGame erzeugt das Fenster fügt Ballgame hinzu, bekommt den KeyListener und startet das Demo.
Hier der Code bei Fragen dazu einfach hier posten.
Java:
import javax.swing.JFrame;

public class StartGame {

    public static void main(String[] args) {
        BallGame game = new BallGame(400, 400);
        JFrame frame = new JFrame("Ballkollison Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(game);
        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);
        frame.addKeyListener(game.getKeyBoard());
        game.start();
    }
}
Java:
import java.awt.geom.Point2D;

@SuppressWarnings("serial")
public class Vector extends Point2D.Float {

    public Vector(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public Vector add(final Vector v) { // addiert Vektoren
        return new Vector(x + v.x, y + v.y);
    }

    public float dotProduct(Vector v) {
        return x * v.x + y * v.y;
    }

    public Vector norm() { // gibt den Vektor der Laenge 1 zurueck
        return scale(1 / size());
    }

    public Vector scale(float scalar) { // multipilziert mit Wert
        return new Vector(x * scalar, y * scalar);
    }

    public Vector sub(final Vector v) {// subtrahiert Vektoren
        return new Vector(x - v.x, y - v.y);
    }

    public float size() { // berechnet die Laenge des Vektors
        return (float) (Math.sqrt(x * x + y * y));
    }

}
Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

public class Sprite {
    public static final int HIT_TOP = 1;
    public static final int HIT_RIGHT = 2;
    public static final int HIT_BOTTOM = 4;
    public static final int HIT_LEFT = 8;
    public static final int HIT_NO_BOUND = 0;
    protected Vector pos = new Vector(0, 0); // Position des Sprites
    protected Vector dir = new Vector(0, 0); // Bewegungsrichtung des Sprites
    protected float speed = 0; // Geschwindigkeit des Sprites
    protected int width;
    protected int height;
    protected Color color = Color.WHITE;
    protected Rectangle boundingBox;

    public Sprite(int width, int height) {
        this.width = width;
        this.height = height;
        boundingBox = new Rectangle(width, height);
    }

    public void draw(Graphics g) {
        g.setColor(color);
        g.fillRect(getX(), getY(), width, height);
    }

    public double getAngle() {
        double angle = 0;
        if (dir.x != 0)
            angle = Math.toDegrees(Math.acos(dir.x / getSpeed()));
        else
            angle = Math.toDegrees(Math.asin(dir.y / getSpeed()));
        angle = Math.round(angle);
        return (dir.y < 0) ? 360 - angle : angle;
    }

    public Rectangle getBoundingBox() {
        boundingBox.x = getX();
        boundingBox.y = getY();
        return boundingBox;
    }

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

    public float getSpeed() {
        return speed;
    }

    public int getX() {
        return (int) pos.x;
    }

    public int getY() {
        return (int) pos.y;
    }

    public void invertX() {
        dir.x = -dir.x;
    }

    public void invertY() {
        dir.y = -dir.y;
    }

    public static boolean isBottomHit(int hit) {
        return (hit & HIT_BOTTOM) != 0;
    }

    public boolean isCollision(Sprite s) {
        return s.getBoundingBox().intersects(getBoundingBox());
    }

    public static boolean isHorizontalHit(int hit) {
        return isLeftHit(hit) || isRightHit(hit);
    }

    public static boolean isLeftHit(int hit) {
        return (hit & HIT_LEFT) != 0;
    }

    public static boolean isRightHit(int hit) {
        return (hit & HIT_RIGHT) != 0;
    }

    public static boolean isVerticalHit(int hit) {
        return isTopHit(hit) || isBottomHit(hit);
    }

    public static boolean isTopHit(int hit) {
        return (hit & HIT_TOP) != 0;
    }

    public int move(Rectangle bounds) {
        pos.x += dir.x * speed;
        pos.y += dir.y * speed;
        if (bounds == null)
            return HIT_NO_BOUND;
        return moveInsideBounds(bounds);
    }

    private int moveInsideBounds(Rectangle bounds) {
        int hit = HIT_NO_BOUND;
        if (pos.x > bounds.x + bounds.width - width) {
            pos.x = bounds.x + bounds.width - width;
            hit = HIT_RIGHT;
        } else if (pos.x < bounds.x) {
            pos.x = bounds.x;
            hit = HIT_LEFT;
        }
        if (pos.y > bounds.y + bounds.height - height) {
            pos.y = bounds.y + bounds.height - height;
            hit += HIT_BOTTOM;
        } else if (pos.y < bounds.y) {
            pos.y = bounds.y;
            hit += HIT_TOP;
        }
        return hit;
    }

    public void setAngle(float angle) {
        angle = (float) Math.toRadians(angle);
        dir.x = (float) (Math.cos(angle));
        dir.y = (float) (Math.sin(angle));
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public void setPos(int x, int y) {
        pos.x = x;
        pos.y = y;
    }

    public void setSpeed(float speed) {
        this.speed = speed;
    }

    @Override
    public String toString() {
        return String.format("[%.2f:%.2f] > %.2f %d°", pos.x, pos.y, getSpeed(), (int) getAngle());
    }

}
Java:
import java.awt.Graphics;

public class Ball extends Sprite {

    public Ball(int size) {
        super(size, size);
    }

    public boolean bounce(Ball b) {
        if (!b.isCollision(this))
            return false;
        float distance = (float) pos.distance(b.pos);
        // pull back balls overlapping
        float overlapp = getRadius() + b.getRadius() - distance;

        Vector x_d = b.pos.sub(pos);
        Vector c = x_d.scale(overlapp / (2 * distance));
        pos = pos.sub(c);
        b.pos = b.pos.add(c);
        Vector v_d = dir.sub(b.dir);
        Vector w = x_d.scale((1 / (distance * distance) * x_d.dotProduct(v_d)));
        Vector d1 = dir.scale(speed);
        Vector d2 = b.dir.scale(b.speed);
        d1 = d1.sub(w);
        d2 = d2.add(w);
        dir = d1.norm();
        speed = d1.size();
        b.dir = d2.norm();
        b.speed = d2.size();
        return true;
    }

    @Override
    public void draw(Graphics g) {
        g.setColor(color);
        g.fillOval(getX(), getY(), width, height);
    }

    public float getRadius() {
        return width / 2;
    }

    public boolean isCollision(Ball b) {
        if (b == null || !super.isCollision(b))
            return false;
        float aRadius = getRadius();
        float bRadius = b.getRadius();
        float xx = (pos.x + aRadius) - (b.pos.x + bRadius);
        float yy = (pos.y + aRadius) - (b.pos.y + bRadius);
        return Math.sqrt(xx * xx + yy * yy) < (aRadius + bRadius);
    }

}
Java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

import javax.swing.JPanel;

@SuppressWarnings("serial")
public class BallGame extends JPanel implements Runnable {
    private Random rnd = new Random(System.currentTimeMillis());
    private Thread animator; // Thread der die Animation steuert
    private boolean running = false; // Spiel läuft bei true
    private Rectangle bounds; // Grenzen des Spielfeldes
    private Ball[] balls; // Array der Baelle
    private Sprite racket; // Schlaeger
    private float racketSpeed = 3;

    public BallGame(int width, int height) {
        setPreferredSize(new Dimension(width, height));
        init(width, height);
    }

    private void createRacket(int width, int height) {
        int w = width / 5;
        int h = height / 20;
        racket = new Sprite(w, h);
        racket.setPos((width - w) / 2, height - 2 * h);
    }

    private void createBalls(int width, int height) {
        int[][] start = { { width / 4, height / 4 }, { 3 * width / 4, height / 4 }, { 3 * width / 4, 3 * height / 4 },
                { width / 4, 3 * height / 4 }, { width / 2, height / 2 } };
        Color[] colors = { Color.WHITE, Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN };
        balls = new Ball[colors.length];
        for (int i = 0; i < colors.length; i++) {
            balls[i] = new Ball(40);
            balls[i].setAngle(rnd.nextInt(259));
            balls[i].setSpeed(5.2f);
            balls[i].setColor(colors[i]);
            balls[i].setPos(start[i][0], start[i][1]);
        }
    }

    private void doBallCollisions(int id, int hit) {
        Ball b = balls[id];
        if (Sprite.isVerticalHit(hit))
            b.invertY();
        if (Sprite.isHorizontalHit(hit))
            b.invertX();
        for (int i = id + 1; i < balls.length; i++) {
            if (b.isCollision(balls[i]))
                b.bounce(balls[i]);
        }
    }

    private void doRacketCollisions(Ball b) {
        if (racket.isCollision(b)) {
            int xR = racket.getX();
            int xB = b.getX();
            if (xB >= xR - b.getWidth() && xB < xR + racket.width) {
                b.setPos(b.getX(), racket.getY() - b.height);
                b.invertY();
                b.setSpeed(b.getSpeed() * 1.1f);
            }
        }
    }

    public KeyListener getKeyBoard() {
        return new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                racket.setSpeed(0); // Schlaeger bleibt stehen
                if (e.getKeyCode() == KeyEvent.VK_S) { // startet Spiel neu
                    stop();
                    createBalls(bounds.width, bounds.height);
                    start();
                }
            }

            public void keyPressed(KeyEvent e) {
                int code = e.getKeyCode();
                if (code == KeyEvent.VK_LEFT) {// Pfeiltaste links
                    racket.setSpeed(racketSpeed);
                    racket.setAngle(180); // Scheager bewegt nach links
                }
                if (code == KeyEvent.VK_RIGHT) { // Pfeiltaste rechts
                    racket.setSpeed(racketSpeed);
                    racket.setAngle(0); // Schlaeger bewegt nach rechts
                }
            }
        };
    }

    public void init(int width, int height) {
        int off = 10; // Randgrösse
        bounds = new Rectangle(off, off, width - 2 * off, height - 2 * off);
        createBalls(width, height);
        createRacket(width, height);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
        for (Ball b : balls) { // zeichne die Baelle
            if (b != null)
                b.draw(g);
        }
        racket.draw(g); // zeichne den Schlaeger
    }

    @Override
    public void run() {// very simple game loop
        while (running) {
            update(); // bewege die Baelle und den Schlaeger
            repaint(); // ruft indirekt paintComponent auf
            try {
                Thread.sleep(7);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Starte das Spiel
     */
    public void start() {
        stop();
        animator = new Thread(this);
        running = true;
        animator.start();
    }

    /**
     * Stopt das Spiel
     */
    public void stop() {
        running = false;
        while (animator != null && animator.isAlive())
            ;
        animator = null;
    }

    /**
     * Bewege die Baelle und den Schlaeger
     */
    private void update() {
        int hit;
        for (int i = 0; i < balls.length; i++) {
            if (balls[i] != null) {
                hit = balls[i].move(bounds);
                doBallCollisions(i, hit); // checke Kollision der Baelle
                doRacketCollisions(balls[i]);// checke Kollision Ball mit Schlaeger
                if (Sprite.isBottomHit(hit))
                    balls[i] = null;// Ball am Boden wird aus dem Spiel genommen
            }
        }
        hit = racket.move(bounds);
        if (Sprite.isLeftHit(hit))
            racket.setSpeed(0);// stopt Schlaeger am linken Rand
        if (Sprite.isRightHit(hit))
            racket.setSpeed(0);// stopt Schlaeger am rechten Rand
    }

}
 

Anhänge

  • pong.jar
    9 KB · Aufrufe: 1

Jxhnny.lpz

Mitglied
Anhang anzeigen 19569
Ich habe daraus ein kleines Demo gemacht lade die Jar zum Testen herunter. Es bewegen sich dabei Bälle innerhalb eines begrenzten Spielfeldes. Diese prallen gegenseitig und an den Wänden ab. Außer am Boden gehen sie verloren. Am Boden ist ein Schläger, der nach links oder rechts bewegt wird mittels der Tasten Pfeil links und rechts.
Die Taste s startet neue Bälle.
Es handelt sich hierbei um ein Demo. Der Code erhebt nicht den Anspruch der Bestmögliche zu sein und ist nicht entsprechend getestet.
Er soll nur einen möglichen Weg aufzeigen, wie man Grafiken bewegt und auf Kollision überprüft.
Für die Bewegung verwende ich die Klasse Vector. Sie stellt einen 2Dimensionalen Vektor dar.
Die Klasse Sprite stellt ein bewegte Grafik mit Kollisionserkennung innerhalb von Grenzen zur Verfügung.
Der Schlaeger (racket) ist vom Typ Sprite.
Die Klasse Ball erbt von Sprite und ist damit auch von diesem Typ.
Die Klasse BallGame erbt von JPanel und ist vom Typ Runnable.
Sie ist für den Gameloop , die Darstellung und die Tastatur zuständig.
Sie beinhaltet die Bälle und den Schläger und kennt die Grenzen des Spielfeldes.
Die Klasse StartGame erzeugt das Fenster fügt Ballgame hinzu, bekommt den KeyListener und startet das Demo.
Hier der Code bei Fragen dazu einfach hier posten.
Java:
import javax.swing.JFrame;

public class StartGame {

    public static void main(String[] args) {
        BallGame game = new BallGame(400, 400);
        JFrame frame = new JFrame("Ballkollison Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(game);
        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);
        frame.addKeyListener(game.getKeyBoard());
        game.start();
    }
}
Java:
import java.awt.geom.Point2D;

@SuppressWarnings("serial")
public class Vector extends Point2D.Float {

    public Vector(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public Vector add(final Vector v) { // addiert Vektoren
        return new Vector(x + v.x, y + v.y);
    }

    public float dotProduct(Vector v) {
        return x * v.x + y * v.y;
    }

    public Vector norm() { // gibt den Vektor der Laenge 1 zurueck
        return scale(1 / size());
    }

    public Vector scale(float scalar) { // multipilziert mit Wert
        return new Vector(x * scalar, y * scalar);
    }

    public Vector sub(final Vector v) {// subtrahiert Vektoren
        return new Vector(x - v.x, y - v.y);
    }

    public float size() { // berechnet die Laenge des Vektors
        return (float) (Math.sqrt(x * x + y * y));
    }

}
Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

public class Sprite {
    public static final int HIT_TOP = 1;
    public static final int HIT_RIGHT = 2;
    public static final int HIT_BOTTOM = 4;
    public static final int HIT_LEFT = 8;
    public static final int HIT_NO_BOUND = 0;
    protected Vector pos = new Vector(0, 0); // Position des Sprites
    protected Vector dir = new Vector(0, 0); // Bewegungsrichtung des Sprites
    protected float speed = 0; // Geschwindigkeit des Sprites
    protected int width;
    protected int height;
    protected Color color = Color.WHITE;
    protected Rectangle boundingBox;

    public Sprite(int width, int height) {
        this.width = width;
        this.height = height;
        boundingBox = new Rectangle(width, height);
    }

    public void draw(Graphics g) {
        g.setColor(color);
        g.fillRect(getX(), getY(), width, height);
    }

    public double getAngle() {
        double angle = 0;
        if (dir.x != 0)
            angle = Math.toDegrees(Math.acos(dir.x / getSpeed()));
        else
            angle = Math.toDegrees(Math.asin(dir.y / getSpeed()));
        angle = Math.round(angle);
        return (dir.y < 0) ? 360 - angle : angle;
    }

    public Rectangle getBoundingBox() {
        boundingBox.x = getX();
        boundingBox.y = getY();
        return boundingBox;
    }

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

    public float getSpeed() {
        return speed;
    }

    public int getX() {
        return (int) pos.x;
    }

    public int getY() {
        return (int) pos.y;
    }

    public void invertX() {
        dir.x = -dir.x;
    }

    public void invertY() {
        dir.y = -dir.y;
    }

    public static boolean isBottomHit(int hit) {
        return (hit & HIT_BOTTOM) != 0;
    }

    public boolean isCollision(Sprite s) {
        return s.getBoundingBox().intersects(getBoundingBox());
    }

    public static boolean isHorizontalHit(int hit) {
        return isLeftHit(hit) || isRightHit(hit);
    }

    public static boolean isLeftHit(int hit) {
        return (hit & HIT_LEFT) != 0;
    }

    public static boolean isRightHit(int hit) {
        return (hit & HIT_RIGHT) != 0;
    }

    public static boolean isVerticalHit(int hit) {
        return isTopHit(hit) || isBottomHit(hit);
    }

    public static boolean isTopHit(int hit) {
        return (hit & HIT_TOP) != 0;
    }

    public int move(Rectangle bounds) {
        pos.x += dir.x * speed;
        pos.y += dir.y * speed;
        if (bounds == null)
            return HIT_NO_BOUND;
        return moveInsideBounds(bounds);
    }

    private int moveInsideBounds(Rectangle bounds) {
        int hit = HIT_NO_BOUND;
        if (pos.x > bounds.x + bounds.width - width) {
            pos.x = bounds.x + bounds.width - width;
            hit = HIT_RIGHT;
        } else if (pos.x < bounds.x) {
            pos.x = bounds.x;
            hit = HIT_LEFT;
        }
        if (pos.y > bounds.y + bounds.height - height) {
            pos.y = bounds.y + bounds.height - height;
            hit += HIT_BOTTOM;
        } else if (pos.y < bounds.y) {
            pos.y = bounds.y;
            hit += HIT_TOP;
        }
        return hit;
    }

    public void setAngle(float angle) {
        angle = (float) Math.toRadians(angle);
        dir.x = (float) (Math.cos(angle));
        dir.y = (float) (Math.sin(angle));
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public void setPos(int x, int y) {
        pos.x = x;
        pos.y = y;
    }

    public void setSpeed(float speed) {
        this.speed = speed;
    }

    @Override
    public String toString() {
        return String.format("[%.2f:%.2f] > %.2f %d°", pos.x, pos.y, getSpeed(), (int) getAngle());
    }

}
Java:
import java.awt.Graphics;

public class Ball extends Sprite {

    public Ball(int size) {
        super(size, size);
    }

    public boolean bounce(Ball b) {
        if (!b.isCollision(this))
            return false;
        float distance = (float) pos.distance(b.pos);
        // pull back balls overlapping
        float overlapp = getRadius() + b.getRadius() - distance;

        Vector x_d = b.pos.sub(pos);
        Vector c = x_d.scale(overlapp / (2 * distance));
        pos = pos.sub(c);
        b.pos = b.pos.add(c);
        Vector v_d = dir.sub(b.dir);
        Vector w = x_d.scale((1 / (distance * distance) * x_d.dotProduct(v_d)));
        Vector d1 = dir.scale(speed);
        Vector d2 = b.dir.scale(b.speed);
        d1 = d1.sub(w);
        d2 = d2.add(w);
        dir = d1.norm();
        speed = d1.size();
        b.dir = d2.norm();
        b.speed = d2.size();
        return true;
    }

    @Override
    public void draw(Graphics g) {
        g.setColor(color);
        g.fillOval(getX(), getY(), width, height);
    }

    public float getRadius() {
        return width / 2;
    }

    public boolean isCollision(Ball b) {
        if (b == null || !super.isCollision(b))
            return false;
        float aRadius = getRadius();
        float bRadius = b.getRadius();
        float xx = (pos.x + aRadius) - (b.pos.x + bRadius);
        float yy = (pos.y + aRadius) - (b.pos.y + bRadius);
        return Math.sqrt(xx * xx + yy * yy) < (aRadius + bRadius);
    }

}
Java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

import javax.swing.JPanel;

@SuppressWarnings("serial")
public class BallGame extends JPanel implements Runnable {
    private Random rnd = new Random(System.currentTimeMillis());
    private Thread animator; // Thread der die Animation steuert
    private boolean running = false; // Spiel läuft bei true
    private Rectangle bounds; // Grenzen des Spielfeldes
    private Ball[] balls; // Array der Baelle
    private Sprite racket; // Schlaeger
    private float racketSpeed = 3;

    public BallGame(int width, int height) {
        setPreferredSize(new Dimension(width, height));
        init(width, height);
    }

    private void createRacket(int width, int height) {
        int w = width / 5;
        int h = height / 20;
        racket = new Sprite(w, h);
        racket.setPos((width - w) / 2, height - 2 * h);
    }

    private void createBalls(int width, int height) {
        int[][] start = { { width / 4, height / 4 }, { 3 * width / 4, height / 4 }, { 3 * width / 4, 3 * height / 4 },
                { width / 4, 3 * height / 4 }, { width / 2, height / 2 } };
        Color[] colors = { Color.WHITE, Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN };
        balls = new Ball[colors.length];
        for (int i = 0; i < colors.length; i++) {
            balls[i] = new Ball(40);
            balls[i].setAngle(rnd.nextInt(259));
            balls[i].setSpeed(5.2f);
            balls[i].setColor(colors[i]);
            balls[i].setPos(start[i][0], start[i][1]);
        }
    }

    private void doBallCollisions(int id, int hit) {
        Ball b = balls[id];
        if (Sprite.isVerticalHit(hit))
            b.invertY();
        if (Sprite.isHorizontalHit(hit))
            b.invertX();
        for (int i = id + 1; i < balls.length; i++) {
            if (b.isCollision(balls[i]))
                b.bounce(balls[i]);
        }
    }

    private void doRacketCollisions(Ball b) {
        if (racket.isCollision(b)) {
            int xR = racket.getX();
            int xB = b.getX();
            if (xB >= xR - b.getWidth() && xB < xR + racket.width) {
                b.setPos(b.getX(), racket.getY() - b.height);
                b.invertY();
                b.setSpeed(b.getSpeed() * 1.1f);
            }
        }
    }

    public KeyListener getKeyBoard() {
        return new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                racket.setSpeed(0); // Schlaeger bleibt stehen
                if (e.getKeyCode() == KeyEvent.VK_S) { // startet Spiel neu
                    stop();
                    createBalls(bounds.width, bounds.height);
                    start();
                }
            }

            public void keyPressed(KeyEvent e) {
                int code = e.getKeyCode();
                if (code == KeyEvent.VK_LEFT) {// Pfeiltaste links
                    racket.setSpeed(racketSpeed);
                    racket.setAngle(180); // Scheager bewegt nach links
                }
                if (code == KeyEvent.VK_RIGHT) { // Pfeiltaste rechts
                    racket.setSpeed(racketSpeed);
                    racket.setAngle(0); // Schlaeger bewegt nach rechts
                }
            }
        };
    }

    public void init(int width, int height) {
        int off = 10; // Randgrösse
        bounds = new Rectangle(off, off, width - 2 * off, height - 2 * off);
        createBalls(width, height);
        createRacket(width, height);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
        for (Ball b : balls) { // zeichne die Baelle
            if (b != null)
                b.draw(g);
        }
        racket.draw(g); // zeichne den Schlaeger
    }

    @Override
    public void run() {// very simple game loop
        while (running) {
            update(); // bewege die Baelle und den Schlaeger
            repaint(); // ruft indirekt paintComponent auf
            try {
                Thread.sleep(7);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Starte das Spiel
     */
    public void start() {
        stop();
        animator = new Thread(this);
        running = true;
        animator.start();
    }

    /**
     * Stopt das Spiel
     */
    public void stop() {
        running = false;
        while (animator != null && animator.isAlive())
            ;
        animator = null;
    }

    /**
     * Bewege die Baelle und den Schlaeger
     */
    private void update() {
        int hit;
        for (int i = 0; i < balls.length; i++) {
            if (balls[i] != null) {
                hit = balls[i].move(bounds);
                doBallCollisions(i, hit); // checke Kollision der Baelle
                doRacketCollisions(balls[i]);// checke Kollision Ball mit Schlaeger
                if (Sprite.isBottomHit(hit))
                    balls[i] = null;// Ball am Boden wird aus dem Spiel genommen
            }
        }
        hit = racket.move(bounds);
        if (Sprite.isLeftHit(hit))
            racket.setSpeed(0);// stopt Schlaeger am linken Rand
        if (Sprite.isRightHit(hit))
            racket.setSpeed(0);// stopt Schlaeger am rechten Rand
    }

}
Vielen Dank.

Ich werde mich erstmal damit auseinander setzen.
Ich werde bestimmt nachher noch ein paar fragen offen haben.

Wäre toll wenn du mir dabei dann auch helfen könntest.
 

Jxhnny.lpz

Mitglied
Ich habe fragen zu diesen Befehlen.
Was machen diese Befehle und wie Funktionieren sie.

Java:
@Override

public String toString() {

    return String.format("[%.2f:%.2f] > %.2f %d°", pos.x, pos.y, getSpeed(), (int) getAngle());

Java:
private int moveInsideBounds(Rectangle bounds) {
        int hit = HIT_NO_BOUND;
        if (pos.x > bounds.x + bounds.width - width) {
            pos.x = bounds.x + bounds.width - width;
            hit = HIT_RIGHT;
        } else if (pos.x < bounds.x) {
            pos.x = bounds.x;
            hit = HIT_LEFT;
        }
        if (pos.y > bounds.y + bounds.height - height) {
            pos.y = bounds.y + bounds.height - height;
            hit += HIT_BOTTOM;
        } else if (pos.y < bounds.y) {
            pos.y = bounds.y;
            hit += HIT_TOP;
        }
        return hit;
    }
 

Jw456

Top Contributor
%.2f ist ein Platzhalter in einem String.
Es wird eine float Zahl angezeigt.
Sie wird als KommaZahl mit zwei Komma Stellen formatiert.
pos.x ist in disem fall sicher eine float Zahl.
Bei den anderen das gleiche.

 

Blender3D

Top Contributor
Ich habe fragen zu diesen Befehlen.
Was machen diese Befehle und wie Funktionieren sie.
Java:
@Override
public String toString() {
    return String.format("[%.2f:%.2f] > %.2f %d°", pos.x, pos.y, getSpeed(), (int) getAngle());
}
Hierbei handelt es sich um die toString() Methode der Klasse Sprite.
Dazu sollte man wissen, dass jedes Object in Java diese Methode hat. Sie wird aufgerufen, wenn versucht wird das Objekt in einen String zu konvertierten.
Ich habe für die Klasse Sprite diese Methode überschrieben worauf die Annotation @Override hinweist.
Das hilft beim Debuggen. Ohne dieses Überschreiben würde der konvertierte String in etwa so aussehen:
String@1fee6fc
Mit dieser Funktion in etwas so
[3.23;120;00]> 12 30°
Also der Sprite befindet sich auf den Koordinaten 3.23, 120 und bewegt sich mit der Geschwindigkeit 12 in einem Winkel von 45°.
Die benutzte String.format Methode hilft dabei, diesen zu formatieren. Wie oben von @Jw456 bereits erwähnt.
Java:
private int moveInsideBounds(Rectangle bounds) {
        int hit = HIT_NO_BOUND;
        if (pos.x > bounds.x + bounds.width - width) {
            pos.x = bounds.x + bounds.width - width;
            hit = HIT_RIGHT;
        } else if (pos.x < bounds.x) {
            pos.x = bounds.x;
            hit = HIT_LEFT;
        }
        if (pos.y > bounds.y + bounds.height - height) {
            pos.y = bounds.y + bounds.height - height;
            hit += HIT_BOTTOM;
        } else if (pos.y < bounds.y) {
            pos.y = bounds.y;
            hit += HIT_TOP;
        }
        return hit;
    }
Diese Methode überprüft ob der Sprite die übergebenen Spielfeldgrenzen erreicht hat. Die Klasse Rectangle besitzt folgende Variablen, um das damit beschriebene Rechteck zu definieren. x, y, width und height.
1667990343985.png
Falls das passiert wird der Sprite so positioniert, dass er genau die jeweilige Grenze berührt und nicht darüber hinausragt.
Der Sprite bewegt sich ja mit einer Geschwindigkeit. Falls er sich zum Beispiel mit eine Geschwindigkeit von 7 Pixel nach links bewegt und sich auf der Position 3, 100 befindet mit bounds.x = 0
würde er als nächstes auf der Position 3-7,100 also -4,100 stehen. Er hätte also den linken Rand des Spielfeldes durchschlagen.
Der relevante Code Teil dabei ist
Java:
       int hit = HIT_NO_BOUND;
        if (pos.x > bounds.x + bounds.width - width) {
            pos.x = bounds.x + bounds.width - width;
            hit = HIT_RIGHT;
        } else if (pos.x < bounds.x) {
            pos.x = bounds.x;  // setzt die x Position der Grenze gleich
            hit = HIT_LEFT; // merke welche Grenze getroffen wurde
        }
moveInsideBounds() hat einen Integer als Rückgabewert. Dieser dient dazu, den Aufrufer darüber zu informieren welche Grenze getroffen wurde.
Ich habe der Klasse Sprite dafür Konstante Werte gegeben, um die Bedeutung der Zahlen besser lesbarer zu machen.
Java:
public class Sprite {
    public static final int HIT_TOP = 1; // Binär 0001
    public static final int HIT_RIGHT = 2; // Binär 0010
    public static final int HIT_BOTTOM = 4; // Binär 0100
    public static final int HIT_LEFT = 8; // Binär 1000
    public static final int HIT_NO_BOUND = 0;
z.B. wird das hier benutzt, um den Schläger am linken Rand zu stoppen.
Java:
        hit = racket.move(bounds);
        if (Sprite.isLeftHit(hit))
            racket.setSpeed(0);// stopt Schlaeger am linken Rand
Der hier verwendete Integer ermöglicht es auch, eine Eckberührung zurückzugeben.
hit = HIT_TOP + HIT_RIGHT = 3 also Binär 0011.
Das Abfragen der Bits wird von den KlassenMethoden von Sprite erleichtert.
Java:
public static boolean isLeftHit(int hit) {
        return (hit & HIT_LEFT) != 0;
    }
Zusammengefasst moveInsideBounds() rückt den bewegten Sprite bei Randberührung zurecht und informiert den Aufrufer darüber.
 
Zuletzt bearbeitet:

Blender3D

Top Contributor
Ich habe daraus ein kleines Demo gemacht lade die Jar zum Testen herunter. Es bewegen sich dabei Bälle innerhalb eines begrenzten Spielfeldes. Diese prallen gegenseitig und an den Wänden ab. Außer am Boden gehen sie verloren. Am Boden ist ein Schläger, der nach links oder rechts bewegt wird mittels der Tasten Pfeil links und rechts.
Die Taste s startet neue Bälle.
@Jxhnny.lpz
Ich habe aus Versehen eine Version Hochgeladen ohne die Taste s.
Hier die aktuelle jar
Für des Verständnis des Codes ist es sehr wichtig zu wissen was er macht.
 

Anhänge

  • pong.jar
    9,1 KB · Aufrufe: 2

Jxhnny.lpz

Mitglied
@Jxhnny.lpz
Ich habe aus Versehen eine Version Hochgeladen ohne die Taste s.
Hier die aktuelle jar
Für des Verständnis des Codes ist es sehr wichtig zu wissen was er macht.
Ich bedanke mich für die Erklärung.
Daran sehe ich schon, dass ich noch ein langen weg vor mir habe.

Die Version ohne die S Taste ist mir auch aufgefallen, aber die Funktion war im Programm vorhanden, deswegen ist es ja nicht weiter schlimm.

;)
 

Ähnliche Java Themen

Neue Themen


Oben