Wenn ich repaint(); mache, flackert es so stark, das man fast nichts erkennen kann.

DarkShadows

Mitglied
Hey Leute, also ich versuche das Spiel Ping Pong zu programmieren, wobei ich das Problem habe, dass wenn ich repaint(); mache, der Bildschirm ziemlich schnell gelöscht und wieder neu gezeichnet wird und ich daher fast nichts erkennen kann. Wie kann ich dieses Problem am einfachsten lösen?
 
Das flackern kann verschiedene Gründe haben. Ohne zu wissen was du genau gemacht hast kann man nur raten. Poste am Besten mal den relevanten Code.
Welches Framework nutzt du? AWT/Swing/FX?
 
Die ganzen Variablen (z.b. Variablen.playerpoints) sind statische Werte in einer anderen Klasse.


package pack1;

import java.awt.*;
import java.util.Timer;
import java.util.TimerTask;

@SuppressWarnings("serial")
public class Draw extends Component{
Timer aktualisieren;
/**
* Zeichnet die Inhalte des Frames
* (Schläger, Hintergrund, Mittellinie, Ball und die Schrift))
*/
public void paint(Graphics g) {
super.paint(g);
int x= this.getHeight();
int y= this.getWidth();
g.setColor(Color.white);
g.fillRect(0,0,y,x);
g.setColor(Color.black);
for(int i=0; i<=x; i++) {
g.fillRect(y/2, i*15, 8, 8);
}
g.fillRect(20, Variablen.j, 25, 120);
g.fillRect(this.getWidth()-40, Variablen.b, 25, 120);
g.setFont(Variablen.pixelfont);
g.drawString("" + Variablen.playerpoints, y/2-90, (int) (x-x/1.12));
g.drawString("" + Variablen.gegnerpoints, y/2+55, (int) (x-x/1.12));
g.fillOval(Variablen.ballx , Variablen.bally, 30, 30);

aktualisieren = new Timer();
aktualisieren.scheduleAtFixedRate(new TimerTask() {

@Override
public void run() {
if(true)
repaint();

}

}, 0, 200);
}


}
 
Also Du machst 3 grundlegende Fehler.

1) niemals paint() überschreiben sondern paintComponent() weil paintComponent von Swing zum richtigen Zeitpunkt aufgerufen wird.
2) Die grafischen Ausgaben nicht einzeln im Graphics Objekt machen, sondern alle in ein Image. Das fertig Image dann am Graphics Objekt zeichnen.
3) Dein Thread versucht pausenlos neu zu zeichnen.
public void run() {
if(true)
repaint();
}

Hier ein Beispiel wie Du einen Gameloop realisieren könntest.
Zu Punkt 3) müsste man hier noch einen Ausgleich für mögliche verlorenen Frames einbauen. Es sollte so aber für Dich funktionieren.😉
Java:
import javax.swing.JFrame;
public class start {
    public static void main(String[] args) {
        JFrame frame = new JFrame("PONG");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GamePanel pong = new GamePanel(400, 400);
        frame.add(pong);
        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);
        frame.addKeyListener(pong);
        pong.startGame();
    }
}
Java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import game.Racket.DIRECTION;

@SuppressWarnings("serial")
public class GamePanel extends JPanel implements Runnable, KeyListener {
    private final GraphicsConfiguration gfxConf = GraphicsEnvironment.getLocalGraphicsEnvironment()
            .getDefaultScreenDevice().getDefaultConfiguration(); // used to create image for drawing
    private BufferedImage imageBuffer = null; // used to draw graphics
    private boolean running = false; // @see running
    private Thread animator = null; // thread for game ticks
    RacketHorizontal racket = new RacketHorizontal(70, 20);

