Java Swing Sprite: how to simulate a rolling dice - java

In a game board we are developing in Java we want to show a sort of rolling dice when a button is pressed. We are trying to use the following image as a sprite:
so when the button is pressed, here's what happens:
private void startAnimationDie(final JPanel panel) {
int launchResult = /* getting a launch dice result from the core game (from 1 to 6)*/
new Thread() {
public void run() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
BufferedImage[] animationBuffer = initAnimationBuffer();
BufferedImage[][] exactDieFaces = initExactDieFaces();
int launchResult = coreGame.launchDie();
coreGame.getMyPartecipant().setLastLaunch(launchResult);
AnimationSprite animation = new AnimationSprite(animationBuffer, Constants.DIE_ANIMATION_SPEED);
animation.start();
JLabel resultDie = new JLabel();
resultDie.setBounds(60, 265, Constants.DIE_SIZE, Constants.DIE_SIZE);
for (int counter = 0; counter < Constants.DIE_ANIMATION_SPEED * 100; counter++) {
animation.update();
//panel.removeAll();
panel.updateUI();
//System.out.println("infor");
resultDie.setIcon(new ImageIcon(animationBuffer[counter % Constants.ROTATIONS]));
panel.add(resultDie);
panel.updateUI();
updateUI();
}
panel.removeAll();
panel.updateUI();
AnimationSprite resultAnimation = new AnimationSprite(exactDieFaces[launchResult - 1], 6);
resultAnimation.start();
resultAnimation.update();
System.out.println("0");
resultDie.setIcon(new ImageIcon(exactDieFaces[launchResult - 1][0]));
System.out.println("1");
resultDie.setBounds(60, 265, Constants.DIE_SIZE, Constants.DIE_SIZE);
System.out.println("2");
panel.add(resultDie);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
userPlayer.getGamePanel().makePossibleMoveFlash();
}
});
} catch (Exception ex) {
}
}
}.start();
where exactDieFaces contains the dice faces according to the pawn color which is launching and that face will be shown after the simulated rolling has finished; on the other hand, animationBuffer contains 40 or 50 random faces of all colors taken from the sprite image like that:
private BufferedImage[] initAnimationBuffer() {
BufferedImage[] result = new BufferedImage[Constants.ROTATIONS];
int rowSprite, colSprite;
Random random = new Random();
for (int i = 0; i < Constants.ROTATIONS; i++) {
rowSprite = 1 + random.nextInt(5);
colSprite = 1 + random.nextInt(5);
result[i] = DieSprite.getSpriteExact(0 /* offset */,colSprite, rowSprite);
}
return result;
}
The problem is that nothing is shown during the animation: I expected to see many dice faces change one after the other until the for cycle ends but nothing is shown...The only thing I see is the launch result according to the color pawn which has launched the die but meanwhile there is nothing...can you help me?

If I understand your code correctly you are attempting to update the UI many times within the same for loop. That's not how animation works. You need a timer which regularly notifies you to move to the next frame to view.
A good place to start in understanding how to use timers is here in the Java tutorial.

Related

How do I make something stretch over a certain period of time

