Im writing a simple game in Java. Here is main code:
public class MainPanel extends JPanel {
private Player player = new Player(100, 100, 3, 3);
private Point2D targetPoint = new Point2D.Float(130, 350); //Pos on begin
private ArrayList<Beam> beams = new ArrayList<Beam>();
public MainPanel() {
setPreferredSize(new Dimension(300, 400));
addMouseMotionListener(new MouseMotionHandler());
//Add shortcuts
makeShortcut("player.BM1", "F1", new SetBeamModeAction(1));
makeShortcut("player.BM2", "F2", new SetBeamModeAction(2));
//Start threads
Thread t = new Thread(new PlayerMoveRunnable());
t.start();
Thread t2 = new Thread(new PlayerShootRunnable());
t2.start();
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, 300, 400);
//Draw player
g2.drawImage(player.getImage(), (int)player.getX(), (int)player.getY(), null);
//Draw beams
for (Beam beam : beams) {
g2.drawImage(beam.getImage(), (int)beam.getX(), (int)beam.getY(), null);
}
}
//Thread running all the time
private class PlayerMoveRunnable implements Runnable {
public void run() {
try {
while (true) {
player.moveToPoint(targetPoint);
repaint();
Thread.sleep(15);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//Thread working all the time
private class PlayerShootRunnable implements Runnable {
public void run() {
try {
while (true) {
//Choose which beam to shoot (depends on set mode)
Thread t;
switch (player.getBeamMode()) {
case 1:
t = new Thread(new BeamMoveRunnable(new Beam1(player.getX()+18, player.getY(), 0, -15)));
break;
case 2:
t = new Thread(new BeamMoveRunnable(new Beam2(player.getX()+18, player.getY(), 0, -30)));
break;
default:
t = null;
break;
}
t.start();
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private class BeamMoveRunnable implements Runnable {
private Beam beam;
public BeamMoveRunnable(Beam beam) {
this.beam = beam;
}
public void run() {
Beam beam = this.beam;
beams.add(beam);
try {
while (true) {
if (beam.getY() <= 0) {
beams.remove(beam);
break;
}
beam.move();
repaint();
Thread.sleep(20);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
[its not whole code. I cut few lines which for sure arent causing problem]
Im getting such error:
Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
at java.util.ArrayList$Itr.next(ArrayList.java:791)
at spacecommander.MainPanel.paintComponent(MainPanel.java:53)
at javax.swing.JComponent.paint(JComponent.java:1054)
and so on...
Where lies the problem? I know what ConcurrentModificationException means but I dont know where can be the problem here.. Maybe I should make some synchronization. If yes please show where
You are modifying the list of beams in another thread e.g. BeamMoveRunnable while you are iterating over the collection.
Creating a thread per object is pretty wasteful and difficult to manage.
I suggest you have a one thread which periodically calls a method on each beam to move. I suggest you not add or remove the beams while they are being drawn so you won't have to synchronize access to the collection.
Before iterating the beams in paintComponent create a copy of the collection and use the copy to iterate.
You are modifying the array list beams in the thread BeamMoveRunnable (the thread which wraps this Runnable object) simultaneously with iterating it in the method paintComponent. This is the cause for the exception.
Related
I'm newbie and I'm trying to make a game but than I don't understand how to use repaint() from different paint with different threads too. One thread with thread.sleep and the other one doesn't have.
Here's my piece of code :
GamePanel :
public class GamePanel extends JPanel implements MouseListener, MouseMotionListener{
EnemyEngine enemyE = new EnemyEngine();
public GamePanel() {
new Thread(new Runnable() {
#Override
public void run() {
while(true){
repaint();
}
}
}).start();
new Thread(new Runnable() {
#Override
public void run() {
while(true){
enemyE.update();
enemyE.repaint();
try {
Thread.sleep(1000/10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void paintComponent(Graphics g) {
// board painting
}
}
EnemyEngine:
public class EnemyEngine extends JPanel{
Vector<Enemy> enemyVect = new Vector<>();
Random rand = new Random();
public void paintComponent(Graphics g){
for (Enemy enemy : enemyVect) {
enemy.render(g);
}
}
public void update() {
for (Enemy enemy : enemyVect) {
enemy.move();
}
}
}
I have already search on the internet but it still didn't work...or maybe I'm the stupid one :/
Please help me senpai
I don't understand clearly your issue about repaint?
If you want to refresh ui immediately, why don't you use paintImmediately(0, 0, getWidth(), getHeight());
I am trying to render a loading animation on top of a nattable. I use the OverLayPainter mechanism to draw a "glasspane" and some text on top of the table, and this works perfect:
public class MessageOverlay
implements IOverlayPainter {
....
#Override
public void paintOverlay(final GC gc, final ILayer layer) {
this.currentGC = gc;
this.currentLayer = layer;
if (visible) {
currentGC.setAlpha(200);
currentGC.fillRectangle(0, 0, currentLayer.getWidth(), currentLayer
.getHeight());
drawMessage();
if (withLoadingAnimation) {
showAnimation = true;
}
} else {
showAnimation = false;
}
}
}
However, the paintOverlay method is not called regularely but rather everytime the table changes.
To be able to display a smoothe animation, I added a new thread
final Thread animatorThread = new Thread(new Runnable() {
#Override
public void run() {
while (!Thread.interrupted()) {
try {
Thread.sleep(1000 / fps);
} catch (final InterruptedException e) {
break;
}
display.asyncExec(new Runnable() {
#Override
public void run() {
if (showAnimation && !currentGC.isDisposed()) {
final Image currentImage = getNextImage();
final int posX = currentGC.getClipping().width / 2
- currentImage.getBounds().width;
final int posY = currentGC.getClipping().height / 2
- currentImage.getBounds().height;
currentGC.drawImage(currentImage, posX, posY);
}
}
});
}
}
});
animatorThread.start();
As you can see it tries to access the graphics context this.currentGC set in the paintOverlay method. My problem is, that currentGC within the animatorThread is always disposed.
How can I a.) ensure that the context is not disposed in the thread or b.) solve this in an alternative way?
Thanks for the help.
You could try to create a new GC with the current NatTable instance and if needed pass the config from the passed in GC instance. Then you are in charge of disposing the GC instance and should not have the risk of a disposed GC outside your thread.
A simple example could look like the following snippet that simply shows the pane for 1000ms and then removes it again. You need of course to change the logic to be more dynamic with regards to your loading operation then:
AtomicBoolean paneThreadStarted = new AtomicBoolean(false);
...
natTable.addOverlayPainter(new IOverlayPainter() {
#Override
public void paintOverlay(GC gc, ILayer layer) {
if (this.paneThreadStarted.compareAndSet(false, true)) {
Display.getDefault().asyncExec(new Runnable() {
#Override
public void run() {
GC currentGC = new GC(natTable);
currentGC.setForeground(GUIHelper.COLOR_WHITE);
currentGC.setBackground(GUIHelper.COLOR_BLACK);
currentGC.setAlpha(200);
currentGC.fillRectangle(0, 0, layer.getWidth(), layer.getHeight());
String load = "Loading data ...";
Point textExtent = currentGC.textExtent(load);
currentGC.drawText(load,
layer.getWidth() / 2 - textExtent.x / 2,
layer.getHeight() / 2 - textExtent.y / 2,
true);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
currentGC.dispose();
natTable.redraw();
}
});
}
}
});
This way you are able to show the pane again by changing the AtomicBoolean from the outside:
Button showPaneButton = new Button(buttonPanel, SWT.PUSH);
showPaneButton.setText("Show Pane");
showPaneButton.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
this.paneThreadStarted.set(false);
natTable.redraw();
}
});
I am trying to create a loading state in the beginning of the game with the company information while it loads some files. The problem is when i use sleep on it. It will not draw the background. If i use wait it returns an error because it needs an objeft. Is there an easy way to do this? Draw a background using a sprite batch and then wait before pushing a new state? thanx
#Override
public void render(SpriteBatch sb) {
sb.begin();
Object obj = new Object();
sb.draw(bk, 0, 0, 56, 56);
try {
TimeUnit.SECONDS.sleep(5);
gsm.push(new MenuState(gsm));
dispose();
} catch (Exception e) {
System.out.println("error");
}
sb.end();
I have also tried this:
TimeUnit.SECONDS.wait(obj, 5);
TimeUnit.SECONDS.timedwait(5);
wait();
Thread.Sleep();
//all in try catch block
I have also tried to seperate into its own thread and failed. There has to be an easy way to do this no?
public void timer(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
System.out.println("error");
}
}
#Override
protected void handleInput() {
if (load)
{
timer();
gsm.push(new MenuState(gsm));
dispose();
}
}
#Override
public void update(float dt) {
handleInput();
load = true;
}
Java.problem is when i start addding new element repaint stops every time for a moment while timer is working and elements are moving finaly when it repaints elements just jump like thay have been moving all this time.also there is throwing strange exeption. some code:
public void paintComponent(Graphics g)
{
g = (Graphics2D) g;
g.drawImage(img1, 0, 0,null);
Iterator<zombie> it = zombies.iterator();
while(it.hasNext())
{
if(it.next().heals <= 0)
{
it.remove();
}
else{
g.drawImage(it.next().img ,it.next().x , it.next().y , null);
}
}
}
public void actionPerformed(ActionEvent e)
{
Iterator<zombie> it = zombies.iterator();
while(it.hasNext())
{
it.next().move();
}
repaint();
}
that is new thread that generates new items:
public void run()
{
Random rd = new Random();
while(true)
{
try {
Thread.sleep(1000 + rd.nextInt(2000));
if(rd.nextInt(4) == 1)
{
zombies.add(new ZombieLow(1000,rd.nextInt(500),1,20,300));
}
else
{
zombies.add(new ZombieHard(1000,rd.nextInt(500),1,15,700));
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
what is that problem with? if you need more info just say i will give it.
try to put the repaint() method in the while loop. In your case you repaint your Zombie after moving all.
another thing if you use Thread.sleep() in the move method you should make every Zombie in his own Thread.
Hope that helps, Salam =)
What im trying to do is pretty simple, I want to show the steps of an algorithm on the screen, hence why im trying to combine repaint() with sleep(), but I am doing it wrong, Id love it if someone knows enough about it to firstly explain whats wrong with this code, and secondly, what do i do to make it work...
thanks!
in summery, what this code was meant to do is paint 10 red vertices, then balcken em one by one in intervals of 200 milliseconds.
here's the code:
public class Tester {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ShowGUIGraph();
}
});
}
private static void ShowGUIGraph() {
JFrame f = new JFrame("something");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p=new JPanel();
p.setLayout(new BorderLayout());
p.add(BorderLayout.CENTER,new SomePanel());
f.add(p);
f.setPreferredSize(new Dimension(800,600));
f.pack();
f.setVisible(true);
}
}
public class SomePanel extends JPanel {
private static final long serialVersionUID = 1L;
LinkedList<Vertex> vertices=new LinkedList<Vertex>();
public SomePanel () {
for (int i=0;i<10;i++) {
Vertex v=new Vertex(i);
v.setLocation(20+30*i, 20+30*i);
vertices.add(v);
}
traverseVerticesRecoursive(0);
traverseVerticesNonRecoursive();
}
public void traverseVerticesRecoursive(int i) {
if (i>=vertices.size()) return;
vertices.get(i).setColor(Color.black);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
repaint();
traverseVerticesRecoursive(i+1);
}
public void traverseVerticesNonRecoursive() {
for (int i=0;i<10;i++) {
vertices.get(i).setColor(Color.red);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
repaint();
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i=0;i<vertices.size();i++) {
vertices.get(i).paintVertex(g);
}
}
}
public class Vertex {
private int x,y,tag,r=20;
private Color color=Color.red;
Vertex (int i) {
tag=i;
}
public void setLocation(int x0,int y0) {
x=x0;
y=y0;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setColor(Color c) {
color=c;
}
public boolean colorIs(Color c) {
return (color.equals(c));
}
public void paintVertex(Graphics g) {
g.setColor(color);
g.fillOval(x,y,r,r);
g.setColor(Color.BLACK);
g.drawOval(x,y,r,r);
g.drawString(""+tag, x+r/2, y+r/2+4);
}
public int getR() {
return r;
}
}
Do not sleep in the Event Dispatch Thread; this will cause the GUI to freeze. For animation, use an EDT-friendly utility class, such as javax.swing.Timer.
Just a few ideas that might make your code cleaner:
In your SomePanel class, put the traversing code in a method out of the constructor. Constructors are intended for initializing fields.
First launch your static GUI, then spawn a worker thread to do the updates via the previous method (this would be your small "engine"). In this thread is were you can call sleep.
In your traverseVerticesRecoursive method, do only the repaint on the UI thread, and the status update on your worker thread.
Tha main modification you should do is not to block the GUI thread with sleep calls, as they have told you in the first answer.
Thread.sleep is a long running task. When you a running such a task in the EDT it blocks all repaint requests from being executed. All repaint requests which are pending and which were sent during the sleep phase are queued for future processing.
As a result when the EDT comes out of the sleep phase it coalesce all such repaint request (if coalescing is enabled which is the default property) into a single repaint which gets executed. If coalescing is not enabled then all queued request are executed serially without any time gap in between. As a result it seems that the UI did not update.
To correct the situation use a timer which triggers periodically after specific intervals of time.
Guy, you could use a new Thread differ with EDT thread to make an animation. For example,
void play() {
Thread thread = new Thread() {
#Override
public void run() {
game();
}
};
thread.start();
}
void game() {
for (; ; ) {
switch (state) {
case GameData.ANIMATING:
// call some function as repaint() to update GUI
break;
case GameData.GAME_ENDED:
return;
default:
break;
}
diffTime = System.currentTimeMillis() - beforeTime;
sleepTime = delay - diffTime;
sleepTime = (sleepTime < 0) ? 0 : sleepTime;
Thread.sleep(sleepTime);
}
}