Java - Different paint with multiple threads that have thread.sleep - java

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());

Related

KeyListener doesn't work

I have the following two classes, the main-class (SamG) and the Panll-class. I have implemented the KeyListener in the Panll-class, but it doesn't seem to work.
public class SamG {
public static void main(String[] args) {
JFrame jf = new JFrame("My APP");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(800, 400);
jf.setVisible(true);
jf.pack();
jf.setContentPane(new Panll());
}
}
public class Panll extends JPanel implements KeyListener {
int x=100,y=100;
boolean run=true;
Panll() {
addKeyListener(this);
}
#Override
public void paint (Graphics g) {
super.repaint();
g.clearRect(0, 0, 800, 400);
update();
draw(g);
try {
Thread.sleep(17);
} catch (InterruptedException ex) {
Logger.getLogger(Panll.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void update(){
x++;
y++;
}
public void draw(Graphics g){
g.drawOval(x, y, 100, 100);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println(e.getKeyChar());
}
#Override
public void keyReleased(KeyEvent e) {
}
}
you can't focus a JPanel without explicit allowing it with setFocusable(true);. if you add this line you can take you focus on the JPanel. if you then press any button the KeyListener works just fine
First never send main thread to sleep. Create a new Thread that periodically calls a repaint of your panel.
Second don't use paint() method to draw your stuff. Use the paintComponent() method instead.
Third add your KeyListener to your JFrame. Your JPanel won't receive the KeyEvents because it never gets the Focus.

How to change Background color of a JButton from a derived class in Java Swing

I have a base class mainframe and i have keeping the JButton as final static Which its BGcolor going to be changed by a extended class of mainframe namely dataframe. Initially i need to set the BGColor of the JButton to red. Then I need to change it to some other colors from the dataframe. I can able to set the BGColor from the mainframe but not from the dataframe(extended class). I've used mainframe.Button_name.setBackground(color.yellow); but still its not changing
`enter code here`
public class mainframe {
final static JButton Button_name = new JButton("Hi");
public static void main(String[] args)
{
public void run()
{
Button_name.setBackground(color.Red); //This is working
}
}
}
class dataframe extends mainframe implements Runnable
{
public void run()
{
//doing other things
while(some condition)
{
if (another_condition)
{
//from here i need to change that Buttons color
// i've tried this
mainframe.Button_name.setBackground(color.yellow); //Not working
}
}
}
}
Kindly anyone help with this issue
So you want to change the state of a UI component from a different thread in a different class. There are multiple ways you might be able to do this, but first, I would start by defining away for those classes to be able to only effect the change you want them to.
Exposing the entire frame, component or even button is not a good idea, people have a habit of changing things you don't want them to, so instead, we define a simple contract which states what they are allowed to do, for example...
public interface Colorable {
public void setColor(Color color);
}
This immediately decouples your code, meaning that any code that wants to change the state of your UI (or change the color of something else) can do so, without relying on the physical implementation.
Thread
First, we're going to have a look at using a Thread to change the UI...
public class ColorChanger {
private Colorable colorable;
public ColorChanger(Colorable colorable) {
this.colorable = colorable;
}
public void start() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
for (int index = 0; index < 1000; index++) {
if (index % 100 == 0) {
if ((index / 100) % 2 == 0) {
colorable.setColor(Color.GREEN);
} else {
colorable.setColor(Color.RED);
}
}
try {
// This is so you can see the colors changing
Thread.sleep(5);
} catch (InterruptedException ex) {
}
}
System.out.println("Done");
}
});
t.start();
}
}
This is a pretty basic class, it requires an instance of Colorable and will change the state of the color for every 100 counts, based on if it's an even or odd hundred
We use a simple JPanel as our base test class, when you click the button, the ColorChanger is created and started.
public class TestPane extends JPanel implements Colorable {
private JButton btn;
private ColorChanger changer;
public TestPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
btn = new JButton("I am your button");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (changer == null) {
changer = new ColorChanger(TestPane.this);
changer.start();
}
}
});
}
#Override
public void setColor(Color color) {
if (EventQueue.isDispatchThread()) {
btn.setBackground(color);
} else {
System.out.println("Not in the EDT");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setColor(color);
}
});
}
}
}
You will note that the setColor method has a bunch of code in it, this is to ensure that the updates to the UI are executed only from within the context of the Event Dispatching Thread.
SwingWorker
An alternative is to use a SwingWorker, which operates very similarly to a Thread, expect it has the ability to publish content to the EDT
public class ColorChangerWorker extends SwingWorker<Void, Color> {
private Colorable colorable;
public ColorChangerWorker(Colorable colorable) {
this.colorable = colorable;
}
#Override
protected void process(List<Color> chunks) {
colorable.setColor(chunks.get(chunks.size() - 1));
}
#Override
protected Void doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
if (index % 100 == 0) {
if ((index / 100) % 2 == 0) {
publish(Color.GREEN);
} else {
publish(Color.RED);
}
}
try {
// This is so you can see the colors changing
Thread.sleep(5);
} catch (InterruptedException ex) {
}
}
System.out.println("Done");
return null;
}
}
You will note here, that when we want to change the color we call publish. The process method is called to let us know that there is more data to be processed, but here, we're only interested in the last change.
And out TestPane...
public class TestPane extends JPanel implements Colorable {
private JButton btn;
private ColorChangerWorker changer;
public TestPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
btn = new JButton("I am your button");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (changer == null) {
changer = new ColorChangerWorker(TestPane.this);
changer.execute();
}
}
});
}
#Override
public void setColor(Color color) {
if (EventQueue.isDispatchThread()) {
btn.setBackground(color);
} else {
System.out.println("Not in the EDT");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setColor(color);
}
});
}
}
}
You will note that the setColor method remains unchanged, this is deliberate, when you test this class, you will note that "Not in the EDT" is never printed, basically meaning we could do away with all that code and just call btn.setBackground(color);, but I want you to see the difference.
The Button...
Now, when I run this code, I get the following output...
Wait a minute, that buttons background is filled?! Actually it is, but many button implementations have a secondary "content area" filling
You can turn this off using something like...
btn.setContentAreaFilled(false);
btn.setOpaque(true);
Which will result in something like...