I'm trying to make an action "stretch" over a certain period of time, being 1 second.
public static int i = 0;
public static void click() {
Robot robot;
try {
robot = new Robot();
for (i = i; i < cps; i++) {
robot.mousePress(InputEvent.BUTTON1_MASK);
Random rn = new Random();
int range = cps - 10 + 1;
int randomNum = rn.nextInt(range) + 10;
robot.delay(randomNum);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
i++;
}
if (i == cps) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i = 0;
}
} catch (AWTException e) {
e.printStackTrace();
}
}
As you can see, this is the code for a "simple" auto clicker.
It runs within this timer.
public static void getClick() {
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
while (enabled) {
click();
}
}
}, 1, 1);
}
Is it in any way possible to make this action "stretch" it's action to 1 second. I really don't want it to click 12 times (Example), and then pause for 1 second. It'd be way nicer if these 12 clicks were "stretched" over that 1 second.
Is it possible anyone can help me?
You could just let it sleeps for 1000ms/cps. If the user set cps=10 then 10 Thread.sleep(1000/cps)would let it click ten times in one sec. With cps=0.5 it would click once in two seconds.
(with a little discrepancy, because your code needs time to execute). And you should not create your random object in your loop.
Random rn = new Random();
while(true) {
robot.mousePress(InputEvent.BUTTON1_MASK);
int range = cps - 10 + 1;
int randomNum = rn.nextInt(range) + 10;
robot.delay(randomNum);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
i++;
try {
Thread.sleep(1000/cps);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

How to stop flickering in sidescroller that uses tiles?

So I am trying to stop my program from flickering but from other peoples problems I can't find a solution that applies to my code. It just flickers and I've seen a suggestion from another persons post to change to paintComponent but I don't see how it works when compared to paint. Any help would be greatly appreciated thanks.
package runalreadypls;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.Timer;
public class GameGen extends JFrame implements KeyListener{
private static final long serialVersionUID = -7177541209377865450L;
Random rand = new Random();//Random generator for map
double move = 0;//Moves graphics generation
int timed = 750;//Moving delay
int speed = 2;//Moves Faster
int [][] world=new int[10][100];//Map generation
int WIDTH = 720;//Width of frame/window
int HEIGHT = 480;//Height of frame/window
String exitMSG;//Exit message
String user;//username
int charX = 0;//Starting coordinates for character
int charY= 4;
BufferedImage img1;//Graphics generation
static double score = 0;//Score of player
GameGen(){//Start game
super("Runner");//Set frame specifications
this.setSize(WIDTH,HEIGHT);
this.setLocationRelativeTo(null);
this.setUndecorated(true);
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
this.setResizable(false);
this.setFocusable(true);
this.setVisible(true);
//Adds spacebar listener
addKeyListener(this);
//Starts world generation
worldGen();
//Paints world
repaint();
//Swing Timer for moving of character and background
Timer timer = new Timer(timed ,null);
timer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//Increasing speed
if (speed < 400){
speed = speed + 5;
}
timer.setDelay(timed - speed);
//Increasing score
score = score + 3;
//Moving character
charX= charX + 1;
//Checks if game is over
if(gameOverCollision()){
timer.stop();//Ends "delayed loop"
}
//Gravity
//If charY is not below the screen to prevent array out of bounds
if (charY < 9){
if (world[charY+1][charX] == 0){//Moves character down/Gravity
charY = charY+1;
deleter();//Deletes character previous coords
mover();//Moves character coords on array
}
}
deleter();//Deletes character previous coords
mover();//Moves character coords on array
}
});
timer.start();//Starts "delayed loop" / swing timer
}
public void deleter(){//Deletes character previous coords so there is only one character on the screen
for(int x = 0;x <9;x++){
for(int y = 0; y < 100; y++){
if (world[x][y] == 2){
world[x][y] = 0;
}
}
}
}
public void mover(){//Moves character on the array
world[charY][charX] = 2;
repaint();
}
/*
*
* 0 = Background
* 1 = Surface
* 2 = Player
* 3 = Below Surface
*
* */
public void worldGen(){
int xAt = 5;
world[4][0] = 2;//Character placement
world[5][0] = 1;//First tile placement
for (int x = 1; x < 100; x++){ //Starting place
int X =3;
X = rand.nextInt(X)+1;//Random Number Generator
if (xAt <= 6 && xAt>=3){//If where the ground is at is between 7 and 2 (exclusive)then it will generate normally
if (X == 1){
xAt =xAt+ 1;
world[xAt][x] = 1;
}else if (X == 3){
xAt =xAt - 1;
world[xAt][x] = 1;
}else if (X==2){
world[xAt][x]=1;
}
/*
* If where the ground is at is too low between 7 and 9 (which is the bottom 3 tiles)
* then it will generate with more chances of going up instead of staying the same
*
*/
}else if(xAt >6 && xAt <9){
if (X==1 || X==3){
xAt =xAt- 1;
world[xAt][x] = 1;
}else if (X==2){
world[xAt][x]=1;
}
/*
* If where the ground is at is too low between 1 and 4((exclusive) which is the top 3 tiles)
* then it will generate with more chances of going down instead of staying the same
*
*/
}else if(xAt >1 && xAt <3){
if (X==1 || X==3){
xAt =xAt+ 1;
world[xAt][x] = 1;
}else if (X==2){
world[xAt][x] = 1;
}
}
}
//Generates ground below the surface
for(int x = 0;x <9;x++){
for(int y = 0; y < 100; y++){
if (world[x][y] == 1){
world[x+1][y] = 3;
}
if (world[x][y] == 3){
world[x+1][y] = 3;
}
}
}
}
#Override
public void paint(Graphics g){
super.paint(g);
g.setColor(Color.WHITE);
g.setFont(new Font("TimesRoman", Font.PLAIN, 100));
g.drawString(Integer.toString((int) score), 220, 100);
//Checks through 2d array to generate images
for(int x = 0;x <9;x++){
for(int y = 0; y < 100; y++){
if(world[x][y] == 0){
try {//If background then accesses background image
img1 = ImageIO.read(new File("src/runalreadypls/Background.png"));
} catch (IOException e) {
e.printStackTrace();
}
} else if(world[x][y] == 1){
try {//If surface then accesses surface image
img1 = ImageIO.read(new File("src/runalreadypls/Ground.png"));
} catch (IOException e) {
e.printStackTrace();
}
}else if (world[x][y] == 2){
try {//If surface then accesses character image
img1 = ImageIO.read(new File("src/runalreadypls/Character2.png"));
} catch (IOException e) {
e.printStackTrace();
}
}else if (world[x][y] == 3){
try {//If below surface then accesses below surface image
img1 = ImageIO.read(new File("src/runalreadypls/Foreground.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
g.drawImage(img1,(int) (45*(y-move)),60*x,null);//Draws images in procedural order
}
}
move = move + .65;//Moves screen
}
/*
*
* WALL collision
* If player misses jump
* Game is over
*
*
* */
public boolean gameOverCollision(){
//Checks if the player has missed a tile or if they have finished the map
if(world[charY][charX] == 1||charX == 99){
/*
*
* Adds current players score to the high score file
*
* */
try {
fileAppend();
} catch (IOException e) {
e.printStackTrace();
}
/*
*
* Sorts high scores in order of greatest to least
*
* */
try {
fileSort();
} catch (IOException e) {
e.printStackTrace();
}
//Deletes window
dispose();
//Exit Msg
exitMSG = "Thank you for playing " +Game.name() +" your score is: " + score;
JOptionPane.showMessageDialog(null, exitMSG, "Thank you for Playing!", JOptionPane.ERROR_MESSAGE);
//Recreates main menu
new Game();
//Reseting Score and speed
score = 0;
speed = 2;
return true;
//Checks if player has finished the map
}else{
return false;
}
}
public void fileSort()throws IOException,FileNotFoundException{
List<Integer> list = new ArrayList<Integer>();//Array list to store integers
File file = new File("highscores.txt");//File
BufferedReader br = null;//br
br = new BufferedReader(new FileReader(file));//br
String text = null;//text
while ((text = br.readLine()) != null) {//Putting integer into list
list.add((int) Double.parseDouble(text));//Converting line text to int and putting into list
}
if (br != null) {//closing
br.close();
}
Collections.sort(list);//sort list
Collections.reverse(list);//reverse list for descending order
FileWriter fw = new FileWriter("highscores.txt");//Write to file
BufferedWriter bw = new BufferedWriter(fw);
for(Integer lists: list) {
bw.write(lists);//WRiting to file
bw.newLine();//New line
}
bw.close();//Closing
fw.close();
}
public void fileAppend()throws IOException{
//Declaring writers
FileWriter fw = null;
BufferedWriter bw = null;
PrintWriter pw = null;
fw = new FileWriter("highscores.txt", true);
bw = new BufferedWriter(fw); pw = new PrintWriter(bw);
pw.println(score); //Adding to file
//Closing
pw.close();
bw.close();
fw.close();
}
/*
*
* Listens to spacebar input to move character up a tile
*
* */
#Override
public void keyPressed(KeyEvent e) {
if(charY<9 && charY >0){
if (e.getKeyCode()==KeyEvent.VK_SPACE){
if(world[charY][charX+1] == 1){
charY-=1;//Moving character Y coords up a tile
deleter();//Deletes character previous coords
mover();//Changing player's tile in array
}
if(world[charY][charX+1] != 1){
score = score -3; //Substracts score if player jumps early
}
}
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
}
Similar suggestions to the many similar questions:
Draw within the protected void paintComponent method of a JPanel, not within a JFrame's paint method. This will give you double buffering by default.
Never read in a file or resource within a painting method. This method's speed is the most important factor in the perceived responsiveness of your program and you should do nothing to slow it down. It should be for painting and painting only.
Instead, read the image into a variable once, and then draw the image from the variable within paintComponent.
Paint background images to a BufferedImage and then draw that within the paintComponent method.
Double Buffer Drawing
in the run() method, you should have something like this --
while(running) {
gameUpdate();
gameRender();
repaint(); // Note: I would not use this either, you should also
// have thead.sleep() call to stop event coalescence: when
// JVM is overloaded by repaint() request, JVM may combine
// request, causing rendering request to be skipped and
// animation to "jump."
}
Well, inorder to reduce flikering -- "jump" -- you should apply a double buffering thechnique. One way to do this to have the gameRender() draw into its own Graphics object, which would represent an image the same size of the screen.
My Example:
// Global variables for off-screen rendering
private Graphics dbg;
private Image dbImage = null;
private void gameRender() {
/* Draw the current frame to an image buffer*/
if(dbImage == null) { // create the buffer
dbImage = createImage(PWIDTH, PHEIGHT);
if(dbImage == null ) {
System.out.println("dbImage is null");
return;
}
else
dbg = dbImage.getGraphics();
}
//clear the background
dbg.setColor(Color.white);
dbg.fillRect(0, 0, PWIDTH, PHEIGHT);
// Draws game elements
// ...
if(gameOver)
gameOverMessage(dbg);
} // end gameRender()
dbImage is placed on screen by the paintComponent() as a result of the repaint() request in the run() loop -- while(running)...gameUpdate(), Render(), repaint()..
Here is the call that would place dbImage on screen. This call is made after rendering steps have been completed (the above code):
public void paintComponent(Graphic g) {
super.paintComponent(g);
if (dbImage != null)
g.drawImage(dbImage, 0,0,null);
}
If extensive drawing is done directly to the screen, the process may take long enough to become noticiable by the user. the call to drawImage() in paintComponent() is fast enough that the change from one frame to the next is preceived as instantaneous.
paintComponent should be kept simple
Futhermore, I would also convert to active rendering, since the call to repaint() is only a request, its hard to know when repaint() has completed... which means the sleep time in the animation thread (while(running)...ect) is basically a guess.. When I say sleep time, I mean this:
running = true;
while (running) {
gameUpdate(); // Game state is updated
gameRender(); // Render to a buffer
paintScreen(); // Note: Change from repaint() to paintScreen(),
// will explain later.
try {
Thread.sleep(20); // sleep a bit.. 20 ms to be exact.
} catch(InterruptedException ex){}
} // end of run()
There should be a specified sleeping time for for 3 main reasons: 1. it stops the animation thread, which frees up the CPU for other task, like garbage collection by the JVM. 2. the sleep call gives the preceeding request to repaint() time to be proccessed. 3. sleep reduces the chances of event coalescence because it give the JVM time to complete tasks instead of combining task causing "jumping" affect in the animation.
However, using the repaint() request, as stated before the sleep time is little more than a guess; if the specified time is too long, then the animation speed is impaired for no reason. if the dely is too short, then the repaint requests may be queued by the JVM and skipped if the load is too lard, causing "flickering"
Active rendering puts the task of active rendering the buffer into your hands, allowing the render time to be accurately measured.
like so,
running = true;
while (running) {
gameUpdate(); // Game state is updated
gameRender(); // Render to a buffer
paintScreen(); // Note: Change from repaint() to paintScreen(),
// will explain later.
try {
Thread.sleep(20); // sleep a bit.. 20 ms to be exact.
} catch(InterruptedException ex){}
System.exit(0);
/**
* Actively render the buffer image to screen
*/
private void paintScreen() {
Graphics g;
try {
g = this.getGraphics(); // get the panel's graphic context
if ((g != null) && (dbImage != null))
g.drawImage(dbImage, 0, 0, null);
Toolkit.getDefaultToolkit().sync(); // sync the display on some
// systems
g.dispose();
}
catch (Exception e) {
System.out.println("Graphics context error: " + e);
} // end catch
} // end paintScreen()
} // end of run()
I would like to explain more, but the rest I leave to you to explore, it is a world of fun.. Forgive any puncuation and spelling -- I am in a rush.. Goodluck
respectfully,
#author James Stowell - !di0m -
Full implementation w/ concerns of seperating updates from rendering mentioned in the comments section below:
Since I prefer writing 3D games, I will use the J3DTimer, you may employ other high resolution timers, the JMF timer is an option...
The run() method without possible pauses:
private static final int MAX_FRAME_SKIPS = 5;
// number of frames that can be skipped in any one animation loop
// i.e the game state is updated but not rendered
private static final int NO_DELAYS_PER_YIELD = 16;
// number of frames with a delay of 0 ms before the animation
// thread yields to other running threads.
#Override
public void run() {
/* Repeatedly update, render, sleep so loop takes close
* to period nsecs. Sleep inaccuracies are handled.
* The timing calculation use the Java 3D timer.
*
* Overrruns in update/renders will cause extra updates
* to be carried out so UPS ~== requested FPS
*/
long beforeTime, afterTime, timeDiff, sleepTime;
long overSleepTime = 0L; // Number zero of type long (0L)
int noDelays = 0;
long excess = 0L;
beforeTime = J3DTimer.getValue();
running = true;
while (running) {
gameUpdate(); // Game state is updated
gameRender(); // Render to a buffer
paintScreen(); // Draw buffer to screen
afterTime = J3DTimer.getValue();
timeDiff = afterTime - beforeTime;
sleepTime = (period - timeDiff) - overSleepTime; // Time left in this
// loop
if (sleepTime > 0) { // Some time left in this cycle
try {
Thread.sleep(sleepTime/1000000L); // nano -> ms
} catch (InterruptedException ex) {}
overSleepTime =
(J3DTimer.getValue()-afterTime) - sleepTime;
}
else { // sleepTime <= 0; frame took longer than the period
excess -= sleepTime; // store excess time value
overSleepTime = 0L;
if (++noDelays >= NO_DELAYS_PER_YIELD) {
Thread.yield(); // Give another thread a chance to run
noDelays = 0; // New thread, no delays.
} // end inner if
} // end outer if
beforeTime = J3DTimer.getValue();
/* If frame animation is taking too long, update the game state
* without rendering it, to get the updates/sec bearer to required
* FPS
* */
int skips = 0;
while((excess > period) &&(skips < MAX_FRAME_SKIPS)) {
excess -= period;
gameUpdate(); // Update state, but don't render
skips++;
}
} // end while
System.exit(0); // So enclosing JApplet/JFrame exits
} // end of run()
/**
* Actively render the buffer image to screen
*/
private void paintScreen() {
Graphics g;
try {
g = this.getGraphics(); // get the panel's graphic context
if ((g != null) && (dbImage != null))
g.drawImage(dbImage, 0, 0, null);
Toolkit.getDefaultToolkit().sync(); // sync the display on some
// systems
g.dispose();
}
catch (Exception e) {
System.out.println("Graphics context error: " + e);
} // end catch
} // end paintScreen()

JLabel setIcon not working

I have a program that displays a picture of a coin. When the user clicks the coin the coin changes from heads to tails and vice versa. That works fine.
The problem arises when I want to have a button that flips the coin a random number of times (In this case between 3 and 10 times inclusive).
The method used to change the image icon:
flip.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e1) {
playerCoinState = coinState;
System.out.println("Clicked");
int flips = (new Random().nextInt(8)) + 3;
for(int i = 0; i < flips; i++){
try{
Thread.sleep(1000);
}catch(InterruptedException e2){
System.exit(1);
}
System.out.println("Auto Flipped");
changeFace();
}
}
});
And this is the method used to change the ImageIcon of the JLabel coin:
private void changeFace(){
System.out.println("Changing...");
switch(coinState){
case 0:
System.out.println("Coin State 0");
try {
coin.setIcon(new ImageIcon(ImageIO.read(new File("res/Heads.png"))));
} catch (IOException e) {
e.printStackTrace();
}
coinState = 1;
break;
case 1:
System.out.println("Coin State 1");
try {
coin.setIcon(new ImageIcon(ImageIO.read(new File("res/Tails.png"))));
} catch (IOException e) {
e.printStackTrace();
}
coinState = 0;
break;
}
}
The JLabel coin is initialised as:
coin = new JLabel(new ImageIcon("res/Tails.png"));
coinState represents the value of the coin. 0 for heads, 1 for tails.
playerCoinState is used to keep track of the coin state the player has selected before the coin is to be flipped by the computer the random amount of times.
This...
for(int i = 0; i < flips; i++){
try{
Thread.sleep(1000);
}catch(InterruptedException e2){
System.exit(1);
}
System.out.println("Auto Flipped");
changeFace();
}
Is blocking the event dispatching thread, preventing ui from been updated until after the method exits
You should try using a Swing Timer instead, which acts as a pseudo loop, but waits in the background for a prescribed period of time before triggering a tick within the context of the EDT, making it safe to update the UI from
coin = new JLabel(new ImageIcon((ImageIO.read(new File("path"))));
Use this instead of coin.setIcon(new ImageIcon(ImageIO.read(new File("res/Tails.png")))); So you are creating the coin label new every time. If you are working with eclispe and windowbuilder, you can just use the tools on the sidebar.
MadProgrammer helped me to solve this.
The new and improved ActionListener for the button is:
flip.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e1) {
playerCoinState = coinState;
int flips = (new Random().nextInt(10) + 5);
Timer timer = new Timer(400, new ActionListener(){
int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
if(counter == flips){
((Timer)e.getSource()).stop();
}
changeFace();
counter++;
}
});
timer.start();
}
});
Thanks again MadProgrammer =)

