#java #swing #jpanel #2d
Вопрос:
В настоящее время я работаю над 2D-игрой на Java для школы. Мы должны использовать абстрактный шаблон заводского дизайна. Для 2D-реализации я использую фабрику следующим образом:
public class Java2DFact extends AbstractFactory {
public Display display;
private Graphics g;
public Java2DFact() {
display = new Display(2000, 1200);
}
@Override
public PlayerShip getPlayership()
{
return new Java2DPlayership(display.panel);
}
В своем классе отображения я создаю JFrame и Jpanel
public class Display {
public JFrame frame;
public JPanel panel;
public int width, height;
public Display(int width, int height) {
this.width = width;
this.height = height;
frame = new JFrame();
frame.setTitle("SpaceInvaders");
frame.setSize(1200,800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel = new JPanel(){
@Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
}
};
panel.setFocusable(true);
frame.add(panel);
}
}
Теперь из моего основного gameloop я вызываю метод визуализации внутри класса Java2DPLayership, чтобы визуализировать свою игровую аудиторию
public class Java2DPlayership extends PlayerShip {
private JPanel panel;
private Graphics2D g2d;
private Image image;
private BufferStrategy bs;
public Java2DPlayership(JPanel panel) {
super();
this.panel = panel;
}
public void visualize() {
try {
image = ImageIO.read(new File("src/Bee.gif"));
Graphics2D g = (Graphics2D) bs.getDrawGraphics();
//g.setColor(new Color(0, 0, 0));
//g.fillRect(10, 10, 12, 8);
g.drawImage(image, (int) super.getMovementComponent().x, (int) super.getMovementComponent().y, null);
Toolkit.getDefaultToolkit().sync();
g.dispose();
panel.repaint();
} catch(Exception e){
System.out.println(e.toString());
}
}
}
Моя цель состоит в том, чтобы передать JPanel каждому объекту и позволить ему отобразить его содержимое на панели, прежде чем показывать его. Однако я, кажется, не могу понять, как это сделать. При использовании этого подхода, изменяя графику панели, я получаю много мерцания.
Комментарии:
1. Вы должны рисовать только из контекста
paintComponent
, который вы переопределяете в JPanel . Больше нигде. Это означает, что, войдя,paintComponent
вы можете вызвать другие методы и передать графический контекст в качестве аргумента. И сохраняйте свою логику рисования как можно короче (с точки зрения времени) каждый раз, когда вызывается перекраска, чтобы избежать задержки потока отправки событий.
Ответ №1:
Вот полностью функциональный, хотя и простой пример, который я написал некоторое время назад. У него просто куча balls
отскакивающих от боковых сторон панели. Обратите внимание, что render
метод Ball class
принимает графический контекст из paintComponent
. Если бы у меня было больше классов, которые нужно было визуализировать, я мог бы создать Renderable
интерфейс и заставить каждый класс реализовать его. Тогда у меня мог бы быть список Renderable
объектов, я мог бы просто просмотреть их и вызвать метод. Но, как я также сказал, это должно было бы произойти быстро, чтобы избежать привязки EDT.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Bounce extends JPanel {
private static final int COLOR_BOUND = 256;
private final static double INC = 1;
private final static int DIAMETER = 40;
private final static int NBALLS = 20;
private final static int DELAY = 5;
private final static int PANEL_WIDTH = 800;
private final static int PANEL_HEIGHT = 600;
private final static int LEFT_EDGE = 0;
private final static int TOP_EDGE = 0;
private JFrame frame;
private double rightEdge;
private double bottomEdge;
private List<Ball> balls = new ArrayList<>();
private Random rand = new Random();
private List<Long> times = new ArrayList<>();
private int width;
private int height;
public Bounce(int width, int height) {
this.width = width;
this.height = height;
frame = new JFrame("Bounce");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
addComponentListener(new MyComponentListener());
frame.pack();
frame.setLocationRelativeTo(null);
rightEdge = width - DIAMETER;
bottomEdge = height - DIAMETER;
for (int j = 0; j < NBALLS; j ) {
int r = rand.nextInt(COLOR_BOUND);
int g = rand.nextInt(COLOR_BOUND);
int b = rand.nextInt(COLOR_BOUND);
Ball bb = new Ball(new Color(r, g, b), DIAMETER);
balls.add(bb);
}
frame.setVisible(true);
}
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public static void main(String[] args) {
new Bounce(PANEL_WIDTH, PANEL_HEIGHT).start();
}
public void start() {
/**
* Note: Using sleep gives a better response time than
* either the Swing timer or the utility timer. For a DELAY
* of 5 msecs between updates, the sleep "wakes up" every 5
* to 6 msecs while the other two options are about every
* 15 to 16 msecs. Not certain why this is happening though
* since the other timers are run on threads.
*
*/
Timer timer = new Timer(0,(ae)-> {repaint();
for (Ball b : balls) {
b.updateDirection();
}} );
timer.setDelay(5); // 5 ms.
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball ball : balls) {
ball.render(g2d);
}
}
class MyComponentListener extends ComponentAdapter {
public void componentResized(ComponentEvent ce) {
Component comp = ce.getComponent();
rightEdge = comp.getWidth() - DIAMETER;
bottomEdge = comp.getHeight() - DIAMETER;
for (Ball b : balls) {
b.init();
}
}
}
class Ball {
private Color color;
public double x;
private double y;
private double yy;
private int ydir = 1;
private int xdir = 1;
private double slope;
private int diameter;
public Ball(Color color, int diameter) {
this.color = color;
this.diameter = diameter;
init();
}
public void init() {
// Local constants not uses outside of method
// Provides default slope and direction for ball
slope = Math.random() * .25 .50;
x = (int) (rightEdge * Math.random());
yy = (int) (bottomEdge * Math.random()) diameter;
xdir = Math.random() > .5 ? -1
: 1;
ydir = Math.random() > .5 ? -1
: 1;
y = yy;
}
public void render(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillOval((int) x, (int) y, diameter, diameter);
}
public void updateDirection() {
x = (xdir * INC);
yy = (ydir * INC);
y = yy * slope;
if (x < LEFT_EDGE || x > rightEdge) {
xdir = -xdir;
}
if (y < TOP_EDGE || y > bottomEdge) {
ydir = -ydir;
}
}
}
}
Комментарии:
1. Ты когда-нибудь пробовал это? Помогло ли это решить вашу проблему?
2. @camickr Исправлено. Спасибо!