- Автомобиль не имеет определенных подсказок по размеру, поэтому его размер по умолчанию равен
0x0
- При добавлении
Car
к RaceTrack
, использующему FlowLayout
, макет Car
будет соответствовать предпочтительному размеру 0x0
- Swing не является потокобезопасным, поэтому, вероятно, у вас также есть куча условий / нарушений в потоке потоков
Не уверен, что это правильный способ решить эту проблему
Не используйте компоненты для этой цели, эта проблема просто кричит о рисовании до конца.
Существует множество блогов и учебных пособий по базовой разработке игр, поэтому я не хочу тратить много времени на изучение одного и того же материала.
По сути, вам нужно определить серию «атрибутов» для объектов, которые вы хотите использовать в своей игре («сущности» АКА). Не все объекты должны быть окрашиваемыми, некоторые могут запускать другие действия или просто действовать как «маркеры» для использования другими объектами.
В этом примере я определяю две основные сущности, «подвижные» и «окрашиваемые». «Окрашиваемый» объект может быть статичным (т.е. гусеница) или «подвижным» (т. Е. Автомобиль)
Цель состоит в том, чтобы предоставить изолированную концепцию функциональности, которую можно легко применить к объектам Verity, чтобы «описать» их функциональность и назначение в игре.
Например ...
public interface MovableEntity extends Entity {
public void update(Rectangle bounds);
}
public interface PaintableEntity extends Entity {
public void paint(Graphics2D g2d, ImageObserver imageObserver, Rectangle bounds);
}
Итак, в вашем случае Car
- это и Paintable
, и Movable
.
Ваш «движок» будет вести один или несколько списков этих «сущностей» и обрабатывать их соответствующим образом.
В этом примере просто используется Swing Timer
в качестве "основного цикла"
mainLoop = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
// Lots of collision detection and other awesome stuff
for (MovableEntity entity : movableEntitys) {
entity.update(bounds);
}
repaint();
}
});
mainLoop.start();
Это обеспечивает уровень безопасности потока, так как Timer
запускается в контексте потока диспетчеризации событий, что означает, что пока мы обновляем сущности, они не могут быть нарисованы.
Затем мы просто используем JPanel
метод paintComponent
, чтобы действовать как процесс рендеринга ...
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
for (PaintableEntity paintable : paintableEntities) {
Graphics2D g2d = (Graphics2D) g.create();
paintable.paint(g2d, this, bounds);
g2d.dispose();
}
}
Это довольно широкий пример, основанный на простой демонстрации основных понятий. Существуют гораздо более сложные возможные решения, которые будут следовать тем же основным принципам.
Лично я бы определил некий «путь», который будет действовать как трек, по которому автомобили будут затем рассчитывать свои позиции на основе различных факторов, но это несколько более сложное решение, чем требуется сейчас. Но если вам действительно интересно, это может выглядеть как-то вот так
Пример запуска ...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.ImageObserver;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
/*
You could have entities which can collide which have collision detection
capabilities
Some entities don't need to be painted and may provide things like
visual or audio affects
*/
public interface Entity {
}
public interface MovableEntity extends Entity {
public void update(Rectangle bounds);
}
public interface PaintableEntity extends Entity {
public void paint(Graphics2D g2d, ImageObserver imageObserver, Rectangle bounds);
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
// Could use a single list and filter it, but hay
private List<PaintableEntity> paintableEntities;
private List<MovableEntity> movableEntitys;
private Timer mainLoop;
public GamePane() {
paintableEntities = new ArrayList<>(25);
movableEntitys = new ArrayList<>(25);
paintableEntities.add(new TrackEntity());
CarEntity car = new CarEntity();
paintableEntities.add(car);
movableEntitys.add(car);
mainLoop = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
// Lots of collision detection and other awesome stuff
for (MovableEntity entity : movableEntitys) {
entity.update(bounds);
}
repaint();
}
});
mainLoop.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
for (PaintableEntity paintable : paintableEntities) {
Graphics2D g2d = (Graphics2D) g.create();
paintable.paint(g2d, this, bounds);
g2d.dispose();
}
}
}
public class CarEntity implements PaintableEntity, MovableEntity {
private int delta = 1;
private int xDelta = 0;
private int yDelta = delta;
private int xPos = 2;
private int yPos = 2;
private int size = 4;
@Override
public void paint(Graphics2D g2d, ImageObserver imageObserver, Rectangle bounds) {
g2d.translate(bounds.x, bounds.y);
g2d.setColor(Color.RED);
g2d.fillRect(xPos - size / 2, yPos - size / 2, size, size);
}
@Override
public void update(Rectangle bounds) {
xPos += xDelta;
yPos += yDelta;
if (xPos + (size / 2) > bounds.x + bounds.width) {
xPos = bounds.x + bounds.width - (size / 2);
xDelta = 0;
yDelta = -delta;
} else if (xPos - (size / 2) < bounds.x) {
xPos = bounds.x + (size / 2);
xDelta = 0;
yDelta = delta;
}
if (yPos + (size / 2) > bounds.y + bounds.height) {
yPos = bounds.y + bounds.height - (size / 2);
xDelta = delta;
yDelta = 0;
} else if (yPos - (size / 2) < bounds.y) {
yPos = bounds.y + (size / 2);
xDelta = -delta;
yDelta = 0;
}
}
}
public class TrackEntity implements PaintableEntity {
@Override
public void paint(Graphics2D g2d, ImageObserver imageObserver, Rectangle bounds) {
g2d.translate(bounds.x, bounds.y);
g2d.setColor(Color.BLUE);
g2d.drawRect(2, 2, bounds.width - 4, bounds.height - 4);
}
}
}