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 =)
Related
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();
}
}
Background
I want to write to a file that would contain the high scores in my game.
Issue
Every time tick() gets called the program writes to the file with the score, however, I only want it to write the score once every time the game ends.
For example, if my score was 30, it would print 30 5 times if I leave the window open for 5ms
How do I get my program to only write once and not every 100ms?
Code
Game loop:
#Override
public void run()
{
while (isRunning)
{
tick();
//render(); Not important in terms of this question
try
{
Thread.currentThread();
Thread.sleep(100);
} catch (Exception e)
{
e.printStackTrace();
}
}
stop();
}
tick() gets called once every 100ms.
private void tick()
{
//inGame is just a global variable to determine if still in game
//move() is a method used to move the player object.
if (inGame) move();
else Util.writeScore("scores", 10);
//10 is just an example, score would be held in a variable in production.
}
private void writeScore(String fileName, int score)
{
if (score == 0)
return;
else
{
try (FileWriter fw = new FileWriter(fileName, true);
BufferedWriter bw = new BufferedWriter(fw);
PrintWriter out = new PrintWriter(bw))
{
out.println(score);
out.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
I think you need two flags -- like this:
// Globals
int inGame=1; // 1 when in game 0 when not
int scoreDone=0; // 1 when score written 0 when not.
private void tick()
{
//inGame is just a global variable to determine if still in game
//move() is a method used to move the player object.
if (inGame == 1)
move();
if ((inGame == 0) && (scoreDone == 0))
{
Util.writeScore("scores", 10);
scoreDone = 1;
}
}
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 trying to make this GUI where there are these words that fall from the top of the screen and I have the following code in my constructor, where the word moves by one unit every 100 milliseconds. However, when I run the program the word appears at 400 once I start and not at the top of the screen. I suppose that there is a specific method that could update my y value in continuous time? Thanks!
while(y <= 400){
y++;
repaint();
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Painting.class.getName()).log(Level.SEVERE, null, ex);
}
}
sleeping your thread isn't the best practice to take the desired result, use Timer instead:
Timer timer = new Timer(100, new ActionListener() { //100 is the delay time between each interval
#Override
public void actionPerformed(ActionEvent e) {
repaint();
y ++;
if (y > 400) {
((Timer) e.getSource()).stop();
}
}
});
timer.start();
These two buttons are cycling through an array and when pressed increment to many times and depending on what the maximum index is. I thought it might be because (for backwards a least I have "i set as maximum index" but changing to current index stops the button functioning all together.
btnprev.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int i = dataArrayMaxIndex; i >= 0; i-- ) {
System.out.println("backwards");
dataArrayCurrentIndex = i;
websitetxt.setText(dataArray[i].getWebsitename());
usernametxt.setText(dataArray[i].getUsername());
passwordtxt.setText(dataArray[i].getPassword());
notestxt.setText(dataArray[i].getNotes());
}
}
});
btnnext.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int i = 0; i<dataArrayMaxIndex; i++) {
System.out.println("foward");
dataArrayCurrentIndex = i;
websitetxt.setText(dataArray[i].getWebsitename());
usernametxt.setText(dataArray[i].getUsername());
passwordtxt.setText(dataArray[i].getPassword());
notestxt.setText(dataArray[i].getNotes());
}
}
});
I am unsure as to fixing this problem and could use some help and suggestions. I feel it would be more helpful for myself to not be given the answer but to have some constructive criticism to lead myself to it, that being said feel free to post an a straight, working answer if that's your thing.
I think that your for loops don't belong in this code as you'll loop all the way to the end or all the way to the beginning with each button press. Instead, simply increment or decrement an index variable and use that index. I assume that you'll be using the dataArrayCurrentIndex for this functionality.
i.e.,
btnprev.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dataArrayCurrentIndex--;
// if you want to do a cyclic loop of the data
if (dataArrayCurrentIndex < 0) {
dataArrayCurrentIndex= maxIndex - 1;
}
System.out.println("backwards");
websitetxt.setText(dataArray[dataArrayCurrentIndex].getWebsitename());
usernametxt.setText(dataArray[dataArrayCurrentIndex].getUsername());
passwordtxt.setText(dataArray[dataArrayCurrentIndex].getPassword());
notestxt.setText(dataArray[dataArrayCurrentIndex].getNotes());
}
});
btnnext.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dataArrayCurrentIndex++;
// if you want to do a cyclic loop of the data
if (dataArrayCurrentIndex >= maxIndex) {
dataArrayCurrentIndex = 0;
}
// etc...
}
});