Othello game timer not working properly

I am just a beginner in Java. We have a college project and we are making a game called othello (also called reversi), we are using Java applet for the game. Board design and gameplay is complete and is working fine but we are having an issue with the timer.
The problem is player's move is not detected until timer has elapsed completely, i.e suppose timer for each move is 10 sec and if the user places his move at the 5th sec, his move will be detected only after 10 sec This behavior does not serve any purpose.
The method I am following is:
Display the time(integer) on the applet screen
Sleep for one sec
Increment time and update graphics
The whole thing is put inside the loop which is actually creating the problem. I need a better and in fact a working modification to this code.
Here are the code of two relevant functions
void startTimer() {
Graphics g = getGraphics();
for(sec =0 ; sec < 10 ; sec++){
String time = new String();
time = Integer.toString(sec);
g.drawString(time, WIDTH+80, 145);
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(NewApplet.class.getName()).log(Level.SEVERE, null, ex);
}
update(getGraphics());
}
}
public boolean mouseUp(Event e, int x, int y) {
int column = (int)(x / (WIDTH / 8));
int row = (int)(y / (WIDTH / 8));
if (turn == BLACK) {
if (placeLegalCheck(column, row, turn) == true){
flipDisk(column, row, turn);
turn = - turn;
diskCount();
startTimer();
update(getGraphics());
try {
Thread.sleep(500);
} catch (Exception excep){
}
}
}
if (turn == WHITE) {
if (placeLegalCheck(column, row, turn) == true){
flipDisk(column, row, turn);
turn = - turn;
diskCount();
startTimer();
update(getGraphics());
try {
Thread.sleep(500);
} catch (Exception excep){
}
}
}
return true;
}

