我有動態數量的 JComponents 在 JPanel 中移動。我想讓它們在另一個 Jcomponent 阻礙時停止。我試圖對 2d 光線投射進行搜索,不幸的是我不知道如何在我的代碼中實作它。Stackoferflow 對此有一些答案,但他們正在構建對撞機,我需要更類似于光線投射的東西。
繼承人JPanel 代碼Map.java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Map extends JPanel implements ActionListener {
final int windowWidth = 1300;
final int windowHeight = 750;
Map() {
this.setPreferredSize(new Dimension(windowWidth, windowHeight));
Timer timer = new Timer(3000, this);
timer.start();
}
@Override
public void paint(Graphics g) {
super.paint(g);
// painting map
}
// Update method of the frame
@Override
public void actionPerformed(ActionEvent e) {
Car car = new Car();
this.add(car);
// For loop responds for data cleaning
for(int i=0;i<this.getComponents().length;i ) {
Car iCar = (Car) this.getComponents()[i];
if(iCar.x >= 1400 || iCar.x <= -300 || iCar.y <= -300 || iCar.y >= 950) {
this.remove(i);
}
}
System.out.println(this.getComponentCount());
this.updateUI();
}
}
繼承人 JComponent 代碼Car.java:
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
public class Car extends JComponent implements ActionListener {
private BufferedImage car;
private final GeoPoint startPoint = GeoSides.pickGeoPoint();
public int x = 0;
public int y = 0;
private int xVelocity;
private int yVelocity;
private final int imageX = 160;
private final int imageY = 120;
private double turnAngle = 0;
Timer timer;
// Initializing Car() object
Car() {
this.setPreferredSize(new Dimension(170, 130));
this.timer = new Timer(1, this); // Setting up the timer for update method
this.timer.start();
try {
Random randomize = new Random();
car = ImageIO.read(new File("src/assets/" (randomize.nextInt(10) 1) ".png")); // Getting a random image of car
} catch (IOException e) {
e.printStackTrace();
}
// Setting start position
}
// This method should be called when another car is in the way
public void stopCar() {
xVelocity = 0;
yVelocity = 0;
}
// Method scales the image and rotate it if needed
private void drawCar(Graphics2D render2D, double rotationAngle) {
//Image scaling and rotating
}
// Inherited method from JComponent
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setLocation(new Point(x, y));
Graphics2D render2D = (Graphics2D) g;
drawCar(render2D, turnAngle);
}
// Frame update of car's x, y speed and turn angle
@Override
public void actionPerformed(ActionEvent e) {
switch (startPoint){
// Setting destination points
}
x = xVelocity;
y = yVelocity;
this.repaint();
}
}
uj5u.com熱心網友回復:
- 不要打電話
updateUI,它沒有做你認為它在做的事情。要安排新的繪制程序,您應該呼叫repaint已更改的組件。 - 我個人不會以這種方式使用組件。
- 您的“主回圈”應該確保對“物體”的任何更改都不會導致沖突,如果發生沖突,則您需要采取適當的措施
this.setLocation(new Point(x, y));在您的內部呼叫paintComponent將不會導致您最終出現問題,因為Graphics背景關系0x0在paintComponent被呼叫之前實際上已被翻譯(因此這是組件在其父組件中的位置)- Swing
Timer不能很好地擴展。也就是說,擁有更多的Timers 實際上會對績效產生部門影響。最好有一個Timer可以附加多個偵聽器的單個偵聽器,或者更好的是,Timer單個偵聽器充當“主回圈”
“主回圈”是什么意思?
“主回圈”是一個常用術語(尤其是在游戲中,但 Swing 將其稱為“事件調度執行緒”或“主事件回圈”)。就您而言,這將是您班級中的Timer's 。ActionListenerMap
這應該定期呼叫,它負責根據需要更新物體,執行碰撞檢測,以任何其他有意義的方式更新狀態并安排重繪。
例子
下面的例子簡化了這個想法。它使用單個“主回圈”負責更新“物體”位置、執行邊界和碰撞檢測以及調度重繪。
請注意,這是一個明顯簡化的示例。我個人會制作Entity一個interface然后制作一個Car實作的專用類,但這只是為了演示核心概念
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
private Timer ticker;
private List<Entity> entities;
public MainPane() {
entities = new ArrayList<>(32);
// For demonstration purposes
int y = (200 - 20) / 2;
entities.add(new Entity(0, y, 1, 0, Color.RED));
entities.add(new Entity(400 - 20, y, -1, 0, Color.BLUE));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
@Override
public void addNotify() {
super.addNotify();
if (ticker != null) {
ticker.stop();
ticker = null;
}
ticker = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tickDidOccur();
}
});
ticker.start();
}
@Override
public void removeNotify() {
super.removeNotify();
if (ticker != null) {
ticker.stop();
ticker = null;
}
}
// It's more efficent then creating a local variable each time
private List<Entity> entitiesToBeRemoved = new ArrayList<>(32);
protected void tickDidOccur() {
entitiesToBeRemoved.clear();
// So some choices. You can either update ALL the enties and then
// do the collision detection, or, you can do the collision detection
// after each enity is been updated.
Rectangle viewBounds = getBounds();
for (Entity entity : entities) {
Rectangle bounds = entity.peekNextPosition();
if (!viewBounds.intersects(bounds)) {
entitiesToBeRemoved.add(entity);
} else {
boolean conflict = false;
for (Entity other : entities) {
if (other == entity) {
continue;
}
// Please note, depending on the amount of change
// it's possible that the enities may be positioned
// further apart. You will need to access this based
// on your needs.
// Bascially what this is going to do, is check to see
// if the next update can be perform or not, if there
// is a conflict, the entities are stopped and the
// current entities update is discarded
if (bounds.intersects(other.bounds)) {
other.stop();
entity.stop();
conflict = true;
}
}
if (!conflict) {
// Commit the next position
entity.update();
}
}
// This is more of a "long winded" bounds check, but as you
// can see, we can simply make use of the functionality
// Rectangle provides to do the same thing
//if (bounds.x bounds.width < 0 || bounds.y bounds.height < 0) {
// entitiesToBeRemoved.add(entity);
//} else if (bounds.x > getWidth() || bounds.y > getHeight()) {
// entitiesToBeRemoved.add(entity);
//}
}
entities.removeAll(entitiesToBeRemoved);
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Entity entity : entities) {
// Because I trust no one
Graphics2D g2d = (Graphics2D) g.create();
entity.paint(g2d);
g2d.dispose();
}
}
}
public class Entity {
private Rectangle bounds;
private Point velocity;
private Color color;
public Entity(int x, int y, int xDelta, int yDelta, Color color) {
bounds = new Rectangle(x, y, 20, 20);
velocity = new Point(xDelta, yDelta);
this.color = color;
}
public Rectangle getBounds() {
return bounds;
}
public Color getColor() {
return color;
}
public void stop() {
velocity = new Point(0, 0);
}
public void start(int xDelta, int yDelta) {
velocity = new Point(0, 0);
}
public Rectangle peekNextPosition() {
Rectangle next = new Rectangle(bounds);
int x = next.x velocity.x;
int y = next.y velocity.y;
next.setLocation(x, y);
return next;
}
public Rectangle update() {
int x = bounds.x velocity.x;
int y = bounds.y velocity.y;
bounds.setLocation(x, y);
return bounds;
}
public void paint(Graphics2D g) {
Graphics2D g2d = (Graphics2D) g.create();
// Now what ever we do wont' have a cascading effect on what
// ever comes next. This is really helpful for when you need
// to apply AffineTransformation
g2d.setColor(getColor());
g2d.fill(bounds);
g2d.dispose();
}
}
}
為什么不使用基于“組件”的物體?
主要原因是,它們很“重”。它們有很多核心功能圍繞著它們,旨在完成其他作業,它們只是不適合這種作業。
Components already have a lot of location/size operations associated with them, which is generally managed by the layout management API, so you could (all too easily) end up fighting that.
Components are, generally, not well suited to this kind of work.
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/352740.html
