I am making an choose-your-own-adventure project with multiple outcomes. One of the things I'm trying to do is incorporate quick time events (QTEs), or those events in games where you have to, say, click a button within a short time limit or you get killed. In this case, I'm trying to make it so that when the time comes, the game says "Suddenly you hear the clopping of hooves coming from your right!", at which point a method is called up that make a GUI pop up with a button, and you only have a few seconds to click the button. If you click the button within the proper amount of time, it should continue with the game (starts with 'CRACK!!'). If not, the game should be over, and a message is printed ('GAME OVER - You were slain by Minotaur Prison Guard. However, when I run it, the GUI doesn't pop up, the Game Over message prints three times with the sleepLines delay, and then the 'CRACK!! You quickly turn...' message appears. Here is my code:
import java.util.Scanner;
import java.awt.Container;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class KCP1Main extends JPanel{
public static JButton AttackButton;
static Scanner myScanner = new Scanner(System.in);
static int quicktimecompletion = 0;
public static void main(String[] args) throws InterruptedException {
//insert leading up to this point here
System.out.println("Suddenly, you hear the clomping of hooves to your right!");
QuickTimeEvent(args);
sleepLines(500,2);
System.out.println("CRACK!!");
sleepLines(1000,1);
scrollText("You quickly turn right, swinging your metal pole at the same time... it swings into the head of ");
scrollText("a minotaur that was charging towards you, with enough force to knock him out.");
}
}
public static void QuickTimeEvent(String[] args) throws InterruptedException
{
for(int timer=0 ; timer<3 ; timer++){
ButtonHandler handler = new ButtonHandler();
AttackButton = new JButton("<!");
//static add(AttackButton);
AttackButton.addActionListener(handler);
sleepLines(1000,1);
if(quicktimecompletion == 1)
{
break;
}
if(timer > 3);
{
System.out.println("GAME OVER");
System.out.printf("You were slain by a Minotaur Prison Guard.");
}
}
}
public static class ButtonHandler implements ActionListener
{
public void actionPerformed (ActionEvent event)
{
if (event.getSource() == AttackButton)
{
quicktimecompletion = 1;
}
}
}
public static void scrollText(String message) throws InterruptedException{
for(int i = 0; i < message.length(); i++)
{
System.out.print(message.charAt(i));
Thread.sleep(62);
}
System.out.print("\n");
}
public static void JPanel(String[] args){
JFrame theGUI = new JFrame();
theGUI.setTitle("Incoming!");
KCP1Main makeButtons = new KCP1Main();
theGUI.add(makeButtons);
theGUI.setSize(300, 200);
theGUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theGUI.setBackground(Color.red);
Container pane = theGUI.getContentPane();
theGUI.setVisible(true);
}
public static void sleepLines(int duration, int lines) throws InterruptedException{
for(int i=0; i < lines; i++){
Thread.sleep(duration);
System.out.println("");
}
}
}
Let's start with the fact that Swing is single threaded, this means that any action which blocks Event Dispatching Thread (like Thread.sleep) will prevent it from processing any new events (including paint events) until it unblocks.
Two golden rules...
Don't block the EDT
Don't update the UI from outside the EDT
In your case, you could achieve your desired result by making use of a Swing Timer
See How to use Swing Timers for more details.
In the back of my head, I'd consider probably making some kind of QTE class, which took the amount of time, a reference to the button the user needs to click and possible some kind of listener/observer/callback.
The QTE would then attached an ActionListener to the button, that if triggered, would stop the Timer and notify the observer of success. Otherwise if the Timer triggered first, it'd notify the observer of failure.
As an idea
Related
I am making a basic java program for the raspberry pi that prints, when a button is pressed, the button number and that its pressed. my 2 code classes are below but as you can see from the output, sometimes it shows the button is pressed twice for one push.
import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.Pin;
import com.pi4j.io.gpio.PinPullResistance;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.event.GpioPinListenerDigital;
import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
public class Button {
private GpioPinDigitalInput buttonPin;
boolean pressed = false;
public Button(GpioController gpio, Pin pin) {
// Provision the pin
buttonPin = gpio.provisionDigitalInputPin(pin, PinPullResistance.PULL_UP);
// Add a listener to the button
buttonPin.addListener(new GpioPinListenerDigital() {
#Override
public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
if(event.getState().isLow()) {
pressed = true;
// When Button is not pressed
GPIOTest.print(event.getPin() + ", pressed");
}
}
});
}
}
class 2
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.Pin;
import com.pi4j.io.gpio.RaspiPin;
import com.pi4j.util.Console;
public class GPIOTest {
private final static Console console = new Console();
private final static GpioController gpio = GpioFactory.getInstance();
private static Pin[] pinList = { RaspiPin.GPIO_00,
RaspiPin.GPIO_01,
RaspiPin.GPIO_02,
RaspiPin.GPIO_03,
RaspiPin.GPIO_04};
public static void main(String args[]) throws InterruptedException {
console.title("Test GPIO");
console.promptForExit();
for (Pin pin : pinList) {
new Button(gpio, pin);
}
console.waitForExit();
}
public static void print(String message) {
console.println(message);
}
}
Likely to be a debounce issue. The button press is likely to be grounded, pulled high, and re-grounded several times when the button is pressed before the switch settles in to the grounded state.
There are several common debounce algorithms. You could read the state several times after the first change and only report "pressed" when several "0" in a row are reported after a very short delay. There are more sophisticated algorithms if you Google around.
Good luck.
I'm trying to write a program that plays musical chords. I'd like to add a window that shows a progress bar displaying the time that the chords play for and how much they have completed. To play the chords, I've been using a slightly modified version of the StdAudio class. So far, I have the following code to be run when I ask a chord to play.
public static void playNotes(double[] frequencies, double duration, double amplitude)
{
PlayAudioGUI g = new PlayAudioGUI(duration);
g.run();
amp = amplitude;
ArrayList<double[]> chord = new ArrayList<double[]>();
for(double freq : frequencies) {
double[] note = StdAudio.tone(freq, duration);
chord.add(note);
}
double[] chordCombined = new double[chord.get(0).length];
for (int i = 0; i < chordCombined.length; i++) {
for (double[] note : chord) {
chordCombined[i] += note[i];
}
chordCombined[i] /= chord.size();
}
StdAudio.play(chordCombined);
}
I've never attempted multithreading before, so I don't know what I'm doing wrong. When I run the code, It shows an empty window while it plays the chord, then afterwards displays the window properly. I'd like for it to display the window at the same time as playing the audio.
Here is my code for the window's class.
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.Timer;
public class PlayAudioGUI implements Runnable {
private JFrame window;
private JProgressBar prog;
private double duration;
private Timer t;
class TimerListener implements ActionListener {
// This runs every few milliseconds, depending on the delay set below
public void actionPerformed(ActionEvent event) {
prog.setValue(prog.getValue() + 1);
// Stop the timer and hide the window when the progress bar
// completes
if (prog.getValue() == prog.getMaximum()) {
t.stop();
window.setVisible(false);
}
}
}
public PlayAudioGUI(double duration) {
this.window = new JFrame("Playing audio...");
this.duration = duration;
}
#Override
public void run() {
// Setting up gridbag layout. I will add more components later.
Container pane = this.window.getContentPane();
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(30, 30, 30, 30);
// Display the approximate duration
String clippedDuration;
if (Double.toString(duration).length() > 5) {
clippedDuration = Double.toString(duration).substring(0, 4);
} else {
clippedDuration = Double.toString(duration);
}
String message = "Playing audio for " + clippedDuration + " seconds";
pane.add(new JLabel(message), c);
// Make a progressbar
c.gridy = 1;
this.prog = new JProgressBar();
this.prog.setMinimum(0);
this.prog.setMaximum(250);
pane.add(this.prog, c);
// More window management stuff
this.window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.window.pack();
this.window.setVisible(true);
// Set up the timer
ActionListener listener = new TimerListener();
final int DELAY = (int) (4 * this.duration); // This works, I did the
// math :)
t = new Timer(DELAY, listener);
t.start();
}
}
Thanks for your help.
Suggestions:
The new dependent dialog window should be just that, a dialog such as a JDialog, not a new JFrame which is creating a whole separate application.
You know that you should be doing your sound creation in a background thread, and your blank screen is being caused by just this problem, and yet I see no thread creation in your code -- why?
Myself, I'd not use a Swing Timer, and poll data, but rather do all within a SwingWorker, within the SwingWorker's doInBackground method I'd update its progress state, and I'd add a PropertyChangeListener to the SwingWorker and monitor this state.
As an aside, you will almost never want to create a Runnable class and then call its run() method. If you're creating the Runnable to allow it to run in a background thread, then you'd likely place it into a Thread and then call start() on the Thread. Since your code above should run on the Swing event thread, then if it is not being called from this thread, it should be queued on to it via SwingUtilities.invokeLater(myRunnable);
I'm not sure how you can get a progress value from your StdAudio library. If there's a way, then use it to set the SwingWorker's progress state via its setProgress(...) method. If not, then you could guess, I suppose or you may be better off using an indeterminate progress bar. I believe JProgressBar has a method called setIndeterminate(true) that would work for this.
I am writing the Sugarscape simulation in Java and need a working GUI. Sugarscape is a spatial landscape consisting of tiles (of sugar), and agents moving and consuming sugar. For simplicity, I have only one agent and no sugar- I just want to see the agent moving.
For the past 2 weeks I have read into painting in java, concurrency in java, concurrency in swing, I have read filthy rich clients and countless StackOverflow threads, but I must resort to asking a question here.
I need my model separate from the GUI. This presents a problem since 99% of tutorials suggest to call for repaint within other methods. My idea was to run one "tick" of the simulation: all agents move, and then send an Event (my GUI class extends Observer) which then triggers a repaint(); request and update the GUI. However the problem (the misunderstanding) lies with the SwingUtilities.InvokeLater method. My code is:
public void setupGUI()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
System.out.println("GUI is being setup, on EDT now? " + SwingUtilities.isEventDispatchThread());
SugarFrame frame = new SugarFrame(simulation.getWorld());
frame.setVisible(true);
}
});
}
For understanding what is happening I have inserted println's everywhere. The order of events is what confuses me:
Console output:
1.Agent created. Starting Position: X= 19 Y= 46 // This is in the Agent constructor
2.Simulation start. Experiment number: 0
GUI is being setup, on EDT now? true // As you see above, this is WITHIN the SwingUtilities.InvokeLater section. But then the EDT pauses and the real model continues:
Tick number 0
Invoke Agent Actions, fire TickStart Event
TickStartEvent created
Invoke Agent Actions, for-loop starting now
Agent number 0 moving now:
Consuming Sugar now.
Moving now.
Sleeping now.
The Sugarframe has been created and Grid added. All on EDT? true // And there it is back again. The paint component follows and the window with the Agent visible appears.
paintComponent called, on EDT? true
Now, I have read that by putting the main thread to sleep, you give the EDT time to run the repaint. However, this only happens once. Repaint is never called again, and I only ever see one iteration of the model.
I simply do not understand what piece of information I am missing to work with the EDT properly. Swingworker and Swingtimer are suggested regularly, but for every suggestion there is a notion that they are not needed for a model such as mine. Either paintComponent is not called at all, or queued up until the end (and then still not repainting, even if I use thread.sleep).
I'd appreciate any help. Apologies for the long post.
//Edit: as per request some more code.
The entire main method:
public class SimulationController {
static Simulation simulation;
public static final int NUM_EXPERIMENTS = 1;
public SimulationController()
{
Random prng = new Random();
SimulationController.simulation = new Simulation(prng);
}
public void run() {
setupGUI();
for(int i=0; i<NUM_EXPERIMENTS; i++) {
System.out.println("Simulation start. Experiment number: " + i);
simulation.getWorld().addObserver(simulation);
simulation.addObserver(simulation.getWorld());
simulation.run();
}
}
public void setupGUI()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
System.out.println("GUI is being setup, on EDT now? " + SwingUtilities.isEventDispatchThread());
SugarFrame frame = new SugarFrame(simulation.getWorld());
frame.setVisible(true);
}
});
}
public static void main(String[] args) {
SimulationController controller = new SimulationController();
controller.run();
}
}
The paint override in my JPanel class:
#Override
public void paintComponent(Graphics g) {
System.out.println(">>>>>>>>paintComponent called, on EDT? " + SwingUtilities.isEventDispatchThread()+"<<<<<<<<<<");
super.paintComponent(g);
//g.clearRect(0, 0, getWidth(), getHeight());
rectWidth = getWidth() / world.getSizeX();
rectHeight = getHeight() / world.getSizeY();
for (int i = 0; i < world.getSizeX(); i++)
{
for (int j = 0; j < world.getSizeY(); j++)
{
// Upper left corner of this terrain rect
x = i * rectWidth;
y = j * rectHeight;
Tile tile = world.getTile(new Position(i, j));
if (tile.hasAgent())
{
g.setColor(Color.red);
} else
{
g.setColor(Color.black);
}
g.fillRect(x, y, rectWidth, rectHeight);
}
}
}
JPanel class again, update methods:
public void update(Observable o, Object arg)
{
if (arg instanceof TickEnd)
{
TickEvent tickEndevent = new TickEvent();
this.addTickEvent(tickEndevent);
}
}
}
private final BlockingQueue<TickEvent> TICK_EVENTS = new LinkedBlockingQueue<TickEvent>();
/**Runnable object that updates the GUI (I think)**/
private final Runnable processEventsRunnable = new Runnable()
{
public void run()
{
TickEvent event = new TickEvent();
while ((event = TICK_EVENTS.poll()) != null)
{
System.out.println("This is within processEventsRunnable, inside the While loop. Repaint is called now.");
repaint();
}
}
};
/**Add Event to the processing-Events-queue**/
public void addTickEvent(TickEvent event)
{
//System.out.println("This is in the Add TickEvent method, but before the adding. "+TICK_EVENTS.toString());
TICK_EVENTS.add(event);
System.out.println("TickEvent has been added! "+TICK_EVENTS.toString() + "On EDT?" + SwingUtilities.isEventDispatchThread());
if (TICK_EVENTS.size() >= 1)
{
SwingUtilities.invokeLater(processEventsRunnable);
}
}
And last but not least, the JFrame constructor:
/** Sugarframe Constructor**/
public SugarFrame(World world)
{
super("Sugarscape"); // creates frame, the constructor uses a string argument for the frame title
grid = new Grid(world); // variable is declared in the class
add(grid);
setDefaultCloseOperation(EXIT_ON_CLOSE); // specifies what happens when user closes the frame. exit_on_close means the program will stop
this.setContentPane(grid);
this.getContentPane().setPreferredSize(new Dimension(500, 500));
this.pack(); // resizes frame to its content sizes (rather than fixed height/width)
System.out.println("The Sugarframe has been created and Grid added. All on EDT? "+ SwingUtilities.isEventDispatchThread());
this.setVisible(true); // makes the Frame appear on screen
}
The sentences,
I need my model separate from the GUI. This presents a problem since 99% of tutorials suggest to call for repaint within other methods.
and
Now, I have read that by putting the main thread to sleep, you give the EDT time to run the repaint.
don't sound quite right to me, so I'll try to clear things up a bit and maybe If you reevaluate the fundamental ideas you had behind those statements you can find the piece of information that you were missing.
First of all, always keep in mind this scheduling model that we were talking about. You can not say "EDT do this for me now!". It is always "EDT here's one more task you need to do, do it when you are done with whatever you are doing". So the EDT has a queue of "tasks" to do and goes through it consuming one by one.
These tasks are usually created by events: pressing a button gives the EDT a task to do, when the state of a component of the GUI changes some listeners may be notified and enqueue some work in the EDT. However, you can also straight up say "EDT execute this piece of code, later". This is what you do with invokeLater, you schedule a work to do in the EDT whenever it's free. Even if you call invokeLater from the EDT the task is scheduled, not executed at the moment.
The same happens with invokeAndWait yes, the code is executed sequentially as if it was executed at the moment, but it is still an scheduled work. So repaint() is no exception to this. repaint() doesn't repaint the GUI, but rather schedules the repainting of the GUI.
However repaint() is exceptional in the sense that it can be called from outside the EDT! This is not surprising now that we know that the only thing that does is scheduling a certain work, it does not actually mess with the GUI so you can call it wherever you want.
This means that the line
SwingUtilities.invokeLater(processEventsRunnable);
where processEventsRunnable basically executes a repaint() is meaningless and the whole tick system overly complex and unnecesary. You just have to call repaint() when you change something on the GUI or on the data that the GUI feeds on so the changes are reflected on the screen.
Furthermore, if you wanted to do something that needs to be executed in the EDT (like changing the text of a Label with the score) you can just put that code in an invokeLater block in your main thread. That will queue and execute the task properly, you don't need to do your own event queue system.
Keeping all this in mind the following makes no sense:
I have read that by putting the main thread to sleep, you give the EDT time to run the repaint
The GUI will be updated on its own shortly after you call repaint(). The main doing a lot of things and calling a lot of repaints does not prevent the GUI from being updated. However, if you want to "sleep" the main so the pace of the changes is slow so the user can appreciate it on the screen, you should use a timer.
So, as long as your main is not accessing GUI values and methods, feel free to call repaint whenever you are done changing the data, periodically or not.
Edit: Also it sounds a little bit weird that you have a main thread doing things. As you read in the concurrency chapter, usually you just create the GUI in the EDT and then the application is mostly event-driven when buttons are pressed and such. If you need to do changes periodically use a timer. You can use auxiliar threads to do specific non-GUI related heavy work, like reading a file. But you don't usually have an auxiliar thread permanently active as part of the design.
The following is a very simple program that moves an square periodically. I just use a timer to change the data and call repaint(). Note that I'm using a SwingTimer (it is executed in the EDT) since I wanted to check the panel width. Otherwise I could run the code of the timer in any thread.
In your case you probably have your "map" stored independently of the GUI, so you just need to check that data to properly move the coordinates of the agent whenever you want (on keyboard press, periodically...).
It looks like this:
Full code:
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingSquareTest
{
int x, y, size, step;
MyPanel panel;
Timer timer;
public static final void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
MovingSquareTest app = new MovingSquareTest();
app.createAndShowGUI();
app.timer.start();
}
});
}
public MovingSquareTest()
{
x = 0;
y = 150;
size = 50;
step = 50;
timer = new Timer(500, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
x += step;
if (x < 0) x = 0;
if (x + size > panel.getWidth()) x = panel.getWidth() - size;
if (x == 0 || x + size == panel.getWidth()) step *= -1;
panel.repaint();
}
});
}
public void createAndShowGUI()
{
JFrame frame = new JFrame("Dance, my square!");
panel = new MyPanel();
frame.add(panel);
frame.setSize(600, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private class MyPanel extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawRect(x, y, size, size);
}
}
}
So I want to make a program that constantly keeps track of time while other functions of the code can run. For my code specifically, I have 4 JButtons and 4 JLabels. Once I click a JButton, I want the corresponding JLabel to update its text every couple of seconds (let's say 5 seconds). But while the code running, I want to be able to select a different button at any given time to update its corresponding JLabel and stop the one before. I've tried a few things, but every time I run into two problems:
The JLabel doesn't update every 5 seconds.
I can't click a different JButton while the another JButton is clicked.
They both have to do with the fact that my code goes through an infinite loop once a JButton is clicked, but I'm not sure how I can go around doing this.
I'm new to coding so my knowledge of the terminology is quite limited. Any help will be appreciated.
Here's the code for what happens when the button is clicked. (I changed the code a few times here and there to try to fix on my own, but couldn't so currently it just gives me a stackoverflow error.) :
package maingame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JLabel;
public class StatButtonListener implements ActionListener
{
private JButton _button;
private ArrayList<JLabel> _jlList;
private static String _clicked;
public StatButtonListener(JButton button, ArrayList<JLabel> jlList)
{
_button = button;
_jlList = jlList;
}
#Override
public void actionPerformed(ActionEvent arg0)
{
_clicked = _button.getText();
Long value = null;
JLabel valueLabel = null;
// to stop an infinite loop
for(int i=0; i < 8; i++)
{
if(_button.getText().equals(_jlList.get(i).getText()))
{
valueLabel = _jlList.get(i+1);
value = Long.parseLong(valueLabel.getText());
}
}
double startTime = System.currentTimeMillis()/1000;
updateStat(value, valueLabel, startTime);
}
public void updateStat(Long value, JLabel valueLabel, double startTime)
{
if(!(value == null || valueLabel == null))
{
if(_clicked.equals(_button.getText()))
{
double endTime=System.currentTimeMillis()/1000;
if((endTime-startTime) >= 1)
{
startTime = System.currentTimeMillis()/1000;
value = value + 3;
valueLabel.setText(value.toString());
}
updateStat(value, valueLabel, startTime);
}
}
}
}
You can do this using the aforementioned Swing-Timers:
http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
This invokes actionPerformed() once its done. You can then update whatever you want to update and restart() it.
Or you can do this using a new Thread:
http://de.wikibooks.org/wiki/Java_Standard:_Threads
This runs completely on its own and does not block the main program - and might need a handle of the objects you want to change.
my final goal for this application is to animate several items in the same JPanel at a different speed using a thread for each item.the first part is done however the items move at the same speed and i have no idea on how to fix this problem.
package javagamestutos;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;
public class Board extends JPanel implements Runnable {
private Star star;
private Thread animator;
ArrayList<Star> items=new ArrayList<Star>();
public Board() {
setBackground(Color.BLACK);
setDoubleBuffered(true);
star=new Star(25,0,0);
Star star2=new Star(50,20,25);
items.add(star2);
items.add(star);
}
public void addNotify() {
super.addNotify();
animator = new Thread(this);
animator.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
for (Star s : this.items) {
g2d.drawImage(s.starImage, s.x, s.y, this);
}
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
public void run() {
while(true){
try {
for (Star s : this.items) {
s.move();
}
repaint();
Thread.sleep(star.delay);
} catch (InterruptedException ex) {
Logger.getLogger(Board.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
here is the star class wich is the moving item.
package javagamestutos;
import java.awt.Image;
import javax.swing.ImageIcon;
/**
*
* #author fenec
*/
public class Star {
Image starImage;
int x,y;
int destinationX=200,destinationY=226;
boolean lockY=true;
int delay;
public Star(int delay,int initialX,int initialY){
ImageIcon ii = new ImageIcon(this.getClass().getResource("star.png"));
starImage = ii.getImage();
x=initialX;
y=initialY;
this.delay=delay;
}
void moveToX(int destX){
this.x += 1;
}
boolean validDestinatonX(){
if(this.x==this.destinationX){
this.lockY=false;
return true;
}
else
return false;
}
void moveToY(int destY){
this.y += 1;
}
boolean validDestinatonY(){
if(this.y==this.destinationY)
return true;
else
return false;
}
void move(){
if(!this.validDestinatonX() )
x+=1;
if(!this.validDestinatonY() && !this.lockY)
y+=1;
/*if(!this.validDestinatonY())
y+=1;
*/
}
}
and here is the skeleton of the animation that extends a JFrame :
package javagamestutos;
import javax.swing.JFrame;
public class Skeleton extends JFrame {
public Skeleton() {
add(new Board());
setTitle("Stars");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(300, 280);
setLocationRelativeTo(null);
setVisible(true);
setResizable(false);
}
public static void main(String[] args) {
new Skeleton();
}
}
do you have any idea how to achieve my goals?am i using threads proprely?
thank you in advance.
That's because you're invoking the "move" method at a fixed rate specified by the delay of the first "start"
Thread.sleep(star.delay);
So if you move them a little every "n" milliseconds, they will seems to move at the same peace.
If you want them to move at different speed, you have to move them in different thread ( you are using only one now ) Bear in mind the comment by omry,
EDIT
I did something similar just recently
I have two different things so animate so I have two timers ( timers use threads underneath, but they can repeat the execution code every fixed rate ).
The first apply the text to a JLabel every second ( 1000 ms )
final Timer timer = new Timer();
timer.scheduleAtFixedRate( new TimerTask() {
public void run(){
setText();
}
}, 0, 1000 );
And other change the displaying image every 10 seconds ( 10,000 ms )
final Timer imageTimer = new Timer();
imageTimer.scheduleAtFixedRate( new TimerTask() {
public void run() {
setImage();
}
}, 0, 10000 );
I have a video of the result here:
For more advanced ( and nice ) time management you MUST take a look at the "Timing Framework" project which adds additional capabilities to timers.
You should be painting in the AWTDispatchThread. To do that you will want to use something like SwingUtilities.invokeLater(Runnable); This applies not only to your animation, but to the creation and setting visible of your JFrame as well. Failing to do this could result in deadlocks with the painting thread. Also, when moving your painting operations into the SwingUtilites methods, you will not want to include any while(true) loops, as that will commandeer your painting thread.
Generally Swing components should be used from the AWT Event Dispatch Thread (EDT). repaint is one of the methods that is supposedly okay to use off EDT. However, your Star is not and should not be thread-safe.
The easiest approach is to go for EDT-only (at least to start with). Instead of using Thread use javax.swing.Timer which fires on the EDT.
Misc comments: There should be no need for your paint method to dispose of the graphics object sent to it, or for it to sync using Toolkit. The component need not be set to double-buffered, but should be set opaque (JPanel is not guaranteed to be opaque). You should just extend JComponent instead of JPanel, as this is not a panel. It's generally not a great idea for outer classes to implement Runnable. Prefer private variables.
I would suggest you take a look at the open source library trident which does just that, its author, Kirill Grouchnikov is well-known in the Swing world (he is the author of the famous Substance look & feel).
Trident should help you solve the problem of having different objects move at different speeds, without having to create one thread per object (which is a problem in the end).
if you are sure you want to paint in the threads, you can use :
update(getGraphics());
instead of repaint.
this is generally considered bad practice, as you normally paint stuff in the AWT thread.