我需要在java
swing
迷你游戏中计算2个椭圆的碰撞。
我有一个JPanel
来吸引ArrayList
个球Thread
和我的球员。在我的非玩家球的run()
方法中,我检查了球员球和球Thread
ArrayList
之间的碰撞。
问题是我的碰撞方法永远不会执行。它甚至没有达到我的碰撞if
声明。永远不要打电话给方法。
Ball.java :
public class Ball extends Thread
{
int x;
int y;
int velocity = 1;
public int radius = 20;
public boolean directionX = true; // True - right, false - left
public boolean directionY = false; // True - up, false - down
public static final int Y_STARTING_POSITION = 0;
public static final int X_STARTING_POSITION = 0;
public Ball()
{
switch (this.getStartingSide())
{
case 0: // Left
{
this.directionX = true;
this.directionY = true;
this.x = this.getRandomHeight();
this.y = this.getRandomWidth();
break;
}
case 1: // Right
{
this.directionX = false;
this.directionY = false;
this.x = this.getRandomHeight();
this.y = this.getRandomWidth();
break;
}
case 2: // Top
{
this.directionX = true;
this.directionY = false;
this.x = this.getRandomWidth();
this.y = this.getRandomHeight();
break;
}
case 3: // Bottom
{
this.directionX = false;
this.directionY = true;
this.x = this.getRandomWidth();
this.y = this.getRandomHeight();
break;
}
}
}
public int getX()
{
return this.x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return this.y;
}
public void setY(int y)
{
this.y = y;
}
public void move()
{
if (this.directionX) // Right
{
this.x += this.velocity;
}
else // Left
{
this.x -= this.velocity;
}
if (this.directionY) // Up
{
this.y -= this.velocity;
}
else // Down
{
this.y += this.velocity;
}
}
@Override
public void run()
{
try
{
this.isCollision(); // Never called
Thread.sleep(20);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
/**
* Get a random number varies from 0 to the screen width.
*
* @return The random number.
*
*/
public int getRandomWidth()
{
Random random = new Random();
return random.nextInt((Program.getPanelWidth() - 0) + 1) + 0; // Minimum = 0,maximum = Program.panelWidth
}
/**
* Get a random number varies from 0 to the screen height.
*
* @return The random number.
*
*/
public int getRandomHeight()
{
Random random = new Random();
return random.nextInt((Program.getPanelHeight() - 0) + 1) + 0; // Minimum = 0,maximum = Program.panelHeight
}
/**
* Get the starting side of a ball.
*
* Left - 0.
* Right - 1.
* Top - 2.
* Bottom - 3.
*
* @return
*
*/
public int getStartingSide()
{
Random random = new Random();
return random.nextInt((4 - 0) + 1) + 0; // Minimum = 0,maximum = 3
}
public void isCollision()
{
System.out.println("SSSSSSSSSSSSSSSS");
if (Math.sqrt(Math.pow(MyPanel.playerX + MyPanel.playerRadius - this.getX() + this.radius,2)
+ Math.pow(MyPanel.playerY + MyPanel.playerRadius - this.getY() + this.radius,2))
<= MyPanel.playerRadius + this.radius) // A collision
{
System.exit(0);
}
}
} // End of Ball class
MyPanel.java :
public class MyPanel extends JPanel implements KeyListener
{
private static final long serialVersionUID = 1L;
private static final Color BACKGROUND_COLOR = Color.WHITE;
private static final Color NPC_BALLS_COLOR = Color.RED;
// The player is an oval
public static int playerRadius = 35;
public static int playerX;
public static int playerY;
// True - first player position, false - otherwise
private boolean playerPosition = true;
// Array of all the balls threads
public static ArrayList<Ball> balls = new ArrayList<Ball>();
public MyPanel()
{
this.setBackground(MyPanel.BACKGROUND_COLOR);
this.setFocusable(true);
this.addKeyListener(this);
new Timer(10,new UpdateUI());
}
// Drawing
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final double PANEL_WIDTH = this.getWidth();
final double PANEL_HEIGHT = this.getHeight();
if (this.playerPosition)
{
MyPanel.playerX = (int)(PANEL_WIDTH / 2 - MyPanel.playerRadius);
MyPanel.playerY = (int)(PANEL_HEIGHT / 2 - MyPanel.playerRadius);
this.playerPosition = false;
}
// Drawing the player
g.setColor(Color.BLACK);
g.fillOval(playerX,playerY, MyPanel.playerRadius * 2, MyPanel.playerRadius * 2);
// Drawing npc's balls
g.setColor(MyPanel.NPC_BALLS_COLOR);
for (Ball ball: MyPanel.balls) // ConcurrentModificationException
{
if (ball.isAlive())
{
ball.start();
}
ball.move();
repaint();
g.fillOval(ball.getX(), ball.getY(),
ball.radius * 2, ball.radius * 2);
}
}
// Keyboard listeners
@Override
public void keyPressed(KeyEvent e)
{
switch (e.getKeyCode())
{
case KeyEvent.VK_W: // Up
{
MyPanel.playerY -= 10;
repaint();
break;
}
case KeyEvent.VK_S: // Down
{
MyPanel.playerY += 10;
repaint();
break;
}
case KeyEvent.VK_D: // Right
{
MyPanel.playerX += 10;
repaint();
break;
}
case KeyEvent.VK_A: // Left
{
MyPanel.playerX -= 10;
repaint();
break;
}
}
}
@Override
public void keyReleased(KeyEvent e)
{
}
@Override
public void keyTyped(KeyEvent e)
{
}
public class UpdateUI implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
repaint();
}
}
} // End of MyPanel class
Program.java :
public class Program
{
private static int panelWidth;
private static int panelHeight;
public static void main(String[] args)
{
MyFrame frame = new MyFrame();
Program.panelWidth = frame.getWidth();
Program.panelHeight = frame.getHeight();
// Generate a ball each 2 seconds
while (true)
{
try
{
MyPanel.balls.add(new Ball());
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
} // End of main method
public static int getPanelWidth()
{
return Program.panelWidth;
}
public static int getPanelHeight()
{
return Program.panelHeight;
}
}
JFrame
并不特别,只需将JPanel
添加到其中即可。
因此我的isCollision()
方法永远不会被调用,即使它位于我run()
的{{1}}方法中。怎么样?
答案 0 :(得分:1)
您需要在repaint
方法中调用while循环中的main
方法,以便调用run()
。你可以这样做:
panel.repaint():
此外,您必须调用run
方法。你可以在球或面板中进行。
答案 1 :(得分:1)
ball.isAlive()
总是返回false,因为你的线程还没有启动。如果要在paintComponent()方法中启动线程,则不应使用此方法。
public class MyPanel extends JPanel implements KeyListener
{
private static final long serialVersionUID = 1L;
private static final Color BACKGROUND_COLOR = Color.WHITE;
private static final Color NPC_BALLS_COLOR = Color.RED;
// The player is an oval
public static int playerRadius = 35;
public static int playerX;
public static int playerY;
// True - first player position, false - otherwise
private boolean playerPosition = true;
private boolean ballsStarted;
// Array of all the balls threads
public static ArrayList<Ball> balls = new ArrayList<Ball>();
public MyPanel()
{
this.setBackground(MyPanel.BACKGROUND_COLOR);
this.setFocusable(true);
this.addKeyListener(this);
new Timer(10,new UpdateUI());
}
// Drawing
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final double PANEL_WIDTH = this.getWidth();
final double PANEL_HEIGHT = this.getHeight();
if (this.playerPosition)
{
MyPanel.playerX = (int)(PANEL_WIDTH / 2 - MyPanel.playerRadius);
MyPanel.playerY = (int)(PANEL_HEIGHT / 2 - MyPanel.playerRadius);
this.playerPosition = false;
}
// Drawing the player
g.setColor(Color.BLACK);
g.fillOval(playerX,playerY, MyPanel.playerRadius * 2, MyPanel.playerRadius * 2);
// Drawing npc's balls
g.setColor(MyPanel.NPC_BALLS_COLOR);
for (Ball ball: MyPanel.balls) // ConcurrentModificationException
{
if (!ballsStarted)
{
ball.start();
}
ball.move();
repaint();
g.fillOval(ball.getX(), ball.getY(),
ball.radius * 2, ball.radius * 2);
}
ballsStarted = true;
}
// Keyboard listeners
@Override
public void keyPressed(KeyEvent e)
{
switch (e.getKeyCode())
{
case KeyEvent.VK_W: // Up
{
MyPanel.playerY -= 10;
repaint();
break;
}
case KeyEvent.VK_S: // Down
{
MyPanel.playerY += 10;
repaint();
break;
}
case KeyEvent.VK_D: // Right
{
MyPanel.playerX += 10;
repaint();
break;
}
case KeyEvent.VK_A: // Left
{
MyPanel.playerX -= 10;
repaint();
break;
}
}
}
@Override
public void keyReleased(KeyEvent e)
{
}
@Override
public void keyTyped(KeyEvent e)
{
}
public class UpdateUI implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
repaint();
}
}
}
答案 2 :(得分:1)
您从未启动过Ball
课程实施的主题。
ConcurrentModificationException
是因为您正在向ArrayList
添加球,如果您不在外部同步,则本质上是线程不安全的数据结构。在这里,您在迭代EDT(事件调度线程)中的列表时添加一个线程。要么在列表上同步,要么使用线程安全的数据结构(对于迭代,您仍然可能需要锁定完整列表)。
查看Collections.synchronizedList或CopyOnWriteArrayList(后者不需要同步进行迭代)。
然而,每次插入和每次迭代的同步对我来说似乎效率很低,特别是因为你的目标是需要快速渲染和后台处理的游戏。但是,对于基本游戏来说可能就足够了。
作为旁注:更好的游戏技术是使用双缓冲:在后台线程中渲染游戏图形,例如:在BufferedImage上,完成后,切换两个缓冲区,使您刚绘制的缓冲区现在显示在屏幕上,另一个缓冲区可以再次用于绘制下一帧。您可能需要帧之间的同步检查点。但这稍微高级一些,所以如果你刚刚开始使用Java,我就不会花太多时间来处理它并保持简单。