    public GamePanel(int width, int height) {
        setPreferredSize(new Dimension(width, height));
        racket.setColor(Color.BLUE);
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_LEFT)
            racket.setDirection(DIRECTION.LEFT);
        if (key == KeyEvent.VK_RIGHT)
            racket.setDirection(DIRECTION.RIGHT);
    }

    @Override
    public void keyReleased(KeyEvent e) {
        racket.setDirection(DIRECTION.NO_MOVE);
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (imageBuffer == null || imageBuffer.getWidth() != getWidth() || imageBuffer.getHeight() != getHeight()) {
            imageBuffer = gfxConf.createCompatibleImage(getWidth(), getHeight());
        }
        if (imageBuffer != null)
            g.drawImage(imageBuffer, 0, 0, this);
    }

    @Override
    public void run() {
        while (running) { // false -> stops game
            try {
                updatePosition(); // update objects positions
                updateGraphics(); // draw graphics
                Thread.sleep(5); // 1000 / 5 update game every 200 millisecond
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void startGame() {
        stopGame();
        while (animator != null && animator.isAlive())
            ;
        animator = new Thread(this);
        running = true;
        animator.start();
    }

    public void stopGame() {
        running = false;
    }

    /**
     * do graphic manipulations here
     */
    public void updateGraphics() {
        if (imageBuffer != null) {
            Graphics g = imageBuffer.createGraphics();
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, getWidth(), getHeight());
            racket.draw(g);
        }
        repaint();
    }

    /**
     * do position manipulations here
     */
    private void updatePosition() {
        racket.move();
    }

}

Java:
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;

public abstract class Racket {
    public enum DIRECTION {
        LEFT, RIGHT, NO_MOVE
    }
    protected final int size;
    protected final int thickness;
    private float speed = 1;
    protected DIRECTION direction = DIRECTION.NO_MOVE;
    protected float x = 0;
    protected float y = 0;
    protected Color color = Color.WHITE;

    public Racket(int size, int thickness) {
        this.size = size;
        this.thickness = thickness;
    };

    public static int directionToSign(DIRECTION direction) {
        switch (direction) {
        case LEFT:
            return -1;
        case RIGHT:
            return 1;
        case NO_MOVE:
        }
        return 0;
    }

    public abstract void draw(Graphics g);

    public abstract Rectangle getCollisionRect();

    public Point getPosition() {
        return new Point((int) x, (int) y);
    }

    public float getSpeed() {
        return speed;
    }

    public abstract void move();

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

    public void setDirection(DIRECTION direction) {
        this.direction = direction;
    }

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

    public void setSpeed(float speed) {
        this.speed = speed;
    }
}
Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

public class RacketHorizontal extends Racket {
    public RacketHorizontal(int size, int thickness) {
        super(size, thickness);
    }

    @Override
    public void draw(Graphics g) {
        Color oldColor = g.getColor();
        g.setColor(color);
        Rectangle r = getCollisionRect();
        g.fillRect(r.x, r.y, r.width, r.height);
        g.setColor(oldColor);
    }

    @Override
    public Rectangle getCollisionRect() {
        return new Rectangle((int) x, (int) y, size, thickness);
    }

    @Override
    public void move() {
        if (direction.equals(DIRECTION.NO_MOVE))
            return;
        x += getSpeed() * Racket.directionToSign(direction);
    }

}

🙂
 
Was ist bei Punkt 2 so schlimm daran?
Es ist ein wichtiges Prinzip für flimmerfreie Darstellung:
Berechne die Änderungen in deiner Szenen --> zeichne die einzelnen grafischen Ausgaben in ein Buffer ( z.B. Image) --> erst nachdem alles gemacht ist gib das neue fertige Bild an die darstellende Instanz weiter.

Warum? ( eine vereinfachte Darstellung )
Wenn die darstellend Instanz dein Bild selbst zeichnen und berechnen muss --> sie brauch länger dafür. -->
Flackern. Andernfalls: Deine Berechnungen und Zeichnung brauchen auch länger aber die darstellende Instanz stellt das vorherige Bild dar ohne Flackern.
😉

PS. Teste meinen Code und versuche ihn zu verstehen.
 
Zuletzt bearbeitet:

Zurück
Oben