I have a problem with my java game. I’m beginner, but i have to write it as a school project.
Game is called „Birthday Cake” there is 7 candles on the cake and randomly one of it is showing for let say 30s and during this time u have to click on it to get point, if u don’t click on it during this time next candle will show. Game end when 10 candle shows.
I made for loop and i tried to make it work for sooo long that I’m dying from frustration
my for loop works but it is so fast that i use Thread.sleep(1000), i tried lots of solutions it looks ok. BUT when i start my game nothing is happening and after few seconds all 7 candles shows and quickly disappear. I think I’m doing something wrong, but i have no idea what.
if(Dane.start){
int liczbaLosowa = 0;
for(int i=0; i<10 ;i++){
liczbaLosowa = (int)(Math.random()*7);
this.wspX= wspX_p[liczbaLosowa];
this.wspY= wspY_p[liczbaLosowa];
g2d.drawImage(plomienImg, wspX, wspY,null);
Toolkit.getDefaultToolkit().sync();
try {
Thread.sleep(1000);
} catch (Exception ex) { }
//repaint();
}
Dane.start=false;
}
this loop is inside JPanel paintComponent...
Never,
Never,
NEVER
call Thread.sleep(...) inside of paintComponent ever. Please understand that this method largely determines the perceived responsiveness of your program and anything that slows it down or freezes it will severely slow down and freeze your GUI. In fact you should never call Thread.sleep inside the code of most Swing programs (all that runs on the Swing event thread) but doing so in paintComponent is an even worse sin. The solution is to use a Swing Timer, and put code that you want to be called repeatedly at regular intervals inside of the Timer's ActionListener's actionPerformed code. Within this method, change the value held by fields within your class, for instance wspX and wspY, call repaint(), and then use those fields inside of paintComponent to determine what gets painted where.
Thread.sleep() is a bad call which can lead into many problems. i was told to never use it. instead i will show you the way i do my game loops. it might not be the perfect game loop but it is good.
i recommand implemets runnable and putting your loop in your run method.
public void run(){
init(); //initialisation of images, sound..etc. will be executed once only
int fps = 60 //number of update per second.
double tickPerSecond = 1000000000/fps;
double delta = 0;
long now;
long lastTime = System.nanoTime();
while(running){
now = System.nanoTime();
delta += (now - lastTime)/tickPerSecond;
lastTime = now;
if(delta >= 1){
tick();
render();
delta--;
}
}
}
private void init(){
//initialisation image, sound, loading world, generate maps....etc
}
private void tick(){
//tick player, world, entities..etc
}
private void render(){
//render graphics.
}
also dont forget to create start and stop method for the thread. you can change the fps to what number you would like, no need to go higher than 60.
Related
I've been working on this really basic Java RPG game, and when I tried using Thread.sleep for my monster to move slower, it seems to affect the player as well, in other words, the movement of the player had slow down when the monster appears, but turns back to normal when I move to another part of the map where the monster is not there
Here's the code for the monster's movement
int wolfRandNum;
if (isKilled() == false){
wolfRandNum = (int) (Math.random()*4);
if (wolfRandNum == 1){ // up
System.out.println("up");
if (playerRow -1 <= 0){
setCurrentEnemySprite(3);
} else {
moveUp();
playerRow--;
}
} else if (wolfRandNum == 2){ // down
System.out.println("down");
if (playerRow +1 >= 8){
setCurrentEnemySprite(0);
} else {
moveDown();
playerRow++;
}
} else if (wolfRandNum == 3){ // left
System.out.println("left");
if (playerColumn -1 <= 0){
setCurrentEnemySprite(1);
} else {
moveLeft();
playerColumn--;
}
} else{ // right
System.out.println("right");
if (playerColumn + 1 >= 9){
setCurrentEnemySprite(2);
} else {
moveRight();
playerColumn++;
}
}
}
The code above is in a method called update, which is from an enemy class.
Here's the where I call the method:
public void paintComponent(Graphics g) {
super.paintComponent(g); //required to ensure the panel is correctly redrawn
map.draw(g);
player.draw(g);
for (Character character : characterList) {
character.draw(g);
}
for (Enemy enemy : enemyList){
enemy.draw(g);
try {
enemy.update();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (Item item : bagList) {
item.draw(g);
}
repaint();
}
Is there anyway to use thread.sleep without affecting the player's movement? If not, what other methods can I use to make the monster move slower but not the player?
I'm not really sure how to fix this problem, any advise would help a lot! Thank you :)
This is a event-driven Swing GUI, and so the answer to the question:
How to use Thread.sleep(...)
is that you don't.
You're coding this as if it were a linear console program, and this design won't work for event-driven non-linear programs. Calling Thread.sleep on the Swing event thread and especially in any painting method is a guarantee to put your entire GUI to sleep so that it becomes totally unresponsive. The proper solution is to create a non-GUI Model class to go with your GUI (your "View"), and to change how the view responds based on the state of the Model, i.e., the state of its key fields.
Also, if your game is being run in "real time", then you will need a game loop to drive your game. This can be done easily via a Swing Timer, although it does not provide absolute precision. If greater precision is required, then use the Swing Timer, but don't assume that each time slice is accurate, and instead measure each delta time and base your physics on the measured slice, not the assumed slice. Other ways to create a game loop include use of a background thread with a while loop and Thread.sleep within this thread.
All you are slowing down is the rendering (drawing) of your monster. As all drawing is done on the UI thread, this will, as you have rightly noticed, have a huge performance impact on your entire application. As a general rule, you should never call a blocking operation such as sleep in the UI thread.
Given you only have two characters, what I would consider trying is creating two threads, one for your character and one for your monster. You will do what you need to do in each of these threads, and then call repaint() when you need to redraw. If you want to slow down the monster, for example, you can call sleep in the monster thread.
I'm a beginner in Java programming and have come across an issue (probably an easy one to solve).
I am experimenting with Java GUI and wish to create a window in which the colours of an array are cycled through until there are no more colours. I believe I can do this using a for loop and cycling through the array, however I do not know how to loop through the background colour.
Any help and explanation would be appreciated.
public void flashColor() {
Color [] color = { Color.red,Color.orange,Color.green };
int i = 0;
for(i=0;i<color.length;i--){
getContentPane().setBackground(Color(i));
}
}
This line tells me:
getContentPane().setBackground(Color(i));
that yours appears to be a Swing GUI (a key bit of information that you left out of your question!), and so you need to take Swing threading into consideration. Your current code will in fact loop through all the colors, but it will do so immediately, and on the Swing thread so that the GUI will have no way to paint any of the colors other than the last one. The solution: use a Swing Timer and not a for loop. Inside the timer advance an index int variable and use it to show the color.
Something like:
getContentPane().setBackground(colorArray[0]);
int delay = 1000; // for 1 second
Timer myTimer = new Timer(delay, new ActionListener() {
int index = 0;
public void actionPerformed(ActionEvent e) {
index++;
if (index >= colorArray.length) {
((Timer)e.getSource()).stop(); // stop the timer
} else {
getContentPane().setBackground(colorArray[index]);
}
}
});
myTimer.start();
The code has not been tested, and you'll want to read the Swing Timer tutorial for the details.
Note the key here is that yes you need to loop, and to pause (so that the color can be seen) but you need to do your looping and pausing in a thread thread is off of the Swing event dispatch thread (or EDT). Yes you could do this using a SwingWorker, but that is a way more difficult way to do this. It is far easier to use a Swing Timer to do this for you. Note that it uses a background thread invisibly for you.
I'm attempting to "animate" a die roll in Java. I currently have an icon (called "diceImage") set up that, when a button is clicked (called "diceRoll"), updates to a new random image of a die face. What I would like to do is have it change image (to a random die face) numerous times over a couple of seconds before stopping on a final image.
The problem I have is not with generating a random number, or rolling it numerous times, it's with the updating the image numerous times within a loop. The code below, which rolls the die 10 times is what I have so far:
private void diceRollActionPerformed(java.awt.event.ActionEvent evt) {
for (int i = 1; i <= 10; i++) {
rollDice();
pause(100);
}
}
This links to the following two methods (the first of which generates the random number, and sets the icon image):
private void rollDice() {
Random r = new Random();
int randomNumber = r.nextInt(6) + 1;
diceImage.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Game/Images/Dice " + randomNumber + ".png")));
}
The method below is "supposed" to pause the programme briefly between updating the image (this was taken from a programming course I was on where we had to animate an image of a car moving across the screen):
private void pause(int sleepTime) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
System.exit(-1);
}
}
All this programme seems to do is pause and then print the final dice roll. It doesn't show any of the "intermediate" faces. Does anyone have any ideas on why this isn't working?
Any help is greatly appreciated.
This question is asked several times a day. If you sleep in the event dispatch thread, you're preventing it from doing its job: react to events and repaint the screen.
Your animation should be done in another thread. Read the tutorial about concurrency in Swing, and use a Swing Timer.
The pause() you are using is when u need to animate moving stuff like a car for example , but just changing the icon of a JLabel which is the dice here doesn't need pause ( unless you just want it to be delayed a bit) ...
but anyways , to solve your issue , you need to call repaint() on that JLabel or updateGraphics(). Setting the Icon for the JLabel doesn't make the new Icon get displayed , you just need to repaint() it .
of course like JB Nizet said , for the app not to hang you need to call repaint on a new Thread .Thought you have to learn how to use Thread well cuz it can be very tricky at times .
good luck
I want to make an animation using java swing. I have a for loop that changes the location of an image slightly. My question is How can I make the each loop execution takes the specified amount of time?.
Its what I've done but I don't know how to use wait() and don't know is there a better way.
// inside of a MyJPanel class extending JPanel
for (int i = 0; i < FJframe.FRAMES; i++){
long start = System.currentTimeMillis();
animationFrame = i;
this.repaint();
long end = System.currentTimeMillis();
long remainder = (end - start);
System.out.println(remainder);
try {
this.wait(200 - remainder);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Edit
here my overrided Jpanel PaintComponent():
//some drawing
drawChanges(g, getDelta(), animationFrame, FJPanle.FRAMES);
And inside drawChanges(Graphics g, ArrayList deltas, int frame, int frames):
// a switch_case with cases similar to this one.
case AGENT_ATTACK:
// tu phase badi animation e kill
//drawImage(g2d, map[delta.getCell().x][delta.getCell().y], attacker);
source = map[delta.getSource().x][delta.getSource().y];
dest = map[delta.getDestination().x][delta.getDestination().y];
distanceX = dest.getCenter().x -
source.getCenter().x;
distanceY = dest.getCenter().y -
source.getCenter().y;
if (counter < frames / 2){
g2d.drawImage(ImageHolder.attacker, source.getBounds().x + (int)(((float)counter/frames) * distanceX),
source.getBounds().y + (int)(((float)counter/frames) * distanceY),
null);
}
else{
g2d.drawImage(ImageHolder.attacker, dest.getBounds().x - (int)(((float)counter/frames) * distanceX),
dest.getBounds().y - (int)(((float)counter/frames) * distanceY),
null);
}
break;
I want each loop takes, for example, exactly 200 miliseconds. How can I achieve this?
Look into using a Timer. For example, the scheduleAtFixedRate() method.
Probably not an acceptable answer, but too long for a comment:
There are several options, depending on the actual intention. The pattern that you described is not uncommon for a "simple game loop". In this case, a code that is similar to the one that you posted is run in an own thread, and regularly triggers a repaint() in order to paint the updated game state. In your case, it seems that only the animationFrame variable is increased. For such a simple action, the alternatives that have already been mentioned may be sufficient:
As suggested by whiskeyspider in https://stackoverflow.com/a/21860250/ : You could use a java.util.Timer with a TimerTask that only updated the animationFrame
Alternatively, you could use a javax.swing.Timer whose ActionListener updates the animationFrame. This might be advantageous here, because you can be sure that the update of the animationFrame happens on the event dispatch thread. Thus, the update of this variable can not interfere with its usage in the painting method, for example
But as mentioned in the comments: An own thread that executed the code that you already posted (with the minor adaptions) is also feasible and not "wrong" per se - but note that in this case (similar to when using a java.util.Timer) you might have to take care of the synchronization on your own
EDIT: Based on the edited question: Similar to what I expected, you are using the animationFrame in your painting method. The crucial point here are the details about how the animationFrame variable is used. For example, if your paintComponent method looks like this
class MyJPanel extends JPanel {
private int animationFrame = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawChanges(g, getDelta(), animationFrame, FJPanle.FRAMES);
drawSomethingElse(g, animationFrame, ...);
drawEvenMore(g, animationFrame, ...);
}
...
}
then it may happen that the value of animationFrame is changed (by another thread, possibly the java.util.Timer thread) while the paintComponent method is executed. That means that drawChanges and drawSomethingElse may receive different values for animationFrame. This may cause rendering artefacts (misplaced images, tearing between tiles etc).
This could either be avoided by using a javax.swing.Timer (because then, the updates of animationFrame will be done on the same thread as the one that executes paintComponent), or by making sure that all painting operations use the same value of animationFrame - roughly like this:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int animationFrameUsedForAllPainting = animationFrame;
drawChanges(g, getDelta(), animationFrameUsedForAllPainting , FJPanle.FRAMES);
drawSomethingElse(g, animationFrameUsedForAllPainting , ...);
drawEvenMore(g, animationFrameUsedForAllPainting , ...);
}
But apart from that, there is not sooo much difference between the aforementioned approaches in this case. So for simplicity, you could use the javax.swing.Timer (or the java.util.Timer when you make sure that the update is "thread safe" regarding the painting operations).
I'm having a problem I'm making a pool game and I need the ballos to react when I simulate a hit, the program works like this, you click the direction and power to hit the ball and the click go, the go button is in the GUI class where my labels are created, the button calls a method from my main class that recieves the parameter and then with a while in it, changes the X and Y of the ball till the power is reduced to 0 and then stops, the code is working, but the ball moves until the while stops. So the while works and when the power int is 0 the while goes out and then the new X,Y are painted.
This is the funcion that the button calls, the button sends all the parameters
public void golpe(int pbola, int pvelocidad, String pdireccion, JLabel[] listalabels) throws InterruptedException{
listabolas[pbola].setVelocidad(pvelocidad);
listabolas[pbola].setDireccion(pdireccion);
while (listabolas[pbola].getVelocidad() > 0) {
moverBola(pbola, listalabels);
//System.out.println(listabolas[pbola].getPosX());
//System.out.println(listabolas[pbola].getPosY());
Thread.sleep(500);
//This line is supposed to change the X and Y of the object over and over
//but only does it till the end
listalabels[pbola].setLocation(listabolas[pbola].getPosX(), listabolas[pbola].getPosY());
}
}
Here is the function moverbola(), only copied one "if" so that the code doesn't look to big
private void moverBola(int pbola, JLabel[] listalabels) {
if (listabolas[pbola].getDireccion().equals("SE")) {
int pposX = listabolas[pbola].getPosX();
listabolas[pbola].setPosX(pposX + 1);
int pposY = listabolas[pbola].getPosY();
listabolas[pbola].setPosY(pposY + 1);
}
Swing is a single threaded framework. That is, all interactions with UI are expected to occur from within a single thread, known as the Event Dispatching Thread.
Any action that blocks this thread, will prevent the EDT from updating the screen or processing any new events.
Your while-loop is blocking the EDT, preventing it from painting any updates until after the while-loop is completed.
Take a look at Concurrency in Swing for more details.
There are a number of approaches you could take...
You could use a Thread, but this causes problems as you need to ensure that any changes you make to the UI are re-synced back to the EDT and this can become messy...
For example
You could use a javax.swing.Timer that ticks at a regular interval and you would update any internal parameters from within it's assigned ActionListener. Because the tick events occur within the EDT, it is save to update the screen from within it.
For example
You could use a SwingWorker to run the task in the background. It has methods for re-syncing updates back to the EDT, but might be a little over kill for your purposes...
Updated with a possible Timer example
Caveat- It is very hard to produce a reasonable example with only a code snippet, but, something like this might work
public void golpe(final int pbola, int pvelocidad, String pdireccion, final JLabel[] listalabels) throws InterruptedException{
listabolas[pbola].setVelocidad(pvelocidad);
listabolas[pbola].setDireccion(pdireccion);
Timer timer = new Timer(40, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (listabolas[pbola].getVelocidad() == 0) {
((Timer)evt.getSource()).stop();
} else {
moverBola(pbola, listalabels);
}
}
});
timer.setRepeats(true);
timer.start();
}