Sorry for my bad english
I write a Java desktop application that plays musical instruments audio files samples.
Each time a note is received by the application, it must stop the current playing note and play the new one. if the user stops playing, the app must do a fade-out to the active note.
By the way, smaller latency is better.
I created a runnable which owns a play and stop method allowing me to play or stop a note(with fade out)
My code works but the sound quality is bad(many click and clipping etc)..
-How to improve my code? Did I make mistake?
-Otherwise it is there any other Java technology than JavaFx media player that is best suited for my need?
public class SamplePlayer implements Runnable{
private boolean fadeOut=false; // true when a fade out is needed
private int soundToFadeOut=0; //the sound number to fade out
private int sound=0; // the sound number to play (nothing if zero)
private int lastSound=0; //the last sound number played that wee need to stop befor playing a new one
MediaPlayer[] sample = new MediaPlayer[42]; //array to store all pre-loaded sounds
public SamplePlayer()
{
for(int i=0;i<42;i++) // load all sounds
{
Media pick = new Media(new File("./src/files/samples/1_"+(i+55)+".wav" ).toURI().toString());
sample[i] = new MediaPlayer(pick);
}
}
/**
* Here i try to play a sound each time the sound variable has changed
* and fade out the sound specified in soundToFadeOut variable if fadeOut variable come to true;
*/
#Override
public void run() {
while(true) // bad, but it's just for testing
{
try {
if(sound!=0) // if a sound need to be played
{
if(lastSound!=0) // if i's not the first sound to played i stop the previous one.
{
sample[lastSound].stop();
}
sample[sound].setVolume(1); //returns the volume to 1 in case we made a fade out
lastSound=sound;
sound=0;//sound = 0 to monitor new other sound
}
if(fadeOut==true) // if wee need to make a fade out
{
fadeOut=false;
fadeOut(sample[soundToFadeOut],50);
}
Thread.sleep(1); // if not the thread keep busy and play, stop method not working properly
} catch (InterruptedException ex) {
Logger.getLogger(SamplePlayer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private void fadeOut(MediaPlayer player,double itteration) // fade out method : run a new thread to itterate on volume
{Runnable task = () -> {
try{
for(int i=0;i<itteration;i++)
{
double value=(itteration-i)/itteration; // set value to 1.0 to near 0.0 each itteration
if(value<0.1)
{
value=0;
}
player.setVolume(value);
Thread.sleep(5);
}
}catch(Exception e){e.printStackTrace();}
};
new Thread(task).start();
}
/*
*
* play and stop method which are called when a sound need to be played or stoped (fade out)
*
*/
public void play(int note)
{
sound=note-55; // -55 just to match with our array range
}
public void stop(int note)
{
soundToFadeOut=note-55;
fadeOut=true;
}
}
Related
I'm trying to make a Java application that simulates someone typing on their keyboard. The keystroke sound is played in a loop (Java chose a keystroke sound among others randomly and plays it) at a variable interval (to simulate a real person typing).
It works fine in the beginning, but after around the 95th iteration, it stops playing the sound (while still looping) for less than 4 seconds then plays the sound again. And after the 160th iteration, it plays the sound almost every second (instead of every third to sixth of a second).
After a while, it stops playing the sound for a long time, then forever.
Here is the source for the AudioPlayer.java class:
package entity;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
public class AudioPlayer implements Runnable {
private String audioFilePath;
public void setAudioFilePath(String audioFilePath) {
this.audioFilePath = audioFilePath;
}
#Override
public void run() {
File audioFile = new File(audioFilePath);
try {
AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFile);
AudioFormat format = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
Clip audioClip = (Clip) AudioSystem.getLine(info);
audioClip.open(audioStream);
audioClip.start();
boolean playCompleted = false;
while (!playCompleted) {
try {
Thread.sleep(500);
playCompleted = true;
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
audioClip.close();
} catch (UnsupportedAudioFileException ex) {
System.out.println("The specified audio file is not supported.");
ex.printStackTrace();
} catch (LineUnavailableException ex) {
System.out.println("Audio line for playing back is unavailable.");
ex.printStackTrace();
} catch (IOException ex) {
System.out.println("Error playing the audio file.");
ex.printStackTrace();
}
}
}
And here is the Main.java class to test the keystroke simulator:
package sandbox;
import java.util.Random;
import entity.AudioPlayer;
public class Main {
public static void main(String[] args) {
Random rnd = new Random();
AudioPlayer audio;
for(int i = 0; i < 10000; i++) {
int delay = rnd.nextInt(200)+75;
try {
Thread.sleep(delay);
}
catch (InterruptedException ie) {}
int index = rnd.nextInt(3)+1;
audio = new AudioPlayer();
audio.setAudioFilePath("resources/keystroke-0"+index+".wav");
Thread thread = new Thread(audio);
thread.start();
System.out.println("iteration "+i);
}
}
}
I used multiple short (less than 200ms) wave files of different sounding keystrokes (3 in total) all in the resources directory.
EDIT
I read your answers and comments. And I'm thinking maybe I misundertood them because the suggested solutions don't work, or maybe I should have made myself clear on what I exactly wanted. Also, I need to note that I don't use threads often (and have no clue what a mutex is).
So I'll first explain what I exactly want the program to do. It should be able to simulate keystroke and so I used a Thread because it allows two keystroke sounds to overlap just like when a real person is typing. Basically the sound clips I am using are keystroke sounds and a keystroke sound is composed of two sounds:
the sound of a key being pressed.
the sound of a key being released.
If at some point the program allows two keystroke to overlap it will sound as if someone pressed one key then another and then released the first key. That's how really typing sounds like!
Now the issues I encountered using the proposed solutions are:
When calling the run() method of the AudioPlayer directly,
public static void main(String[] args)
{
// Definitions here
while (running) {
Date previous = new Date();
Date delay = new Date(previous.getTime()+rnd.nextInt(300)+75);
// Setting the audio here
audio.run();
Date now = new Date();
if (now.before(delay)) {
try {
Thread.sleep(delay.getTime()-now.getTime());
} catch (InterruptedException e) {
}
}
System.out.println("iteration: "+(++i));
}
}
the sounds play sequentially (one after the other) and at a rate that depends on the sleep duration of the AudioPlayer (or depends on the delay if the delay in the main() method is higher than the sleep duration of the AudioPlayer), which is no good because it won't sound like the average typist (more like someone who is new to typing and still looking for every keys when typing).
When calling the join() method of the AudioPlayer's Thread,
public static void main(String[] args)
{
//Variable definitions here
while (running) {
int delay = rnd.nextInt(200)+75;
try
{
Thread.sleep(delay);
}
catch (InterruptedException ie)
{
}
//Setting the AudioPlayer and creating its Thread here
thread.start();
try
{
thread.join();
}
catch(InterruptedException ie)
{
}
System.out.println("iteration "+(++i));
}
}
the sounds play sequentially as well and at a rate that depends on the sleep duration of the AudioPlayer (or depends on the delay if the delay in the main() method is higher than the sleep duration of the AudioPlayer) which, again, is no good for the same reason as before.
So, to answer one of the commenter's question. Yes! there are other concerns not expressed before which require the threads in the first place.
I found a workaround that "solves" my issue (but that I don't consider as a proper solution since I am, in a way, cheating): What I did is increase the sleep duration of the AudioPlayer to something that is unlikely to be reached before the program is stopped (24 hours) and from what I've seen it doesn't use much resources even after more than an hour.
You can check out what I want, what I get when running the suggested solutions and what I get using my workaround on this youtube videos (Unfortunately StackOverflow doesn't have video uploading feature. so I had to put it on youtube).
EDIT
The sound effects can be downloaded here.
How about this single-threaded solution which is a cleaner version of your own, but re-using already opened clips from buffers? To me the typing sounds pretty natural even though there are no two sounds playing at the same time. You can adjust the typing speed by changing the corresponding static constants in the Application class.
package de.scrum_master.stackoverflow.q61159885;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine.Info;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static javax.sound.sampled.AudioSystem.getAudioInputStream;
import static javax.sound.sampled.AudioSystem.getLine;
public class AudioPlayer implements Closeable {
private final Map<String, Clip> bufferedClips = new HashMap<>();
public void play(String audioFilePath) throws IOException, UnsupportedAudioFileException, LineUnavailableException {
Clip clip = bufferedClips.get(audioFilePath);
if (clip == null) {
AudioFormat audioFormat = getAudioInputStream(new File(audioFilePath)).getFormat();
Info lineInfo = new Info(Clip.class, audioFormat);
clip = (Clip) getLine(lineInfo);
bufferedClips.put(audioFilePath, clip);
clip.open(getAudioInputStream(new File(audioFilePath)));
}
clip.setMicrosecondPosition(0);
clip.start();
}
#Override
public void close() {
bufferedClips.values().forEach(Clip::close);
}
}
package de.scrum_master.stackoverflow.q61159885;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.IOException;
import java.util.Random;
public class Application {
private static final Random RANDOM = new Random();
private static final int ITERATIONS = 10000;
private static final int MINIMUM_WAIT = 75;
private static final int MAX_RANDOM_WAIT = 200;
public static void main(String[] args) throws UnsupportedAudioFileException, IOException, LineUnavailableException {
try (AudioPlayer audioPlayer = new AudioPlayer()) {
for (int i = 0; i < ITERATIONS; i++) {
sleep(MINIMUM_WAIT + RANDOM.nextInt(MAX_RANDOM_WAIT));
audioPlayer.play(randomAudioFile());
}
}
}
private static void sleep(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException ignored) {}
}
private static String randomAudioFile() {
return "resources/keystroke-0" + (RANDOM.nextInt(3) + 1) + ".wav";
}
}
You might have noticed that the AudioPlayer is Closeable, i.e. you can use "try with resouces" in the calling application. That way it makes sure that at the end of the program all clips are closed automatically.
The key to replaying the same clip is of course clip.setMicrosecondPosition(0) before you start.
Update: If you want to simulate multiple persons, just modify the main class like this. BTW, I don't know anything about audio programming and whether there is a way to better deal with mixers and overlapping sounds. It is just a proof of concept in order to give you an idea. There is one thread per person, but each person types in a serial fashion, not two keys at the same time. But multiple persons can overlap because there is one AudioPlayer per person with its own set of buffered clips.
package de.scrum_master.stackoverflow.q61159885;
import java.util.Random;
public class Application {
private static final Random RANDOM = new Random();
private static final int PERSONS = 2;
private static final int ITERATIONS = 10000;
private static final int MINIMUM_WAIT = 150;
private static final int MAX_RANDOM_WAIT = 200;
public static void main(String[] args) {
for (int p = 0; p < PERSONS; p++)
new Thread(() -> {
try (AudioPlayer audioPlayer = new AudioPlayer()) {
for (int i = 0; i < ITERATIONS; i++) {
sleep(MINIMUM_WAIT + RANDOM.nextInt(MAX_RANDOM_WAIT));
audioPlayer.play(randomAudioFile());
}
} catch (Exception ignored) {}
}).start();
}
private static void sleep(int delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException ignored) {}
}
private static String randomAudioFile() {
return "resources/keystroke-0" + (RANDOM.nextInt(3) + 1) + ".wav";
}
}
Besides the things EdwinBuck said, I believe you are doing way to much work in your AudioPlayer class (and every time). Try pre-create an AudioPlayer instance one time for all of your audio files (I believe it is 4?), and add a separate play() method, so that inside your loop you can do something like audioplayers[index].play().
Also note that in your AudioPlayer class you are waiting 500ms for the sound to finish which is longer than you wait to play the next sound. This will - after a while - lead to you running out of available threads... perhaps there's a callback you could use when the AudioClip is finished, instead of waiting.
With threads, you are talking about independent flows of execution. Your program is designed such that the picking of a delay an the playing of the sound are not independent.
something like
for (int i = 0; i < 10000; i++) {
int delay = rnd.nextInt(200)+75;
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
}
int index = rnd.nextInt(3)+1;
audio = new AudioPlayer();
audio.setAudioFilePath("resources/keystroke-0"+index+".wav");
audio.run();
System.out.println("iteration "+i);
}
Would express that you "wait" then "run a wav" then "repeat".
Right now you're relying on consistent scheduling of threads of execution on the cores to get the desired result; except that threads aren't intended to be the way to express consistent scheduling of execution. Threads are intended to be the way to express independent scheduling of execution.
Odds are between your current wait and the current "play the wave" a few other things get in between, and with enough time, perhaps even the wav files could be played out-of-order. I'd make the ordering explicit.
If you need good control on the timing within the loop, look to a gaming loop type setup. It's similar to your for loop, but looks like
while (running) {
SomeTimeType previous = new TimeType();
SomeTimeOffset delay = new TimeOffset(rnd.nextInt(200)+75);
...
audio.run();
SomeTimeType now = new TimeType();
if (now.minus(offset).compareTo(previous) > 0) {
try {
Thread.sleep(now.minus(offset).toMillis())
} catch (InterruptedException e) {
}
}
}
The primary difference here is that your random delays start from the beginning of the wav file's play time to the beginning of the next wav file's play time, and there is no delay between files if the delay is shorter than the wav file's play time.
Also, I'd look into if the AudioPlayer can be reused between wave file playbacks, as that will probably get you even better results.
Now on the off chance you really need to keep the playing in a separate thread, you need to join the thread of the loop to the thread of the AudioPlayer to ensure that the AudioPlayer finishes before the loop thread advances. Even though you are waiting a longer time in the for loop, remember, any process can come off the CPU core at any time, so your wait isn't an assurance that the for loop takes more time per iteration than the AudioPlayer takes per wav file, if the CPU had to handle something else (like a network packet).
for (int i = 0; i < 10000; i++) {
int delay = rnd.nextInt(200)+75;
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
}
int index = rnd.nextInt(3)+1;
audio = new AudioPlayer();
audio.setAudioFilePath("resources/keystroke-0"+index+".wav");
Thread thread = new Thread(audio);
thread.start();
thread.join();
System.out.println("iteration "+i);
}
The thread.join() will force the for loop to go into a sleep state (possibly shifting it off the CPU) until the audio thread completes.
The library AudioCue was made for exactly this sort of thing. You might try running the "frog pond" demo, simulating a number of frogs all croaking, all generated from a single frog croak recording.
You can take a single typewriter click and run everything from it, create a cue with, say 10 simultaneous overlaps allowed. Then use an RNG to pick which of the 10 "cursors" to click. The 10 typists can each have their own volume & pan location, and can be pitched slightly differently so that it sounds like the typewriters are different models or the keys are being hit with different weights (as if an old manual typewriter).
One can tweak RNG algorithms for the different typing speeds (using varying sleep times).
For myself, I wrote an event system where the play commands are queued up on an event system using a ConcurrentSkipListSet, where the stored objects include a timing value (milliseconds after a given zero point) that is used for sorting as well as controlling when the play gets executed. That might be overkill if you don't intend to do this sort of thing very often.
I am creating "Air Percussion" using IMU sensors and Arduino to communicate with computer (3 separate IMUs and Arduinos). They are connected to the computer through USBs. I am gathering data on separate Threads (each thread for each sensor). When I connect only one "set" my program is working really fast. I can get even 5 plays of sound per second. Unfortunatelly when i am trying to connect 3 sensors and run them on separate Threads at the same time my program slows down horribly. Even when im moving only one of sensors, I can get like 1 "hit" per second and sometimes it's even losing some of the sounds it should play. I'll show only important parts of the code below.
In the main i've got ActionListener for button, where it should start gathering the data. I run there 3 separate Threads for each USB Port.
connectButton.addActionListener(new ActionListener(){
#Override public void actionPerformed(ActionEvent arg0) {
int dialogButton = 1;
if(!flagaKalibracjiLewa || !flagaKalibracjiPrawa){ //some unimportant flags
dialogButton = JOptionPane.showConfirmDialog(null, "Rozpoczynając program bez kalibracji będziesz miał do dyspozycji mniejszą ilość dzwięków. Czy chcesz kontynuować?","Warning",JOptionPane.YES_NO_OPTION);
}else{
dialogButton = JOptionPane.YES_OPTION;
}
if(dialogButton == JOptionPane.YES_OPTION){
if(connectButton.getText().equals("Connect")) {
if(!flagaKalibracjiLewa && !flagaKalibracjiPrawa) podlaczPorty();
Thread thread = new Thread(){
#Override public void run() {
Scanner data = new Scanner(chosenPort.getInputStream());
dataIncoming(data, "lewa");
data.close();
}
};
Thread thread2 = new Thread(){
#Override public void run() {
Scanner data = new Scanner(chosenPort2.getInputStream());
dataIncoming(data, "prawa");
data.close();
}
};
Thread thread3 = new Thread(){
#Override public void run() {
Scanner data = new Scanner(chosenPort3.getInputStream());
dataIncoming(data, "stopa");
data.close();
}
};
thread.start();
thread2.start();
thread3.start();
connectButton.setText("Disconnect");
} else {
// disconnect from the serial port
chosenPort.closePort();
chosenPort2.closePort();
chosenPort3.closePort();
portList.setEnabled(true);
portList2.setEnabled(true);
portList3.setEnabled(true);
connectButton.setText("Connect");
}
}
}
});
in "dataIncoming" method there is bunch of not important things (like picking, which sound should be played etc.). The important part is in the while loop. In the "while" im gathering next lines of data from sensor. When one of the values is higher than something it should play a sound but only if some time has passed and the sensor has moved a certain way. (when the drumstick is going down the "imuValues[4]" is increasing, when its going up its decreasing, so when its past 160 it means that the player has taken the drumstick up so its ready for the next hit)
while(data.hasNextLine()) {
try{
imuValues = data.nextLine().split(",");
if(Double.parseDouble(imuValues[4])>200 && flagaThreada) {
flagaThreada = false;
playSound(sound1);
}
if(Double.parseDouble(imuValues[4])<160 && System.currentTimeMillis()-startTime>100) {
flagaThreada = true;
startTime=System.currentTimeMillis();
}
}catch(Exception e){
System.out.println("ERROR");
}
}
and finally the method for playing the sound is :
public static synchronized void playSound(String sound) {
try {
String url = "/sounds/"+sound+".wav";
Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(
Main.class.getResourceAsStream(url));
clip.open(inputStream);
clip.start();
} catch (Exception e) {
System.err.println("ERROR IN OPENING");
}
}
Is my computer to slow to compute and play sounds for 3 sensors at the same time? Or is there a way to create those Threads in a better fashion?
I wrote a version of Clip, called AudioCue, which allows multi-threading on the play commands. It is open source, BSD license (free), consists of three files which you can cut and paste into your program. There is also an API link for it. More info at AudioCue. The site has code examples as well as link to API and source code. There is also some dialogue about its use at Java-gaming.org, under the "Sound" topic thread.
The basic principle behind the code is to make the audio data available in a float array, and send multiple, independent "cursors" through it (one per play command). The setup lets us also do real time volume fading, pitch changes and panning. The audio is output via a SourceDataLine which you can configure (set thread priority, buffer size).
I'm maybe a week or two away from sharing a more advanced version that allows all AudioCues to be mixed through a single output line. This version has five classes/interfaces instead of three, and is being set up for release on github. I'm also hoping to get a donate button and the like set up for this next iteration. The next version might be more useful for Arduino in that I believe you are only allowed up to 8 audio outputs on that system.
Other than that, the steps you have taken (separating the open from the play, using setFramePosition for restarts) are correct. I can't think of anything else to add to help out besides writing your own mixer/cue player (as I have done and am willing to share).
I made clone of ""Flappy bird" game by watching video tutorials.i programmed it so that when the bird falls or collides with the tubes a game over message appears on the screen and the game restarts when the player taps on the screen.
The problem is that when the user fails to tap the bird in time and it collides with the tube,the game over screen appears immediately and the user happens to tap on the game over screen which results in restarting of the game.
This makes the user unable to see the score.I have already tried using Thread.sleep().Following is the code
(gameState == 2)
{
batch.draw(gameOver,Gdx.graphics.getWidth()/2-gameOver.getWidth()/2,Gdx.graphics.getHeight()/2-gameOver.getHeight()/2);
try
{
Thread.sleep(2000);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
if (Gdx.input.justTouched()) {
gameState = 1;
startGame();
score =0;
scoringTube = 0;
velocity = 0;
}
}
With this code the problem is that even the gameover image is being delayed and the previous problem is still occuring but now with a delay.I basically need a way so that justTouched method becomes inactive for a while when the game over screen is there.Please help.
I really wouldn't recommend using Thread.sleep; instead, you could try to use a boolean that is changed once the game ended, and prevent the method from executing in that case. Combine that with e.g a Timer that resets it after a fixed delay, and you should have the solution to your problem.
Example Usage for the timer:
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
//execute code here (change boolean state)
}
},
yourDelayHere
);
I am writing a board game where I need to check for enemies on the the player is moving and prompt to make an attack. However because of how the game is made the move method is called on the JavaFX application thread, I want to be able to prompt the user if they want to fight an enemy.
My dialogue box which works fine normal uses wait and notify which dont work on the main thread without crashing the program, does anyone know how to pause the execution of this thread until a user clicks one of the buttons.
I apologise for the description, its late.
Method that checks for enemies
This method checks for the enemies and returns the enemy if the user selected yes. It runs on the JavaFX Thread.
private Ship pathBattle(Vector gridPosition){
//Check if there are ships on the path to the destination
for(Button cell : activeButtons){
//Check the button position is not that of the current button, dont go past the current button
Vector pixPosition = new Vector(cell.getLayoutX(), cell.getLayoutY());
//Convert to a grid referance
Vector gridPos = Vector.pixToGrid(pixPosition);
if(!gridPos.equals(gridPosition)){
//This button is not the destination
//Check for any ships on that cell
Ship collided = Ship.isOccupiedButton(cell);//Returns the ship if there is one on the cell
if(collided != null){
//There is a ship, prompt to battle
boolean battle = UtilPopups.showConfirmationDialog("Do you want to battle " + collided.getName(), "YAR!", "NAY!");
Game.printError("Ship collision");
if(battle){
return collided; //Return the ship to battle
}
}
}
//On to the next button
}
return null;
}
The code to display the Popup
This does work in other areas of the porgram with no issue
public static boolean showConfirmationDialog(String lblPrompt, String btnYes, String btnNo){
//Check if the confirmation controller is not null
if(confirmationDialog != null){
confirmationDialog.lbl_prompt.setText(lblPrompt);
confirmationDialog.btn_no.setText(btnNo);
confirmationDialog.btn_yes.setText(btnYes);
//Show the base
show(confirmationDialog.base_pane);
//Pause this thread until user input is given from GUI thread
synchronized (confirmationDialog) {
try{
confirmationDialog.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
//Program resumed, user input given and stored in response
if(confirmationDialog.response.equals("yes")){
confirmationDialog.response = null; //Set back to null
return true;
} else {
confirmationDialog.response = null; //Set back to null
return false;
}
}
//Class not initialized
Game.printError("UtilPopups->showConfirmationDialog: Dialog NULL!");
return false;
}
For blocking any caller thread during showing dialog, for example:
static private void showAndBlock(Dialog dialog) {
if (Platform.isFxApplicationThread()) {
dialog.showAndWait();
} else {
CountDownLatch lock = new CountDownLatch(1);
Platform.runLater(() -> {
dialog.showAndWait();
lock.countDown();
});
try {
lock.await();
} catch (InterruptedException e) {
// Just in case you call yourTherad.interrupt(),
// the thread will be joined forcibly through here.
}
}
}
But I'm not sure whether or not it works in your threading design.
The collided method literally doesnt nothing, it doesn't ask for a input so it can't work.
I think, sorry not good english
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());
}
}
}
}