I want to add possibility to pause/resume to my game. Unfortunately my solution only pauses the game, but does not resume it (when I click resume button nothing happens - the image is still). I also tried to call wait/notify methods on thread, but it also did not work - I got Exception in thread "AWT-EventQueue-0" java.lang.IllegalMonitorStateException. What is the best solution to make pause/resume?
Game Loop
public void run() {
init();
long startTime;
long reTime;
long waitTime;
while (running) {
startTime = System.nanoTime();
int CarPosX = car.zwrocPolozenieX();
int CarPosY = car.zwrocPolozenieY();
update(b, CarPosX, CarPosY);
render(b);
//draw();
repaint();
if(b<4390) {
b=b+szybkoscMapy;
}
reTime = System.nanoTime() - startTime;
waitTime = targetTime - reTime/100000000;
try {
Thread.sleep(waitTime);
}
catch(Exception e) {
}
}
}
public void init() {
if(thread == null) {
thread = new Thread(this);
thread.start();
}
b=0;
running = true;
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
map = new Map(levelNumber);
car = new Car(map);
car.setxpos(338);
car.setypos(150);
}
Listeners
pause_button.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
bp.running = false;
}
});
resume_button.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
bp.running = true;
}
});
Use javax.swing.Timer to pace the animation. Typical controls that invoke the timer's start() and stop() methods are examined here. This fleet simulation, which uses such a Timer, may also be if interest.
Related
I'm working on my idle game, and I'm starting to add in an adventure side to it, but it's not working. I had it to where it would flicker on and off the screen, but when I started working on it more, it started to not work.
Here is the threaded code:
thread = new Thread(this);
IH = new ImageHandler(this);
listener = new Listener(this);
removeEntity = new ArrayList<>();
addEntity = new HashMap<>();
frame = new JFrame("IDLE");
frame.add(this);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setResizable(false);
frame.setUndecorated(true);
frame.addWindowListener(listener);
frame.addMouseListener(listener);
frame.addMouseMotionListener(listener);
frame.addKeyListener(listener);
frame.pack();
Image bgBase = IH.getImg("background");
frame.setVisible(true);
WIDTH = frame.getWidth();
HEIGHT = frame.getHeight();
background = IH.getBG();
adv = new Adventures(this);
//Stuff here to load in the variables and entity data
start();
Now, here are my start and stops:
private synchronized void start() {
thread.start();
}
public synchronized void stop() {
save();
frame.setVisible(false);
try {
thread.join();
} catch(InterruptedException e) {
e.printStackTrace();
}
System.exit(1);
}
And now my "run" class (because of the Thread):
public void run() {
long lastTime = System.nanoTime();
final double ns = 1000000000.0 / 60.0;//60 times per second
double delta = 0;
requestFocus();
while(frame.isVisible()) {
long now = System.nanoTime();
delta = delta + ((now-lastTime) / ns);
lastTime = now;
while (delta >= 1)//Make sure update is only happening 60 times a second
{
//handles all of the logic restricted time
update();
delta--;
}
this.repaint();//displays to the screen unrestricted time
}
}
If you need more code, please let me know!
Please make sure you actually implemented on of the interface methods the KeyListener addresses.
I see the declaration and binding in your code frame.addKeyListener(listener); but there should be something like a keyPressed(KeyEvent e)method.
I have got problems with my Android application (Android ANR keyDispatchingTimedOut). I know the reason behind, but I can't solve the problem. My UI thread is probably overloaded and I wondering how to move expensive code/methods to a background thread. I also wonder, what kind of graphics/update methods can be moved to a background thread?
This is my UI thread:
Canvas canvas;
Log.d(TAG, "Starting game loop");
long beginTime; // The time when the cycle begun
long timeDiff; // The time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // Number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// Try locking the canvas
try {
canvas = this.surfaceHolder.lockCanvas();
if (canvas != null) {
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// Update game state here!
this.gameView.update();
// Render state to the screen
// Draws the canvas on the panel
this.gameView.render(canvas);
// Calculate how long time the cycle took
timeDiff = System.currentTimeMillis() - beginTime;
// Calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
try {
// Send the thread to sleep for a short period,
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// Need to catch up by updating without rendering
// Update game state here!
this.gameView.update();
// Add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
}
} finally {
// In case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // End finally
}
And here are the two methods, update() and render()
public void update() {
// Gets the start level if it isn't initialized (when you
// enter the game for the first time)
if (startLvl == 0) {
startLvl = ((SingleGameActivity)getContext()).getStartLvl();
currentLvl = startLvl;
}
if (playing) {
// Update the life icons
updatePlayerLives();
updateComputerLives();
// Checks if you have won or lost
outOfBounds();
// Update the position of the ball, player and computer including
// collision detection and reaction.
ball.moveWithCollisionDetection(box, player, computer);
player.moveWithCollisionDetection(box);
computer.setDirectionWithAI(ball);
computer.moveWithCollisionDetection(box);
} else {
// Create new objects
newObjects();
if (newRound) {
playing = true;
newRound = false;
}
}
}
// //////////////////////////////////////////////////////////////////////////////////
// The render() method renders the UI graphics on the screen
public void render(Canvas canvas) {
super.onDraw(canvas);
if (playing) {
// Draw components
box.draw(canvas);
// Draw booster/s
if (racketTouches > 4) {
if (b1.isUsed()) {
b1 = new Booster();
}
canvas.drawBitmap(b1.booster, b1.boosterX, b1.boosterY, null);
}
if (racketTouches > 14) {
if (b2.isUsed()) {
b2 = new Booster();
}
canvas.drawBitmap(b2.booster, b2.boosterX, b2.boosterY, null);
}
if (racketTouches > 24) {
if (b3.isUsed()) {
b3 = new Booster();
}
canvas.drawBitmap(b3.booster, b3.boosterX, b3.boosterY, null);
}
// Draw rackets and ball
player.draw(canvas);
computer.draw(canvas);
ball.draw(canvas);
} else {
// Draw components
box.draw(canvas);
player.draw(canvas);
computer.draw(canvas);
ball.draw(canvas);
}
}
Are the methods overloaded? If so, what can I move to a background thread? And how?
FYI: I'm creating a retro pong/tennis game with boosters included.
You never want to do expensive tasks like drawing and animations on the UI thread. There are two ways to utilize a worker thread...
Thread with a runnable (this can be created inside your drawing surface, drawing surface must implement Runnable):
new Thread(new Runnable() {
public void run() {
update();
render();
}
}).start();
Extend Thread in a separate class:
class myThread extends Thread {
Context context;
//Everything that you currently have in your UI thread should be in here...
/* Constructor for thread. Pass useful things like context, drawing surface, etc */
public myThread(Context context) {
this.context = context;
}
public void setRunning(Boolean running) {
this.running = running;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
long beginTime; // The time when the cycle begun
long timeDiff; // The time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // Number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// Try locking the canvas
try {
canvas = this.surfaceHolder.lockCanvas();
if (canvas != null) {
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// Update game state here!
this.gameView.update();
// Render state to the screen
// Draws the canvas on the panel
this.gameView.render(canvas);
// Calculate how long time the cycle took
timeDiff = System.currentTimeMillis() - beginTime;
// Calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
try {
// Send the thread to sleep for a short period,
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// Need to catch up by updating without rendering
// Update game state here!
this.gameView.update();
// Add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
}
} finally {
// In case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // End finally
}
}
}
And when you create your drawing surface, assign the thread:
Thread thread;
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new myThread(getContext());
thread.setRunning(true);
thread.start();
}
So that's pretty much it - note that you'll probably have to pass some other things to the thread in the second example through its constructor for it to work with your game, such as the surface on which you are drawing and the SurfaceHolder, but that's the idea.
Good luck with your game!
I have created the loop, which periodically repaints component:
public class A extends Thread {
private Component component;
private float scaleFactors[];
private RescaleOp op;
public A (Component component){
this.component = component;
}
public void run(){
float i = 0.05f;
while (true) {
scaleFactors = new float[]{1f, 1f, 1f, i};
op = new RescaleOp(scaleFactors, offsets, null);
try {
Thread.sleep(timeout);
} catch (InterruptedException ex) {
//Logger.getLogger(...)
}
component.repaint();
i += step;
}
}
}
But in this case I get the message (NetBeans 7.3.1):
Thread.sleep called in loop
Maybe there is better solution in this situation?
Swing is single threaded. Calling Thread.sleep in the EDTprevents UI updates.
I suggest using a Swing Timer instead. It was designed to interact with Swing components.
Timer timer = new Timer(timeout, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
component.repaint();
}
});
timer.start();
Edit:
Stopping a timer from within its own ActionListener is typically done using
#Override
public void actionPerformed(ActionEvent e) {
Timer timer = (Timer) e.getSource();
timer.stop();
}
I've been working on a simple game engine. All was going pretty well, and I got the foundation finished. By that, I mean, the game sets up with user defined display settings, initializes stuff, and starts the game loop. Everything works great, except I have an issue in both Windowed mode and full screen:
In Windowed mode, sometimes the screen will appear blank, until I resize or minimize the window.
In full screen, I only get a blank screen if I use the default Display resolution. Most of the other resolutions appear just fine.
Here is some code relevant to displaying the screen:
This is performed when the use presses the play button on the display settings dialog
public void actionPerformed(ActionEvent e) {
DisplayMode dm = modes[resolutionBox.getSelectedIndex()];
boolean fs = fullscreenBox.getSelectedItem().equals ("Fullscreen");
boolean vs = vSyncCheckBox.isSelected();
game.initScreen (dm, fs, vs);
runGame (game);
f.dispose();
}
});
private final void runGame(EGame g)
{
Thread t = new Thread (g);
t.start();
}
This is the run method in the EGame class
public final void run() {
start();
isRunning = true;
gameLoop();
endGame();
}
The user of the engine will call the method setGameCanvas() in the start() method. Other things can go in the start() method, but for my test games, it just that method:
protected final void setGameCanvas(EGameCanvas c)
{
screen.remove (canvas);
canvas = c;
canvas.addKeyListener (keyboard);
screen.add (canvas);
}
And finally, here is the gameLoop:
private final void gameLoop()
{
// Final setup before starting game
canvas.setBuffer (2);
screen.addKeyListener (keyboard);
canvas.addKeyListener (keyboard);
int TARGET_FPS = screen.getTargetFps();
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
long lastLoopTime = System.nanoTime();
long now = 0;
long lastFpsTime = 0;
long updateLength = 0;
double delta = 0;
int fps = 0;
while (isRunning)
{
now = System.nanoTime();
updateLength = now - lastLoopTime;
lastLoopTime = now;
delta = updateLength / ((double) OPTIMAL_TIME);
lastFpsTime += updateLength;
fps++;
if (lastFpsTime >= 1000000000)
{
screen.setFps (fps);
lastFpsTime = 0;
fps = 0;
}
keyboard.poll();
keyboardEvents();
update (delta);
render();
try {
Thread.sleep( (lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000 );
}
catch (Exception ex) {}
}
}
Now, keep in mind, neither of my issues existed, until I added the run method and ran it in a separate thread. I can about guarantee it's a threading issue.
But, I'm no expert, so any advice would be appreciated.
I believe the issue is that you're trying to modify GUI-related components outside of the event dispatch thread - that is the only thread allowed to modify the graphical interface, otherwise you might encounter issues like this.
To check this try if the following solves the issue:
try {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeAndWait( new Runnable() {
#Override
public void run() {
// Do rendering and other stuff here...
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
If that helps, I'd recommend taking a look on the Concurrency in Swing tutorial.
I have a JDialog being displayed on screen and I want to simulate its movement (Drag from one location to another) based on a condition. Is there any way this can be done ?
See this piece of code below. I have just tested it and it works fine. It is just a proof of concept.
private void startDialog() {
final JDialog d = new JDialog(this, "Test", true);
d.getContentPane().add(new JLabel("Something"));
d.setBounds(100, 100, 400, 300);
Thread t = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 50; i++) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Point p = d.getLocation();
d.setLocation(p.x + 10, p.y + 10);
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// ignore
}
}
}
});
t.start();
d.setVisible(true);
}
You can improve the code yourself:
use a Timer instead of a regular Thread
tweak the sleep times and the location jumps and so on
Just call this method from any Swing application and it will work.