Java game loop (painting) freezes my window

I'm changing "views" with cardLayout (this class has a JFrame variable). When a user clicks a new game button this happens:
public class Views extends JFrame implements ActionListener {
private JFrame frame;
private CardLayout cl;
private JPanel cards;
private Game game;
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("New game")) {
cl.show(cards, "Game");
game.init();
this.revalidate();
this.repaint();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
game.loop();
}
});
}
}
}
Game's loop method and heading of class:
public class Game extends JPanel implements KeyListener {
public void loop() {
while (player.isAlive()) {
try {
this.update();
this.repaint();
// first class JFrame variable
jframee.getFrame().repaint();
// first class JFrame variable
jframee.getFrame().revalidate();
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void update() {
System.out.println("updated");
}
}
I'm painting using paintComponent()
public void paintComponent(Graphics g) {
System.out.println("paint");
...
}
Actually it's not painting anything. When I do not call loop() method (so it paints it just once) all images are painted correctly. But when I call loop() method, just nothing is happening in the window. (Even close button on JFrame doesn't work.)
How to fix that? (When I was creating JFrame inside game class everything worked fine, but now I want to have more views so I need JFrame in other class.)
Thanks.
Precursor: The Event Dispatch Thread (EDT).
Swing is single-threaded. What does this mean?
All processing in a Swing program begins with an event. The EDT is a thread that processes these events in a loop along the following lines (but more complicated):
class EventDispatchThread extends Thread {
Queue<AWTEvent> queue = ...;
void postEvent(AWTEvent anEvent) {
queue.add(anEvent);
}
#Override
public void run() {
while (true) {
AWTEvent nextEvent = queue.poll();
if (nextEvent != null) {
processEvent(nextEvent);
}
}
}
void processEvent(AWTEvent theEvent) {
// calls e.g.
// ActionListener.actionPerformed,
// JComponent.paintComponent,
// Runnable.run,
// etc...
}
}
The dispatch thread is hidden from us through abstraction: we generally only write listener callbacks.
Clicking a button posts an event (in native code): when the event is processed, actionPerformed is called on the EDT.
Calling repaint posts an event: when the event is processed, paintComponent is called on the EDT.
Calling invokeLater posts an event: when the event is processed, run is called on the EDT.
Everything in Swing begins with an event.
Event tasks are processed in sequence, in the order they are posted.
The next event can only be processed when the current event task returns. This is why we cannot have an infinite loop on the EDT. actionPerformed (or run, as in your edit) never returns, so the calls to repaint post paint events but they are never processed and the program appears to freeze.
This is what it means to "block" the EDT.
There are basically two ways to do animation in a Swing program:
Use a Thread (or a SwingWorker).
The benefit to using a thread is that the processing is done off the EDT, so if there is intensive processing, the GUI can still update concurrently.
Use a javax.swing.Timer.
The benefit to using a timer is that the processing is done on the EDT, so there is no worry about synchronization, and it is safe to change the state of the GUI components.
Generally speaking, we should only use a thread in a Swing program if there's a reason to not use a timer.
To the user, there is no discernible difference between them.
Your call to revalidate indicates to me that you are modifying the state of the components in the loop (adding, removing, changing locations, etc.). This is not necessarily safe to do off the EDT. If you are modifying the state of the components, it is a compelling reason to use a timer, not a thread. Using a thread without proper synchronization can lead to subtle bugs that are difficult to diagnose. See Memory Consistency Errors.
In some cases, operations on a component are done under a tree lock (Swing makes sure they are thread-safe on their own), but in some cases they are not.
We can turn a loop of the following form:
while ( condition() ) {
body();
Thread.sleep( time );
}
in to a Timer of the following form:
new Timer(( time ), new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if ( condition() ) {
body();
} else {
Timer self = (Timer) evt.getSource();
self.stop();
}
}
}).start();
Here is a simple example demonstrating animation both with a thread and a timer. The green bar moves cyclically across the black panel.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class SwingAnimation implements Runnable{
public static void main(String[] args) {
SwingUtilities.invokeLater(new SwingAnimation());
}
JToggleButton play;
AnimationPanel animation;
#Override
public void run() {
JFrame frame = new JFrame("Simple Animation");
JPanel content = new JPanel(new BorderLayout());
play = new JToggleButton("Play");
content.add(play, BorderLayout.NORTH);
animation = new AnimationPanel(500, 50);
content.add(animation, BorderLayout.CENTER);
// 'true' to use a Thread
// 'false' to use a Timer
if (false) {
play.addActionListener(new ThreadAnimator());
} else {
play.addActionListener(new TimerAnimator());
}
frame.setContentPane(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
abstract class Animator implements ActionListener {
final int period = ( 1000 / 60 );
#Override
public void actionPerformed(ActionEvent ae) {
if (play.isSelected()) {
start();
} else {
stop();
}
}
abstract void start();
abstract void stop();
void animate() {
int workingPos = animation.barPosition;
++workingPos;
if (workingPos >= animation.getWidth()) {
workingPos = 0;
}
animation.barPosition = workingPos;
animation.repaint();
}
}
class ThreadAnimator extends Animator {
volatile boolean isRunning;
Runnable loop = new Runnable() {
#Override
public void run() {
try {
while (isRunning) {
animate();
Thread.sleep(period);
}
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
};
#Override
void start() {
isRunning = true;
new Thread(loop).start();
}
#Override
void stop() {
isRunning = false;
}
}
class TimerAnimator extends Animator {
Timer timer = new Timer(period, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
animate();
}
});
#Override
void start() {
timer.start();
}
#Override
void stop() {
timer.stop();
}
}
static class AnimationPanel extends JPanel {
final int barWidth = 10;
volatile int barPosition;
AnimationPanel(int width, int height) {
setPreferredSize(new Dimension(width, height));
setBackground(Color.BLACK);
barPosition = ( width / 2 ) - ( barWidth / 2 );
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
int currentPos = barPosition;
g.setColor(Color.GREEN);
g.fillRect(currentPos, 0, barWidth, height);
if ( (currentPos + barWidth) >= width ) {
g.fillRect(currentPos - width, 0, barWidth, height);
}
}
}
}
What does update do? You probably shouldnt call game.loop() on the EDT. You are running a loop on EDT, your repaint wont ever be invoked since repaint queues an event on EDT and it seems kind busy. Try moving game.loop() to another thread
new Thread(new Runnable() {
#override
public void run() {
game.loop();
}
}).start();
This way you wont block the EDT while the repaint still gets to be executed on the EDT.
Move game.loop() method invocation to something like:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
game.loop()
}
});
Thanks.