JPanel freezes my whole application when trying to draw on it

I am coding Oregon Trail for a school project and I am implementing the hunting mini game. We are using model view presenter with a card layout. When the HuntingPanel gets switched to it calls run, and the JOptionPane comes up, but then the whole application freezes and I have to force quit. I coded the entire hunting game in a separate project, and just now brought the files over to the Oregon Trail game. It works fine in its own project with its own JFrame. I'm not sure what to do.
I call this to initialize the panel, switch to it, and run the game.
public void initialize(int ammo) {
player.setBullets(ammo);
bulletLabel.setText("Bullets: "+player.getBullets());
presenter.switchToPanel(OregonTrailPresenter.HUNTING_PANEL);
run();
}
This is my run method.
public void run() {
// starting message
JOptionPane.showMessageDialog(null, "You have reached a nearby field to hunt. You will stay\nhere until " +
"you run out of ammunition or click Return to Trail.");
// while the player has bullets or doesn't click return to trail
while (player.getBullets() > 0 && stillHunting) {
// creates random animals
checkForAnimal();
// moves and updates screen
repaint();
update();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
endHunting();
}
And here are other method used.
private void checkForAnimal() {
int x = 0;
int y = rand.nextInt(MAX_Y)-40;
int rand1 = rand.nextInt(100);
String str = null;
if (rand1 < 50) {
str = "left";
x = MAX_X-40;
}
else if (rand1 >= 50) {
str = "right";
x = 0;
}
double gen = rand.nextGaussian(); // gen is a number from -inf to +inf
gen = Math.abs(gen); // gen is now a number from 0 to inf
if (gen >= 1.9 && gen < 2.1) { //1.19%
animalList.add(new Bunny(x,y,str));
}
if(gen >= 2.1 && gen < 2.2) { //0.9%
animalList.add(new Bear(x,y,str));
}
if (gen >= 2.2 && gen < 2.3) {
animalList.add(new Deer(x,y,str));
}
}
public void update() {
for (int i = 0; i < animalList.size(); i++) {
animalList.get(i).move();
}
}
You have to implement javax.swing.Timer instead of Thread.sleep(int), because this code line freezes all GUI during EDT until Thread.sleep(int) ends. Here is demonstrations what happens if the GUI is delayed during EDT by Thread.sleep(int)
Your program "freezes" because you did not start a new thread for the while loop. Since the panel updates and redraws are handled in the main thread, you are preventing them from happening. To fix this problem you have to start a new thread. You can do this by making your class implement runnable and use new Thread(this).start() to run your loop.
class HuntingGame extends JPanel implements Runnable {
public void initialize(int x) {
//...
new thread(this).start();// This will run your 'run()' method in a new thread.
}
}

Categories