In my platforming game, I have a swing timer that ticks the following method every 17 milliseconds. It is here that I run the various events that I need to run. My plan was to sleep the timer for 1 second whenever the player died.
My problem is that I don't really have a firm understanding of how to sleep a swing timer. Every example that I look at involves using a Thread which is not what I am doing. When I do the following I get a IllegalMonitorStateException error.
public void actionPerformed(ActionEvent e)
{
if (!louis.isDead)
{
if (louis.right)
{
louis.moveR();
}
if (louis.left)
{
louis.moveL();
}
if (!louis.left && !louis.right)
louis.friction();
louis.gravity();
louis.checkCol(charMap, mapX, mapY);
mapX -= louis.moveX();
mapY -= louis.moveY();
louis.checkDeath(charMap, mapX, mapY);
}
else
{
try {
time.wait(1000);
} catch (InterruptedException e1) {e1.printStackTrace();}
mapX = initMapX;
mapY = initMapY;
louis = new Player(spawnX, spawnY);
}
repaint();
}
Thanks in advance
My problem is that I don't really have a firm understanding of how to sleep a swing timer.
You don't. You don't sleep anything in Swing, not unless you want to put your entire GUI asleep. Instead why not simply record the start time, check the elapsed time with each tick, and then activate whatever code you want activated when the elapsed time is greater than your cut-off?
Not sure what you're trying to do, but perhaps something in this sort of range (code not tested)
private static final long TOTAL_DEATH_TIME = 1000L; // total time of your delay
private long deathInitTime = -1L; // start time of death throes
public void actionPerformed(ActionEvent e) {
if (!louis.isDead) {
// .... etc... unchanged from your code
} else {
// he's dead
if (deathInitTime < 0) {
// if dead but just now dead, initialize deathInitTime
deathInitTime = System.currentTimeMillis();
} else {
// he's been dead
// check how long he's been dead
long deathTime = System.currentTimeMillis() - deathInitTime;
if (deathTime > TOTAL_DEATH_TIME) {
// if he's been dead long enough, call this code
mapX = initMapX;
mapY = initMapY;
louis = new Player(spawnX, spawnY);
deathInitTime = -1L; // and re-initialize deathInitTime
}
}
}
repaint();
}
Related
I'm trying to create something like an info screen that just scrolls through images (and maybe one day pdfs) that are uploaded with a small managment app I wrote.
After some reading I think I understand why I should NOT have Thread.sleep in my for loop. I read some posts on stackoverflow and other pages that teached me not to do it.
Apparently I'm supposed to use ExecutorService for something like this, but I can't wrap my head around it.
So after preparing my GUI and everything else I finally call my showImages and stay in this while loop.
After each loop I reload the records from the file systems, so any changes are represented on the screen.
I created a metafile containing the preferred delay for each image and just sleep for the amount of time.
private void showImages() {
//noinspection InfiniteLoopStatement
while (true) {
loadRecords();
for (ImageRecord record : records) {
System.out.println("Showing: " + record.getFilename());
imageLabel.setIcon(record.getIconForInfoScreen(screenWidth, screenHeight));
int delay = record.getDurationAsInt();
System.out.println("Waiting for " + delay + " milliseconds");
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
So if I understand correctly I could use something like
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(showImage(),0, delay, TimeUnit.MILLISECONDS);
But this would trigger the showImage() every "delay" millisecond and I still need to increment some counter, to get to the next image in my records.
Could someone push me in the right direction? I'm kinda lost at the moment.
All the comments on my question can be considered good ideas an and one of them also lead me to my final result. But it in the end, the matter was, that I did not understand how to implement a timer into my loop.
Or in other words, the loops had to be gone and instead use a counter and set the delay for the follow up record inside the timer loop.
private void loopImages() {
loadRecords();
recordSize = records.size();
int delay = records.get(1).getDurationAsInt();
timer = new Timer(delay, e -> showImages());
timer.start();
}
private void showImages() {
if (recordIndex == recordSize) {
loadRecords();
recordSize = records.size();
recordIndex = 0;
}
ImageRecord record = records.get(recordIndex);
imageLabel.setIcon(record.getIconForInfoScreen(screenWidth, screenHeight));
recordIndex++;
if (recordIndex < recordSize) {
record = records.get(recordIndex);
int delay = record.getDurationAsInt();
timer.setDelay(delay);
}
}
I'm trying to make an image twinkle with RaffleImage(); while I'm executing the timer, my character is immune to any collision, I want it to be immune only for 2 seconds, so the timer get execute only for 2 seconds and then get finished.
I've tried subtracting System.currentTimeMillis() but any variable I create from this method, have always the same value, making me get a zero from that subtracting.
Do you know how I can stop or pause the timer after any elapsed time in seconds?
immuneTimer = new Timer(50, new ActionListener() {
#Override
public synchronized void actionPerformed(ActionEvent e) {
long initMillis = System.currentTimeMillis();
if (System.currentTimeMillis() - initMillis > 2000 ) { // this substract gives me 0
initImages();
setImmune(false); // so this never reached
immuneTimer.stop();
} else {
raffleImage(); //its executing like forever;
}
}
});
The Swing timer fires a an ActionEvent. From the event you can use getSource() to get the source of the event. Cast that source to the swing timer object and use that to turn it off.
To know when to turn it off you need to have a variable count the number of times the swing timer is invoked. When the variable reaches that amount, turn it off.
int elapsedTime = 0;
int timerDelay = 50;
int max = 2000;
public void actionPerformed(ActionEvent ae) {
elapsedTime += timerDelay; // you could use getDelay here but it
// is in milliseconds.
if (elapsedTime >= max) {
Timer s = (Timer)ae.getSource();
s.stop();
}
// rest of code
}
I'd like to create MIDI clock which works basically like a normal clock. It simply ticks and counts its ticks. Now I have read quite a few times that Thread.sleep() isn't accurate at all. So correcting it every every few cycles ensures that it is stable in the long term?
My Clock Class
public class Clock implements Runnable {
long beatsPassed = 0;
double bpm = 120; // default
double beatLength; // default
boolean running = false;
Clock(int bpm) {
this.bpm = bpm;
this.beatLength = 60.0 / bpm;
this.running = true;
}
public void run() {
int beatLengthInMS = (int) (this.beatLength * 1000);
long baseTime = System.currentTimeMillis();
// long corrected = 1;
try {
while (running) {
// check delay every 9 beats
// mod == 0 lets it the first time through which causes a negative timeout
if (this.beatsPassed % 10 == 9) {
// corrected = (System.currentTimeMillis() - baseTime) - (beatLengthInMS * 9);
Thread.sleep(beatLengthInMS + ((System.currentTimeMillis() - baseTime) - (beatLengthInMS * 9)));
baseTime = System.currentTimeMillis();
} else {
Thread.sleep(beatLengthInMS);
}
this.beatsPassed++;
// System.out.println(corrected);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Now I have measured actually quite steady times. It always adds about 6-9ms.
Am I forgetting something fundamental or is my approach wrong? Also great would be if you could tell me a more performant way to this?
The simplest approach (apart from using Timer, there are AFAIK two of them in the JDK) is a method
void sleepUntil(long absoluteTime) throw InterruptedException {
while (true) {
long now = System.currentTimeMillis();
if (now >= absoluteTime) break;
Thread.sleep(absoluteTime - now);
}
}
The loop is used because of spurious wakeups (which may never occur in practice, but better safe than sorry). The absoluteTime gets computed in advance (basically, you only look at the current time at the very beginning).
I am working on a drawing application, using Java and Swing. It has a constant update loop that runs constantly, as long as a boolean variable is set to true. The loop is located inside a thread.
It works fine, but now I want the loop to only run at certain times (only when the mouse is pressed), and otherwise not run. (Thus not wasting memory for nothing).
To stop the loop, I can simply set that variable to false. But my question is, how can I restart the loop after stopping it? Setting that variable back to true will not restart the loop. What would be a good way to do this?
EDIT: My (a little simplified) loop:
public void run(){
int TICKS_PER_SECOND = 50;
int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
int MAX_FRAMESKIP = 10;
long next_game_tick = System.currentTimeMillis();
int loops;
boolean app_is_running = true;
while( app_is_running ) {
loops = 0;
while( System.currentTimeMillis() > next_game_tick && loops < MAX_FRAMESKIP) {
update();
next_game_tick += SKIP_TICKS;
loops++;
}
repaint();
}
}
Use Object.wait to suspend the thread when it isn't running. Have another thread call Object.notify to wake it up from its sleep.
To execute the thread body once every FRAME_RATE ms while being controllable by an externally defined Boolean, the run method could be structured as such:
public void run()
{
long delay;
long frameStart = System.currentTimeMillis();
// INSERT YOUR INITIALIZATION CODE HERE
try
{
while (true)
{
if (active) // Boolean defined outside of thread
{
// INSERT YOUR LOOP CODE HERE
}
frameStart += FRAME_RATE;
delay = frameStart - System.currentTimeMillis();
if (delay > 0)
{
Thread.sleep(delay);
}
}
}
catch (InterruptedException exception) {}
}
Additionally, if you want to eliminate the slight overhead of the constantly running loop (for a mostly inactive thread), the Boolean in the while loop could be replaced with a Semaphore object:
while (true)
{
semaphore.acquire(); // Semaphore defined outside thread with 1 permit
// INSERT YOUR LOOP CODE HERE
semaphore.release();
frameStart += FRAME_RATE;
delay = frameStart - System.currentTimeMillis();
if (delay > 0)
{
Thread.sleep(delay);
}
}
To stop the loop externally use semaphore.acquire(); to restart it use semaphore.release().
I'm working on a school project in Java and need to figure out how to create a timer.
The timer I'm trying to build is supposed to count down from 60 seconds.
You can use:
int i = 60;
while (i>0){
System.out.println("Remaining: "i+" seconds");
try {
i--;
Thread.sleep(1000L); // 1000L = 1000ms = 1 second
}
catch (InterruptedException e) {
//I don't think you need to do anything for your particular problem
}
}
Or something like that
EDIT, i Know this is not the best option, otherwise you should create a new class:
Correct way to do this:
public class MyTimer implements java.lang.Runnable{
#Override
public void run() {
this.runTimer();
}
public void runTimer(){
int i = 60;
while (i>0){
System.out.println("Remaining: "+i+" seconds");
try {
i--;
Thread.sleep(1000L); // 1000L = 1000ms = 1 second
}
catch (InterruptedException e) {
//I don't think you need to do anything for your particular problem
}
}
}
}
Then you do in your code:
Thread thread = new Thread(MyTimer);
Since you didn't provide specifics, this would work if you don't need it to be perfectly accurate.
for (int seconds=60 ; seconds-- ; seconds >= 0)
{
System.out.println(seconds);
Thread.sleep(1000);
}
Look into Timer, ActionListener, Thread
There are many ways to do this. Consider using a sleep function and have it sleep 1 second between each iteration and display the seconds left.
It is simple to countdown with Java. Lets say you want to countdown 10 min so Try this.
int second=60,minute=10;
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
second--;
// put second and minute where you want, or print..
if (second<0) {
second=59;
minute--; // countdown one minute.
if (minute<0) {
minute=9;
}
}
}
};
new Timer(delay, taskPerformer).start();