I am trying to create some little game where in one Thread is running an Infinite Loop that is going to do Engine job and in JFrame is going to be all stuff that is Outputed on screen.
But I am facing one big problem that I am unable to fix and also not finding any answer on the internet. When I have no immediate output to console in infinite loop in my Thread then it seem like That Thread (in Engine class) is killed by Program and leaving me with JFrame only. But when there is some output to console in infinite loop then Thread (in Engine class) is fully working as expected to do and that is driving me crazy :(
Main CLASS:
package ccarsimulator;
import javax.swing.SwingUtilities;
public class CCarSimulator {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Window window = new Window("CCarsimulator");
});
}
}
Window CLASS:
package ccarsimulator;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Window extends JFrame{
Engine oEngine = new Engine(this);
JLabel LabelOutput = new JLabel();
JPanel PanelCanvas = new JPanel();
JLabel LabelSpeed;
JLabel LabelRPM;
JLabel LabelGearSet;
JLabel LabelTotalTime;
JLabel LabelFPS;
JLabel LabelCPS;
public Window(String WindowTitle){
super(WindowTitle);
this.addKeyListener(new KeyListener(){
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent oKey) {
System.out.println("Key Pressed");
oEngine.bKeyPressed[oKey.getKeyCode()] = true;
}
#Override
public void keyReleased(KeyEvent oKey) {
oEngine.bKeyPressed[oKey.getKeyCode()] = false;
}
});
this.addWindowListener(new WindowAdapter(){
#Override
public void windowClosing(WindowEvent oEvent){
System.out.println("Closing a Window");
oEngine.bStopEngine = true;
}
});
this.add(PanelCanvas);
PanelCanvas.add(LabelOutput);
LabelOutput.setText("Test");
this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
this.setFocusable(true);
}
}
Engine CLASS:
package ccarsimulator;
import java.awt.event.KeyEvent;
public class Engine extends Thread{
Thread thisSelf = this;
public boolean bStopEngine = false;
public Window oWindow;
public boolean []bKeyPressed = new boolean[KeyEvent.CHAR_UNDEFINED + 1];
public boolean []bKeyReleased = new boolean[KeyEvent.CHAR_UNDEFINED + 1];
public Engine(Window oWindow){
this.oWindow = oWindow;
thisSelf.start();
}
#Override
public void run(){
System.out.println("Waiting for window");
//while(!oWindow.isVisible() && !bStopEngine);
System.out.println("Engine is up and Running");
while(!bStopEngine){
//
//---------------------------------------
//Add some output to console to make this loop working...
//---------------------------------------
//
if(bKeyPressed[KeyEvent.VK_ESCAPE]){
System.out.println("Pressed Escape");
}
}
System.out.println("Engine was disabled");
}
}
Result without some output to console in Engine's class Thread infinite loop is
Waiting for window
Engine is up and Running
Key Pressed
Key Pressed
Key Pressed
Key Pressed
Closing a Window
Result with some output is:
Waiting for window
Engine is up and Running
Some debug
Some debug
...
...
Some debug
Closing a Window
Some debug
Some debug
Engine was disabled
Alright, My Java Guru friend helped me to figure this all out.
And also answered my question why this got disliked in few seconds as I published this Question what I still find unfair because I couldn't describe it much clear, If I did I would find answer on my problem without asking.
Problem was that I had to add volatile keyword on variables these were handled by Two threads (My Engine thread and Swing thread).
Without keyword volatile I was experiencing that my Swing thread was changing these two Engine thread's (bStopEngine and bKeyPressed) variables in RAM memory while these two Engine thread's variables were in CPU Cache memory so Engine thread could not know something changed because these two variables were not changed at right place.
Also helped me understand why my code works with only setting one of these two variables with volatile. It is because Java should by default load about 4KB block of memory page into CPU memory. So if by any luck my volatile variable gets into this page with another used variable by Swing Thread without using volatile keyword then that page is still set on thread synchronization and still synchronizing my variables with Swing thread so that is why it still works.
Just telling to help other people in future to rid off their confusions with similar problem or for myself in future when I once again forget all of this ;)
Related
I've got a short question and I hope somebody can help me.
Please look at the following code snippet:
public void mouseEntered(MouseEvent e){
//wait 2 seconds.
//if no other mouseEntered-event occurs, execute the following line
//otherwise restart, counting the 2 seconds.
foo();
}
Can somebody help me with that problem? I want to realize a behavior like an ToolTip: you enter a region with your mouse. If your mouse stays in that position, do something.
Start a Timer with a delay of 2 seconds in your mouseEntered() method that calls whatever it is you want to do.
Set up a new handler (mouseExited()) that stops the timer if it hasn't gone off.
Basically, you know the mouse is still there if mouseExited() hasn't been called. The timer will either go off in two seconds doing what you want or be cancelled if the mouse exits.
Although the answer provided by #Brian Roach is correct, there is yet another (and more succinct) way of achieving this. That is, using the ToolTipManager.
Example:
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
public final class ToolTipDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
ToolTipManager.sharedInstance().setInitialDelay(2000);
createAndShowGUI();
}
});
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(new JToolTipButton());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static final class JToolTipButton extends JButton{
private static final long serialVersionUID = -5193366265809801639L;
protected JToolTipButton(){
super("I can haz tooltip?");
setToolTipText("Hey man, get off me!");
}
}
}
By invoking setInitialDelay, I've changed the time the manager waits to display the tool tip from 750ms to 2000ms.
Note - Although I'm not sure, I think this may change the delay for ALL components (guess I was right), which may not be what you want..but it's still worth mentioning.
I'm trying to build a simple game in Java. Ran into the problem of the JTextPanel not updating until after the game loop terminates, which of course, isn't a good experience for the player.
I'm unfamiliar with multithreading but trying to figure it out. I can run separate code now in multiple threads, but I can't get my head around how to have the Threads interact. It's highly likely I'm missing something simple, but I can't find it by searching, so I throw myself at your mercy. I'm hanging by a thread...
Controller class and main thread. I need the gamePanel and the game to run separately. I've tried to run the Game class in a separate thread, but the game code isn't running in the gamePanel.
Controller:
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Controller_LetterFall{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new MainFrame();
}
});
}
}
And the MainFrame Class. I try to run gameplay() in a new thread.
package wordFall;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainFrame extends JFrame implements Runnable {
private GamePlay game;
private TextPanel gamePanel;
private Header header;
private Player player;
private Dictionary dictionary;
private GamePlay game;
public MainFrame(){
super("Game");
// Set the size of the frame.
setSize(400,600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
// Establish default player.
player = new Player();
dictionary = new Dictionary();
game = new GamePlay();
header = new Header();
gamePanel = new TextPanel();
add(header, BorderLayout.NORTH);
add(gamePanel, BorderLayout.CENTER);
this.game.setBoardInterface(
new BoardInterface(){
#Override
public void redraw(String text) {
gamePanel.appendText(text);
}
});
}
#Override
public void run() {
game.play();
System.out.println("The game is over.");
}
}
Any help would be appreciated.
You need something like a thread safe data container. Lets call it GameData.
Your GamePlay object needs to know that GameData object and the UI needs to know that as well because changes made by the user has to be propagated to the GameData object.
In your GamePlay object you can look for changes of the GameData object every second or so. If there are changes than you have something to do if not ..
But it would be better use an event like approach i.e. the Observer Pattern. If changes are made by the UI the GamePlay will be notified by the observable GameData object.
Furthermore, the UI can be notified as well when data changes.
This seperates the concerns very clearly and follows the Model View Controller pattern.
Well, in this case, your MainFrame object is being constructed from the swing dispatcher thread. As you said, you have to start a new thread somewhere to handle the game. Assuming you would have this code in an instance method of a MainFrame object, that would be:
Thread thr = new Thread(this);
thr.start();
Then, whenever your separate game thread wants to update the ui object, it should not do it directly, but use SwingUtilities.invokeLater:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
/* Update the ui element... */
}});
That runnable will be executed in a swing dispatcher thread when it will have time.
I use .show() before a "blocking" code like a while loop. But even though the .show gets called, the UI doesn't actually show the called panel.
Here is the code that shows the issue:
(WARNING: This code contains a while true loop.)
import javax.swing.JFrame;
import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JButton;
public class CardTest extends JFrame{
public CardTest() {
CardLayout cl = new CardLayout(0,0);
getContentPane().setLayout(cl);
JPanel panelA = new JPanel();
getContentPane().add(panelA, "PanelA");
JLabel lblPanelA = new JLabel("Panel A");
panelA.add(lblPanelA);
JButton btnSwitchToPanel = new JButton("Switch to Panel B");
panelA.add(btnSwitchToPanel);
JPanel panelB = new JPanel();
getContentPane().add(panelB, "PanelB");
JLabel lblPanelB = new JLabel("Panel B");
panelB.add(lblPanelB);
btnSwitchToPanel.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent event) {
cl.show(getContentPane(), "PanelB");
getContentPane().revalidate();
// Here is the problem. Even though cl.show is called first,
// it still doesn't show, before the while loop has terminated.
int i = 0;
while(i < 1000000){
i++;
System.out.println(i);
}
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args){
new CardTest();
}
}
If you are wondering, I need this for a downloader, where the while true loop (to download a file) is called after pressing a button in the first panel. The second panel contains the progress bar. But the progress panel never gets displayed even though the .show function is called before the download code.
UPDATE
I do know that putting the loop into a new thread, solves the draw problem, but it also introduces other problems, because I rely on sequential execution of functions after the loop(download file(loop), Unzipp file, move those files...).
The best solution would be to find a way to allow the .show() call to actually take the time to switch panes before continuing with the loop.
I use .show() before a "blocking" code like a while loop. But even though the .show gets called, the UI doesn't actually show the called panel.
Yes, because you are "blocking" the Event Dispatch Thread (EDT) which is responsible for repainting the GUI. So the GUI can't repaint itself until the code finishes executing.
You need to create a separate Thread to executing the long running task so you don't block the EDT. One way to do this is to use a SwingWorker. The SwingWorker will create the Thread for you and will notify you when the task is complete so you can update the GUI.
Read the section from the Swing tutorial on Concurrency for more information and a working example.
This happens because you are doing work on the EventDispatchingThread. This Thread is also responsible for actually drawing the GUI.
You have no other choice than doing your work in another thread.
E.g.: (Quick + Dirty)
new Thread(){
#Override
public void run() {
while (...) {...}
}
}.start();
This is because redrawing the UI is done in the same thread as event processing and doesn't happen until after the event processing has completed (i.e., all event handling methods have returned).
Best thing to do is move that "blocking" code into a runnable and execute it in a worker thread.
Is there a way to send the java frame in front of every other opened program. I know you can use
JFrame.setAlwaysOnTop(true);
but that just keeps it in front allways. I want it to only happen when a certain function is called. For instance, When I press a button on a frame, it will wait using Thread.sleep(10000) for ten seconds, but the I want it to just the frame to the front in case you clicked out of the window for a second. Any suggestions?
Take a look at Window#toFront
You may also want to take a look at
WindowListener
Swing Timer
Be careful of using Thread.sleep in a GUI environment, if used incorrectly, this will cause you window to stop updating (painting)
This is surprisingly fiddly.
The exact behavior might also depend on the operating system. But at least on Windows, a call to frame.toFront() will not necessarily bring the window to the front. Instead, it will cause the corresponding entry in the task bar to blink for a few seconds. I tried something like
f.setAlwaysOnTop(true);
f.setAlwaysOnTop(false);
which basically works, but after the window was brought to the front, it is not "active", and none of my attempts to make it active worked (e.g. requesting the focus or so).
The only solution that I found now to (reliably) work (on Windows, at least) was
if (!f.isActive())
{
f.setState(JFrame.ICONIFIED);
f.setState(JFrame.NORMAL);
}
But wonder wheter there is a more elegant solution.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class FrameToTopTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Bring me to top after 3 seconds");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
triggerBringToFront(f, 3000);
}
});
f.getContentPane().add(button);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static void triggerBringToFront(final JFrame f, final int delayMS)
{
Timer timer = new Timer(delayMS, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
// This will only cause the task bar entry
// for this frame to blink
//f.toFront();
// This will bring the window to the front,
// but not make it the "active" one
//f.setAlwaysOnTop(true);
//f.setAlwaysOnTop(false);
if (!f.isActive())
{
f.setState(JFrame.ICONIFIED);
f.setState(JFrame.NORMAL);
}
}
});
timer.setRepeats(false);
timer.start();
}
}
I'm trying to make a simple 2D game in Java.
So far I have a JFrame, with a menubar, and a class which extends JPanel and overrides it's paint method. Now, I need to get a game loop going, where I will update the position of images and so on. However, I'm stuck at how best to achieve this. Should I use multi-threading, because surely, if you put an infinite loop on the main thread, the UI (and thus my menu bar) will freeze up?
Here's my code so far:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class GameCanvas extends JPanel {
public void paint(Graphics g) {
while (true) {
g.setColor(Color.DARK_GRAY);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
#SuppressWarnings("serial")
public class Main extends JFrame {
GameCanvas canvas = new GameCanvas();
final int FRAME_HEIGHT = 400;
final int FRAME_WIDTH = 400;
public static void main(String args[]) {
new Main();
}
public Main() {
super("Game");
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
JMenuItem startMenuItem = new JMenuItem("Pause");
menuBar.add(fileMenu);
fileMenu.add(startMenuItem);
super.add(canvas);
super.setVisible(true);
super.setSize(FRAME_WIDTH, FRAME_WIDTH);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.setJMenuBar(menuBar);
}
}
Any pointers or tips? Where should I put my loop? In my main class, or my GameCanvas class?
Your game loop (model) should not be anywhere near any of your GUI classes (view). It uses your GUI classes--but even that you probably want to do through an intermediary (controller). A good way to ensure that you are doing it right is to check that your model doesn't have a single "include javax.swing.???".
The best thing you could do is to keep the game loop in it's own thread. Whenever you want to make a change in the GUI, use SwingWorker or whatever the young kids use now to force it onto the GUI thread for just that one operation.
This is actually awesome because it makes you think in terms of GUI Operations (which would constitute your controller). For instance, you might have a class called "Move" that would have the GUI logic behind a move. Your game loop might instantiate a "Move" with the right values (item to move, final location) and pass it to a GUI loop for processing.
Once you get to that point, you realize that simply adding a trivial "undo" for each GUI operation allows you to easily undo any number of operations. You will also find it easier to replace your Swing GUI with a web GUI...
You need one thread for you game loop and one thread to handle Swing UI events like mouse clicks and keypresses.
When you use the Swing API, you automatically get an additional thread for your UI called the event dispatch thread. Your callbacks are executed on this thread, not the main thread.
Your main thread is fine for your game loop if you want the game to start automatically when the programs runs. If you want to start and stop the game with a Swing GUI, then have then main thread start a GUI, then the GUI can create a new thread for the game loop when the user wants to start the game.
No, your menu bar will not freeze up if you put your game loop in the main thread. Your menu bar will freeze up if your Swing callbacks take a long time to finish.
Data that is shared between the threads will need to be protected with locks.
I suggest you factor your Swing code into its own class and only put your game loop inside your main class. If you're using the main thread for your game loop, this is a rough idea of how you could design it.
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
class GUI extends JFrame {
GameCanvas canvas = new GameCanvas();
final int FRAME_HEIGHT = 400;
final int FRAME_WIDTH = 400;
public GUI() {
// build and display your GUI
super("Game");
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
JMenuItem startMenuItem = new JMenuItem("Pause");
menuBar.add(fileMenu);
fileMenu.add(startMenuItem);
super.add(canvas);
super.setVisible(true);
super.setSize(FRAME_WIDTH, FRAME_WIDTH);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.setJMenuBar(menuBar);
}
}
public class Main {
public static void main(String args[]) {
GUI ui = new GUI(); // create and display GUI
gameLoop(); // start the game loop
}
static void gameLoop() {
// game loop
}
}
Java is really suited for event-driven programming. Basically, set up a timer event and listen. At each 'tick-tock' you update your game logic. At each GUI-event you update your data structures that the game logic method will read.