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()
Related
So, I'm trying to program a Game of Life simulation (Conway), and I want to show it in a JFrame.
For this purpose, I've created a JPanel, and it works perfectly, until I try to actually show a new generation. With prints, I've figured out, that the list is actually correct inside the newGeneration() method, but when paint(Graphics g) gets called (aka, when I try to repaint the JFrame), the list isn't updating.
I'm sure I've missed something obvious, and I'm not well versed in Java, but it's just getting so annoying. I'd really appreciate your help.
Here's my code;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Main {
public static void main(String[] args) {
new GameOfLife();
}
}
class GameOfLife {
// Initialising all class wide variables; sorted by type
JFrame frame = new JFrame("Game of Life");
JPanel panel;
Scanner gameSize = new Scanner(System.in);
String dimensions;
String splitHorizontal;
String splitVertical;
String confirmation;
Boolean accepted = false;
Integer split;
Integer horizontal;
Integer vertical;
Integer livingNeighbours;
int[][] cells;
int[][] newCells;
public GameOfLife() {
// Prompt for game Size
System.out.println("Please enter your game size in the following format; 'Horizontal,Vertical'");
// Run until viable game Size has been chosen
while (!accepted) {
dimensions = gameSize.nextLine();
// Check for correct format
if (dimensions.contains(",")) {
split = dimensions.indexOf(",");
splitHorizontal = dimensions.substring(0, split);
splitVertical = dimensions.substring(split + 1);
// Check for validity of inputs
if (splitHorizontal.matches("[0-9]+") && splitVertical.matches("[0-9]+")) {
horizontal = Integer.parseInt(dimensions.substring(0, split));
vertical = Integer.parseInt(dimensions.substring(split + 1));
// Check for game Size
if (horizontal > 1000 || vertical > 1000) {
System.out.println("A game of this Size may take too long to load.");
} else {
// Confirmation Prompt
System.out.println("Your game will contain " + horizontal + " columns, and " + vertical + " rows, please confirm (Y/N)");
confirmation = gameSize.nextLine();
// Check for confirmation, anything invalid is ignored
if (confirmation.matches("Y")) {
accepted = true;
System.out.println("Thank you for your confirmation. Please select live cells. Once your happy with your game, press Spacebar to start the Simulation.");
// Setting parameters depending on Size
frame.setSize(horizontal * 25 + 17, vertical * 25 + 40);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
}
}
// Prompt asking for new dimensions in case of invalid dimensions or non confirmation
if (!accepted) {
System.out.println("Please enter different dimensions.");
}
}
// Creating list of cells
cells = new int[horizontal][vertical];
// Showing the empty panel for selection of live cells
panel = new PaintCells(horizontal, vertical, cells);
frame.add(panel);
// Select live cells
panel.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (cells[(int) Math.ceil(e.getX() / 25)][(int) Math.ceil(e.getY() / 25)] == 1) {
cells[(int) Math.ceil(e.getX() / 25)][(int) Math.ceil(e.getY() / 25)] = 0;
} else {
cells[(int) Math.ceil(e.getX() / 25)][(int) Math.ceil(e.getY() / 25)] = 1;
}
frame.repaint();
}
});
// Simulation start
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == ' ') {
newGeneration();
}
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
});
}
// Generating new generations
void newGeneration() {
newCells = new int[horizontal][vertical];
// Pause inbetween generations
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
* Way of Life Rules:
* Living cells with 2 or 3 living neighbours live on to the next generation.
* Dead cells with exactly 3 living neighbours become living cells in the next generation.
* Every other living cell dies.
*/
// iterate through every cell
for (int l = 0; l < vertical; l++) {
for (int k = 0; k < horizontal; k++) {
livingNeighbours = 0;
// check amount of neighbours
if (k - 1 > -1) {
if (l - 1 > -1) {
if (cells[k - 1][l - 1] == 1) {
livingNeighbours++;
}
}
if (l + 1 < vertical) {
if (cells[k - 1][l + 1] == 1) {
livingNeighbours++;
}
}
if (cells[k - 1][l] == 1) {
livingNeighbours++;
}
}
if (k + 1 < horizontal) {
if (l - 1 >= 0) {
if (cells[k + 1][l - 1] == 1) {
livingNeighbours++;
}
}
if (l + 1 < vertical) {
if (cells[k + 1][l + 1] == 1) {
livingNeighbours++;
}
}
if (cells[k + 1][l] == 1) {
livingNeighbours++;
}
}
if (l - 1 >= 0) {
if (cells[k][l - 1] == 1) {
livingNeighbours++;
}
}
if (l + 1 < vertical) {
if (cells[k][l + 1] == 1) {
livingNeighbours++;
}
}
// change cell value depending on amount of neighbours
if (cells[k][l] == 1) {
if (livingNeighbours < 2 || livingNeighbours > 3) {
newCells[k][l] = 0;
} else {
newCells[k][l] = 1;
}
} else {
if (livingNeighbours == 3) {
newCells[k][l] = 1;
}
}
}
}
cells = newCells;
frame.validate();
frame.paint(frame.getGraphics());
newGeneration();
}
}
// Our canvas
class PaintCells extends JPanel {
private Integer horizontal;
private Integer vertical;
private int[][] newOriginalCells;
// Get our X and Y from the original prompts
public PaintCells(Integer originalHorizontal, Integer originalVertical, int[][] originalCells) {
this.horizontal = originalHorizontal;
this.vertical = originalVertical;
this.newOriginalCells = originalCells;
}
#Override
public void paint(Graphics g) {
for (int i = 0; i < vertical; i++) {
for (int j = 0; j < horizontal; j++) {
// Check cell value
if (newOriginalCells[j][i] == 1) {
g.setColor(Color.black);
} else {
g.setColor(Color.white);
}
// paint according to value
g.fillRect(j * 25, i * 25, 25, 25);
if (newOriginalCells[j][i] == 1) {
g.setColor(Color.white);
} else {
g.setColor(Color.black);
} // maybe change style?
g.drawRect(j * 25, i * 25, 25, 25);
}
}
}
}
I'm guessing, the problem is somewhere in newGeneration(), but other than that, I really have no idea anymore.
You have a common problem which I had myself a few months ago.
Java Swing GUI system works in thread called Event Dispatch Thread (EDT). This thread handle events like mouse clicks, typing etc. and paint the components to the screen. You should use this thread not as your main thread, but as sub-thread which working only once a certain time/when event happens, and not let him run continuously. In your code, since the user choose the cell to live, this thread run non-stop (because you started the program inside a listener, which is part of the EDT), and your GUI stuck, because it's updating only at the end of the thread.
You can solve this by using javax.swing.Timer. Timer is an object that allows you do tasks once a while, and it is perfect to this problem.
Use code like this:
ActionListener actionListaner = new ActionListener(){
public void actionPerformed(ActionEvent e){
//Put here you ne genration repeating code
}
};
int delay = 1000;//You delay between generations in millis
Timer timer = new timer(delay, actionListener);
The code in the actionPerformed method will repeat every second (or any other time you want it to repeat), and every operation of the timer will recall EDT instead of let it run non-stop.
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;
}
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.
I'm currently working on an assignment for Java applets. This is the assignment:
Use arrays to create an applet that lists five of your favorite songs. The applet should:
Scroll the list of song titles, one at a time.
Each song title should start at the top of the applet and scroll to the middle then, scroll off the right hand side.
Each new song title should scroll in a different colour.
The applet should loop, that is, when it gets to the end of the list, start over again at the beginning of the list.
So far, my code is as follows:
import java.applet.Applet;
import java.awt.*;
public class SongList extends Applet implements Runnable
{
String[] list = {"A - B", "C - D", "E - F", "G - H", "I - J"};
int counter = 0, xPos = 100, yPos = 0;
Color text_color = getRandomColor();
Thread runner;
public void start()
{
if (runner == null)
{
runner = new Thread(this);
runner.start();
}
}
public void run()
{
while (xPos < 220)
{
if (yPos < 100)
{
yPos += 2;
repaint();
try
{
runner.sleep(50);
}
catch (InterruptedException e) { }
}
else if (yPos == 100 && xPos != 220)
{
xPos += 2;
repaint();
try
{
runner.sleep(50);
}
catch (InterruptedException e) { }
}
else if (xPos == 220)
{
counter++;
xPos = 100;
yPos = 0;
}
}
}
public void paint(Graphics gr)
{
setBackground (Color.yellow);
gr.drawString(list[counter], xPos, yPos);
}
}
HTML file:
<html>
<body>
<applet code = "SongList.class" width = 200 height = 200>
</applet>
</body>
</html>
The code compiles fine but the I can't find a way to change to the next song after the previous one scrolls out of the screen.
How can I achieve that?
else if (xPos == 220) is useless. :) because you are using loop upto 219 as xPos<220 so change it to xPos<=220
catch (InterruptedException e) { } you are swallowing Exception so that it compiles properly but you won't determine the Exception so use
catch (InterruptedException e) { e.printStackTrace();}
and because of 1. gr.drawString(list[counter], xPos, yPos); can't get proper counter in public void paint(Graphics gr).
public class SnowflakeWallpaper extends WallpaperService {
// Limit of snowflakes per snowflake type; 4 types * 4 snowflake = 16 total
// Should keep memory usage at a minimal
static int SNOWFLAKE_AMOUNT = 4;
Drawable drawWall;
Rect wallBounds;
// Draw all snowflakes off screen due to not knowing size of canvas at creation
static int SNOW_START = -90;
ArrayList<Snowflakes> snow = new ArrayList<Snowflakes>();
private final Handler mHandler = new Handler();
#Override
public void onCreate() {
super.onCreate();
//WallpaperManager to pull current wallpaper
WallpaperManager wManager = WallpaperManager.getInstance(this);
drawWall = wManager.getFastDrawable();
wallBounds = drawWall.copyBounds();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public Engine onCreateEngine() {
return new SnowEngine();
}
class SnowEngine extends Engine {
private final Runnable mDrawSnow = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
SnowEngine() {
if(snow.size() < 16){
//Back snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakeback),
SNOW_START,
SNOW_START,
((float)(Math.random() * 2) + 1)) // Fall speed initial setup, back slowest to front fastest potentially
);
}
//MidBack snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakemid),
SNOW_START,
SNOW_START,
((float)(Math.random() * 4) + 1)
));
}
// Mid snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakemidfront),
SNOW_START,
SNOW_START,
((float)(Math.random() * 8) + 1))
);
}
// Front snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflake),
SNOW_START,
SNOW_START,
((float)(Math.random() * 16) + 1))
);
}
}
}
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawSnow);
}
#Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(mDrawSnow);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
drawFrame();
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawSnow);
}
/*
* Update the screen with a new frame
*/
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
/*
* if the snow goes too low or too right, reset;
*/
for(int i = 0; i < snow.size(); i++){
if(snow.get(i).getX() > holder.getSurfaceFrame().width()){
snow.get(i).setX(-65);
}
if(snow.get(i).getY() > holder.getSurfaceFrame().height()){
snow.get(i).setY(-69);
}
}
// Test if the array was just create; true - randomly populate snowflakes on screen
if(snow.get(1).getX() < -70){
for(int i = 0; i < snow.size(); i++){
snow.get(i).setX((int)(Math.random() * getSurfaceHolder().getSurfaceFrame().width() +1));
snow.get(i).setY((int)(Math.random() * getSurfaceHolder().getSurfaceFrame().height() + 1));
}
}
// Change snowflake x & y
for(int i = 0; i < snow.size(); i++){
snow.get(i).delta();
}
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// call to draw new snow position
drawSnow(c);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
// Reschedule the next redraw
mHandler.removeCallbacks(mDrawSnow);
if (mVisible) {
mHandler.postDelayed(mDrawSnow, 1000 / 100);
}
}
/*
* Draw the snowflakes
*/
void drawSnow(Canvas c) {
c.save();
// Draw bg
//********** add code to pull current bg and draw that instead of black. Maybe set this in config?
if(drawWall == null){
c.drawColor(Color.BLACK);
}else{
drawWall.copyBounds(wallBounds);
drawWall.draw(c);
}
/*
* draw up the snow
*/
for(int i = 0; i < snow.size(); i++){
c.drawBitmap(snow.get(i).getImage(), snow.get(i).getX(), snow.get(i).getY(), null);
}
c.restore();
}
}
}
Same question as Gabe - what's the problem?
Some general thoughts:
* You should avoid doing lots of work in a constructor. Your constructor does a ton of work that should (imho) be in some init/setup method instead. Easier to benchmark / profile there independently from instance creation.
You're using Math.random in many places - I assume you are singly threaded, but Math.random is synchronized. Per the javadocs: " if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator"
You're using Math.random which gets you a double, then multiplying, then adding, then casting. This looks wasteful. Any way to get fewer ops?
You seem to have some division - see line "mHandler.postDelayed(mDrawSnow, 1000 / 100);". Sure, that's probably compiled or JIT'd away, but you should avoid division in performance critical code (it's far slower than multiplying). So any div by a constant can be replaced by multiplying by 1 / C as a static.
You have lots of repeat accessor method calls (in some cases nearly all are repeat). See snippit:
for(int i = 0; i < snow.size(); i++){
if(snow.get(i).getX() > holder.getSurfaceFrame().width()){
snow.get(i).setX(-65);
}
if(snow.get(i).getY() > holder.getSurfaceFrame().height()){
snow.get(i).setY(-69);
}
}
You should store "holder.getSurfaceFrame().width() in a temporary / local var (perhaps once per draw loop assuming your surface is resizable by the user). You might also store snow.get(i) in a local var. Better yet (style) you can use the enhanced for loop as snow is an ArrayList. So use
for (Snow mySnow : snow) {
// Do something with mySnow
}
Hope this helps. Good luck!