I have a slot machine program with some graphics, and the graphic is supposed to change when you pull the lever and for some reason it changes the graphic after it goes through all of the other code first. Have a look:
Declare images:
static ImageIcon jImage = new ImageIcon("C:\\Users\\Harry\\Desktop\\jackpotLeverNotPulled.png");
static ImageIcon jackpot1 = new ImageIcon("C:\\Users\\Harry\\Desktop\\jackpotimage1.png");
static ImageIcon jackpotPulled = new ImageIcon("C:\\Users\\Harry\\Desktop\\jackpotLeverPulled.png");
Now I add to panel:
static JLabel jlb = new JLabel(jImage);
Now I want the image to change when a certain area is clicked on my panel but the main jackpot code runs first and then the image changes:
public void mousePressed(MouseEvent e) {
// Returns the X coordinate of where the mouse was click on the panel
System.out.println("X Coordinate: " + e.getX() );
// Returns the Y coordinate of where the mouse was click on the panel
System.out.println("Y Coordinate: " + e.getY() );
System.out.println();
Scanner ansr = new Scanner(System.in);
String yesno;
int random = (int)(Math.random() * 21 );
int percentCheck = (int)(Math.random() * 10 );
if (e.getX ()>975 && e.getX ()<1159 && e.getY ()>82 && e.getY ()<218){
jlb.setIcon(jackpotPulled);
if (cashMoneyz<1) {
System.out.println("Insufficient funds");
image1.setIcon(jackpot1);
} else {
System.out.println("One dollar has been removed from you slot machine balance");
cashMoneyz--;
try {
System.out.println("Spinning...");
Thread.sleep(1000);
System.out.println("Spinning...");
Thread.sleep(1000);
System.out.println("SPINNINGGGGG...OMG SOOO INTENSE");
Thread.sleep(1000);
} catch (InterruptedException ie)
{
}
}
System.out.println("You have this much money (in dollars) left in your slot machine balance: " + cashMoneyz);
System.out.println("");
System.out.println("----------------------------------------------------------------------------------");
}
It does the if statements and try catches and only changes the graphic to jackpotPulled at the end of everything.
Thanks in advance :)
There are basically 2 problems in your code:
1)
A call to label.setImage() won't update immediately, as this is true for everything in AWT and Swing. Any time a repaint request is fired, it is simply added to a repaint queue, that will wait patiently for all other tasks done in the EDT (Event Dispatching Thread) to finish. But since you do other things in the mousePressed(), they will run first. A simple solution for this would be to do the computing in mouseReleased(). But a bigger problem exists.
2)
What you are currently doing is "starving" the EDT - a bad programming practice - as all screen related invocations must be executed immediately. Sleeping the EDT won't allow any repaints to take place while running. This is true for any long running tasks as well.
Its solution is to run the non-painting calls in a different thread:
private volatile boolean isComputing = false;
public void mousePressed(MouseEvent evt) {
if(isComputing)
return;
isComputing = true;
// .
// .
// .
// change icon here, or any
// other swing related change.
// .
// .
// run game
new Thread(){
public void run(){
// all non-swing computations
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// change back icons,
// and some other swing updates
}
}
isComputing = false;
}
}.start();
}
An event handler (mousePressed) souldn't take a long time to complete, there should be no sleeps in it. As long as it runs (or sleeps), nothing else can happen. The runtime is waiting for the handler to return and displays the image after that. I would move most of the code to a function called by a timer, and use a state variable to keep track of how intensely the wheels are spinning.
Related
I created a maze generator in swing for a class, and it works great. The only thing is, I want to show the maze being created in realtime, but the way I have everything set, it only updates after all the calculations have completed. I am using paintComponent and repaint in my code. How do I have it show the JFrame and draw lines immediately, rather than doing the algorithm and showing them all at the end?
Here is the relevant code:
public void generateMaze() {
Stack<Box> stack = new Stack<>();
int totalCells = Finals.numCol * Finals.numRow, visitedCells = 1;
Box currentCell = boxes[0][0];
Box nextCell;
stack.add(currentCell);
while (visitedCells < totalCells) {
nextCell = checkNeighbors(currentCell.xCoord, currentCell.yCoord);
if (nextCell != null) {
knockWalls(currentCell, nextCell);
stack.add(currentCell);
currentCell = nextCell;
visitedCells++;
} else {
currentCell = stack.pop();
}
}
repaint();
}
Here is my paintComponent method override
public void paintComponent(Graphics g) {
for(int x = 0; x < Finals.numRow; x++) {
for(int y = 0; y < Finals.numCol; y++) {
if(boxes[y][x].top != null)
boxes[y][x].top.paint(g);
if(boxes[y][x].bottom != null)
boxes[y][x].bottom.paint(g);
if(boxes[y][x].left != null)
boxes[y][x].left.paint(g);
if(boxes[y][x].right != null)
boxes[y][x].right.paint(g);
}
}
}
The knockWalls method sets certain walls equal to null, which causes them to not be drawn in the paintComponent method. I'm still fairly new at a lot of this, so I apologize if some of the code isn't super high quality!
Thanks everyone.
As MadProgrammer already pointed out in the comments, you are almost certainly blocking the Event Dispatch Thread. This is the thread that is responsible for repainting the GUI, and for handling the interaction events (like mouse clicks and button presses).
So presumably, you start the computation via a button click, roughly like this:
// The actionPerformed method of the button that
// starts the maze solving computation
#Override
void actionPerformed(ActionEvent e)
{
generateMaze();
}
That means that the event dispatch thread will be busy with executing generateMaze(), and not be able to perform the repainting.
The simplest solution would be to change this to something like
// The actionPerformed method of the button that
// starts the maze solving computation
#Override
void actionPerformed(ActionEvent e)
{
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
generateMaze();
}
});
thread.start();
}
However, some care has to be taken: You may not modify Swing components from this newly created thread. If you have to modify Swing components, you have to put the task that performs the actual modification of the Swing component back on the EDT, using SwingUtilities.invokeLater(task). Additionally, you have to make sure that there are no other synchronization issues. For example, the lines
if(boxes[y][x].top != null)
boxes[y][x].top.paint(g);
are still (and have to be!) executed by the event dispatch thread. In this case, you have to make sure that no other thread can set the boxes[y][x].top to null after the EDT has executed the first line and before it executes the second line. If this may be an issue in your case, you might have to provide a bit more code, e.g. the code that is showing where and how the boxes[y][x] are modified.
I am working on Deal or No Deal with a user interface. The first problem I ran into was how to wait for a button action to continue, and I used Count Down Latches and it worked perfectly. But whenever, I click a button, everything in my JFrame disappears and comes back when you mouse over, it all of a sudden reappears when I press another buttton (This never happened before I used Count Down Latches, and this also happens with Semaphores, etc.) I'll try to keep my code as relevant as possible.
public CountDownLatch cdl = new CountDownLatch(1);
pickFirst();
try {
cdl.await();
} catch (Exception E) {
}
while (banker.findCasesLeft() > 2) {
banker = new Banker(Main.f.values);
for (i = casesToPick; i >= 1; i--) {
cdl = new CountDownLatch(1);
pickCase();
picked = false;
try {
cdl.await();
} catch (Exception E) {
}
}
^^^ That was my class that deals with picking cases Below is class with actionlisteners
public void actionPerformed(ActionEvent ae) {
if (!Main.me.pickedFirst) {
Main.me.pickedCase = caseNo;
Main.f.log += "You picked to keep case " + caseNo + ".\n";
setText(caseNo + "\np");
Main.f.changeLog();
Main.me.pickedFirst = true;
Main.me.cdl.countDown();
} else {
int value = Main.me.values[caseNo-1];
Main.me.values[caseNo] = 0;
Main.f.values[getIndex(value)].setSelected(true);
Main.f.log += "You picked to get rid of case " + caseNo + ". It contained $" + value + ".\n";
Main.f.changeLog();
Main.me.picked = true;
Main.me.cdl.countDown();
}
setEnabled(false);
}
Note that the await() method of CountDownLatch "Causes the current thread to wait until the latch has counted down to zero." If that thread is the event dispatch thread, GUI updates will be blocked until the wait condition changes. In general, CountDownLatch is meant to allow separate threads to rendezvous; it should't be used to within the event dispatch thread. This complete example, which coordinates multiple SwingWorker instances, may help clarify the usage.
I try to improve the movement of my figures but i dont find the real reason why they stutter a bit. I am moving them with an SequenceAction containing an MoveToAction and an RunnableAction that does reset the moveDone flag so there can be started a new move.
The game itself is gridbased so if a move is done the squence starts a move to the next grid depending on the direction. So here is how it looks like:
note that this is inside of the Act of the figure
....//some more here
if (checkNextMove(Status.LEFT)) //check if the position is valid
{
status = Status.LEFT; //change enum status
move(Status.LEFT); // calls the move
screen.map.mapArray[(int) mapPos.x][(int) mapPos.y] = Config.EMPTYPOSITION;
screen.map.mapArray[(int) (mapPos.x - 1)][(int) mapPos.y] = Config.CHARSTATE;
mapPos.x--;
moveDone = false;
}
//... same for the up down right and so on.
//at the end of this checking the updating of the actor:
// methode from the absctract to change sprites
updateSprite(delta);
super.act(delta); // so the actions work
//end of act
And here is the move Method that does add the Actions
protected void move(Status direction)
{
// delete all old actions if there are some left.
clearActions();
moveAction.setDuration(speed);
//restart all actions to they can used again
sequence.restart();
switch (direction)
{
case LEFT:
moveAction.setPosition(getX() - Config.TILE_SIZE, getY());
addAction(sequence);
break;
case RIGHT:
moveAction.setPosition(getX() + Config.TILE_SIZE, getY());
addAction(sequence);
break;
case UP:
moveAction.setPosition(getX(), getY() + Config.TILE_SIZE);
addAction(sequence);
break;
case DOWN:
moveAction.setPosition(getX(), getY() - Config.TILE_SIZE);
addAction(sequence);
break;
default:
break;
}
}
The figures dont really move smothy.
Anyone does see a misstake or isnt it possible to let them move smothy like this?
It always stutter a bit if a new move is started. So i think this might not work good. Is there a differnt approach to move them exactly from one Grid to an nother? (Tried it myself with movementspeed * delta time but this does not work exactly and i struggeled around and used the Actionmodel)
it seems to make troubles with the one Frame where it does not move for example.
Here is an mp4 Video of the stuttering:
stuttering.mp4
just to mention, the camera movement is just fine. it's smothy but the figure stutters as you can see i hope
If you use your moveAction each move, you should call moveAction.restart() to reset the counter inside it:
// delete all old actions if there are some left.
clearActions();
sequence.reset(); // clear sequence
// add movementspeed. Note it can change!
moveAction.restart();
moveAction.setDuration(speed);
UPDATE:
Now issue occurs, because you call restart of SequenceAction after clearActions(). If your clearActions() removes all actions from SequenceAction then restart will be called for empty SequenceAction. So, do this instead:
//restart all actions to they can used again
sequence.restart();
// delete all old actions if there are some left.
clearActions();
moveAction.setDuration(speed);
Okay so i solved it myself. It has nothing todo with changing sequences around. It has something todo with the updatefrequency of the act() of the figures. The MoveToAction interpolates between the 2 points given by time. So if the last update, updates the MoveToAction by "to much time" it would need to go over the 100% of the action. So it would move to far but the action does not do this it set the final position and thats what it should do. Thats why it does not look fluent.
So how to solve this issue?
By decreasing the updatetime the "to far movement" decreases too because the steplength is getting smaller. So i need to increase the updatespeed above the 60fps. Luckily i got an thread for my updating of the figures and positions and so on. GameLogicThread. This ran on 60fps too. So i solved the stuttering by increasing it's frequence. up to around 210fps at the moment so the "overstep" is minimal. You can't even notice it.
To Show how my thread works:
public class GameLogicThread extends Thread
{
private GameScreen m_screen;
private boolean m_runing;
private long m_timeBegin;
private long m_timeDiff;
private long m_sleepTime;
private final static float FRAMERATE = 210f;
public GameLogicThread(GameScreen screen)
{
m_screen = screen;
setName("GameLogic");
}
#Override
public void run()
{
m_runing = true;
Logger.log("Started");
while (m_runing)
{
m_timeBegin = TimeUtils.millis();
// hanlde events
m_screen.m_main.handler.processEvents();
synchronized (m_screen.figureStage)
{
// now figures
if (m_screen.m_status == GameStatus.GAME)
{
m_screen.character.myAct(1f / GameLogicThread.FRAMERATE);// and here it is ;)
m_screen.figureStage.act(1f / GameLogicThread.FRAMERATE);
}
}
m_timeDiff = TimeUtils.millis() - m_timeBegin;
m_sleepTime = (long) (1f / GameLogicThread.FRAMERATE * 1000f - m_timeDiff);
if (m_sleepTime > 0)
{
try
{
Thread.sleep(m_sleepTime);
}
catch (InterruptedException e)
{
Logger.error("Couldn't sleep " + e.getStackTrace());
}
}
else
{
Logger.error("we are to slow! " + m_sleepTime);
}
}
}
public void stopThread()
{
m_runing = false;
boolean retry = true;
while (retry)
{
try
{
this.join();
retry = false;
}
catch (Exception e)
{
Logger.error(e.getMessage());
}
}
}
}
I am attempting to create a simple animation in which a series of bubble rotate around a centre point. I have one version of animation where the bubbles spread from the centrepoint before they begin to rotate, which works fine, but as soon as I click one of the images (which sparks the animation) the screen freezes for a moment and then the bubbles appear in their end position, rather than showing each step they made.
What I have so far is:
while(bubble[1].getDegree() != 270)
{
long time = System.currentTimeMillis();
//the below if statement contains the function calls for
//the rotating bubble animations.
next();
draw();
// delay for each frame - time it took for one frame
time = (1000 / fps) - (System.currentTimeMillis() - time);
if (time > 0)
{
try
{
Thread.sleep(time);
}
catch(Exception e){}
}
}
public void draw()
{
for(int i = 1; i < bubble.length; i++)
{
iconLabel[i].setLocation(bubble[i].getX(), bubble[i].getY());
textLabel[i].setLocation((bubble[i].getX()+10),(bubble[i].getY()+10));
}
}
For clarity, the method "next()" merely changes the position of the bubble to the appropriate place, I know this to be functioning as I have had the animation work before but once I implemented the animation to JLabels it stopped working.
Any help would be appreciated.
The drawing is frozen because you block the event dispatch thread. Drawing is done in the same thread as the while loop, and since the loop prevents anything else happening while it's running, swing can get to drawing only after the loop is finished, so only the last position is drawn.
Use a swing Timer instead:
timer = new Timer(delay, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// whatever you need for the animation
updatePositions();
repaint();
}
});
timer.start();
And then call timer.stop() when all the frames you need have been processed.
I am trying to implement a Fish Eye Image Menu in a JavaBean. As a start, I created a JLabel and put this code on the mouseEntered event. But when I run this, the output is shaky and doesn't re-size the JLabel.
This is my code.
new Thread() {
public void run() {
for (int i = 0; i < 30; i++) {
int x = imgLabel.getWidth()+1;
int y = imgLabel.getHeight()+1;
imgLabel.setSize(x , y );
// sets the icon to the label
imgLabel.setIcon(new ImageIcon(new ImageIcon(getClass().getResource("/pics/icon.png")).getImage().getScaledInstance(x , y, Image.SCALE_DEFAULT)));
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}.start();
If I comment that line where I set the image to the JLabel, the label gets re-sized perfectly.
Where has this gone wrong?
The problem is that getScaledInstance() together with resource loading is slow. Do these calculations once and cache them (in an array of 30 items). Not every time in the for loop.
Another thing: make sure you use a Swingworker for your animation, that helps in the timing and avoids setting icons outside the Event Dispatch Thread (EDT).