Getting Concurrent Modifiraction Exception. Where is the prob.lem? (code)

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.

JME: How to get the complete screen in WHITE without buttons, etc etc

Please have a look at the following code
First, Please note I am a 100% newbie to Java Mobile.
In here, I am making the light on and vibrate on when user click the button. However, I really wanted to create a SOS application which turn the whole screen into white, and go to black, like that, in the thread. I guess I didn't achieve that by this app because even the lights are on, the buttons are still there. I tried to turn the "Form" color to "white" but it seems like JME has no "Color" class.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Midlet extends MIDlet{
private Form f;
private Display d;
private Command start,stop;
private Thread t;
public Midlet()
{
t = new Thread(new TurnLightOn());
}
public void startApp()
{
f = new Form("Back Light On");
d = Display.getDisplay(this);
d.setCurrent(f);
start = new Command("Turn On",Command.OK,0);
stop = new Command("Turn Off",Command.OK,1);
f.addCommand(start);
f.setCommandListener(new Action());
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional)
{
this.notifyDestroyed();
}
private class Action implements CommandListener
{
public void commandAction(Command c, Displayable dis)
{
f.append("Light is Turnning On");
t.start();
}
}
private class ActionOff implements CommandListener
{
public void commandAction(Command c, Displayable dis)
{
}
}
private class TurnLightOn implements Runnable
{
public void run()
{
f.append("Working");
for(int i=0;i<100;i++)
{
try
{
d.flashBacklight(200);
d.vibrate(200);
Thread.sleep(1000);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
}
}
}
Use the javax.microedition.lcdui.Canvas instead of Form. This example can get you started
public void startApp()
{
f = new Form("Back Light On");
d = Display.getDisplay(this);
start = new Command("Turn On",Command.OK,0);
stop = new Command("Turn Off",Command.OK,1);
f.addCommand(start);
f.setCommandListener(new Action());
myCanvas = new MyCanvas();
d.setCurrent(myCanvas);
myCanvas.repaint();
}
Now create a canvas and implement paint method like this:
class MyCanvas extends Canvas {
public void paint(Graphics g) {
// create a 20x20 black square in the center
// clear the screen first
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0xffffff); // make sure it is white color
// draw the square, <b>changed to rely on instance variables</b>
<b>g.fillRect(x, y, getWidth(), getHeight());</b>
}
}

Categories

Resources