So I am making a simple game with spites. But im having some issues with the tick/render loop.
I need it to run at 30 ticks a second. But the fps needs to be as fast as it can.
The issue I am having is the while loop doesn't run
lastTime += msPerTick;
So my output looks like this. No adding. No loop. No render.
x:0.375 y:0.03333333333333333
x:0.375 y:0.03333333333333333
x:0.375 y:0.03333333333333333
x:0.375 y:0.03333333333333333
x:0.375 y:0.03333333333333333
x:0.375 y:0.03333333333333333
x:0.375 y:0.03333333333333333
x:0.375 y:0.03333333333333333
Here is a snippet of the code. Let me know if you need more.
public static final int TICKS_PER_SECOND = 30;
private static final int MAX_TICKS_PER_FRAME = 10;
private boolean running;
private Thread thread;
private int tickCount;
private int frames;
private boolean paused;
public void run() {
init();
float lastTime = (System.nanoTime() / 1000000) / 1000.0f;
running = true;
double msPerTick = 1.0 / TICKS_PER_SECOND;
while (running) {
synchronized (this) {
float now = (System.nanoTime() / 1000000) / 1000.0f;
int frameTicks = 0;
while (now - lastTime > msPerTick) {
System.out.println("x:" + (now - lastTime) + " y:" + msPerTick);
if (!paused && frameTicks++ < MAX_TICKS_PER_FRAME)
tick();
lastTime += msPerTick; //<-- Issue here
}
if (!paused) {
render((now - lastTime) / msPerTick);
}
}
//Thread.yield(); No need for yield lets use sleep.
try {
Thread.sleep(paused ? 500 : 1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Because you're not adding 2 doubles. If you change the type of lastTime to double the code will work fine.
float f = (float) 0.2;
double d = 2;
f += d => 2.0
d += f => 2.2
I think there is an algorithm problem here which happens if your render action takes less than 1/30 second.
When you start the outer loop executes multiple times for 1/30 seconds without entering the inner loop. At the 1/30 second mark it enters the inner loop exactly once and then spends another 1/30 seconds looping in the outer loop.
If the render takes more than 1/30 seconds then you should not have the problem.
You cannot rely on the Thread.yield() to do much of anything.
Related
I'm working on a java 2d game, using this simple game loop to cap the FPS and UpdatesPS to 60:
public void run() {
final int MAX_FPS = 60;
final int MAX_UPS = 60;
final double fOPTIMAL_TIME = 1000000000 / MAX_FPS;
final double uOPTIMAL_TIME = 1000000000 / MAX_UPS;
double uDeltaTime = 0, fDeltaTime = 0;
int frames = 0, updates = 0;
long startTime = System.nanoTime();
long timer = System.currentTimeMillis();
// GameLOOP starts here
while (running) {
long currentTime = System.nanoTime();
uDeltaTime += (currentTime - startTime);
fDeltaTime += (currentTime - startTime);
startTime = currentTime;
if (uDeltaTime >= uOPTIMAL_TIME) {
gameUpdate();
updates++;
uDeltaTime -= uOPTIMAL_TIME;
}
if (fDeltaTime >= fOPTIMAL_TIME) {
gameRender();
gameDraw();
frames++;
fDeltaTime -= fOPTIMAL_TIME;
}
if (System.currentTimeMillis() - timer >= 1000) {
fps = frames; //saves the current FPS
ups = updates; //saves the current UPS
updates = 0;
frames = 0;
timer += 1000;
}
}
}
The loop works, but I get only 30 FPS for the first ~10 Seconds after starting the game.
After I wait, the FPS raises up to the wanted 60. I don't have a problem to wait a few seconds to let the program stabilize and reach the wanted framerate. But I can't find the reason like a methode who drops the FPS because it's fetching a big file after startup.
Do you have any idea why my engine needs so long to stabilize the framerate?
Thanks for your help!
I think this should do the trick:
public static void run()
{
final int desiredFPS = 60;
final int desiredUPS = 60;
final long updateThreshold = 1000000000 / desiredUPS;
final long drawThreshold = 1000000000 / desiredFPS;
long lastFPS = 0, lastUPS = 0, lastFPSUPSOutput = 0;
int fps = 0, ups = 0;
loop:
while(true)
{
if((System.nanoTime() - lastFPSUPSOutput) > 1000000000)
{
System.out.println("FPS: " + (double)fps);
System.out.println("UPS: " + (double)ups);
fps = 0;
ups = 0;
lastFPSUPSOutput = System.nanoTime();
}
if((System.nanoTime() - lastUPS) > updateThreshold)
{
lastUPS = System.nanoTime();
updateGame();
ups++;
}
if((System.nanoTime() - lastFPS) > drawThreshold)
{
lastFPS = System.nanoTime();
drawGame();
fps++;
}
// Calculate next frame, or skip if we are running behind
if(!((System.nanoTime() - lastUPS) > updateThreshold || (System.nanoTime() - lastFPS) > drawThreshold))
{
long nextScheduledUP = lastUPS + updateThreshold;
long nextScheduledDraw = lastFPS + drawThreshold;
long minScheduled = Math.min(nextScheduledUP, nextScheduledDraw);
long nanosToWait = minScheduled - System.nanoTime();
// Just in case
if(nanosToWait <= 0)
continue loop;
try
{
Thread.sleep(nanosToWait / 1000000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
Edit: I fixed the issue now! The issue was that I was saving the lastFPS/lastUPS after the scene was updated/drawn, and when I set both lastUPS/lastFPS before the scene is drawn/updated, we get the desired fps!Another neat thing about this code is that it doesn't consume a whole cpu core(I measured the difference, your code was consuming 100%, whilest my code only consumed about 10%. If you want to measure it yourself, please note that for some reason, the core on which the code is executed regularly switches(at least this was the case when I measured the code))By the way if you use LWJGL (or have direct access to a windowing library like GLFW) you can activate V-Sync, which would cut your fps down to 60 fps.
hi am trying to make a small game using canvas and bitmaps i want my game to run the same on all devices i found delta time is the best practice for this but for some reason when i try to implement it into my code i have display issues for example am trying to move my coluds in the sky but when i add the delta they all disapere i dont know if im doing it wrong so please can sombody help me heres the code
private float c1x = 0.0f;
private float c2x = cloudWidth;
private float c3x = cloudWidth * 2;
private float cloudSpeed = 0.1f;
private long curentTime;
private long lastTime = 0;
private double delta;
#Override
public void run(){
while(running){
if(!holder.getSurface().isValid()){
continue;
}
curentTime = System.nanoTime();
delta = curentTime - lastTime;
lastTime = curentTime;
cloudMovement();
canvas = holder.lockCanvas();
canvas.drawBitmap(bg, 0, 0, null);
canvas.drawBitmap(sun, 20, 20, null);
canvas.drawBitmap(cloud1, c1x, c1y, null);
canvas.drawBitmap(cloud2, c2x, c2y, null);
canvas.drawBitmap(cloud3, c3x, c3y, null);
holder.unlockCanvasAndPost(canvas);
}
}
private void cloudMovement(){
if(c1x <= 0 - cloudWidth){
c1x = w;
c1y = y.nextInt(rand);
}
if(c2x <= 0 - cloudWidth){
c2x = w;
c2y = y.nextInt(rand);
}
if(c3x <= 0 - cloudWidth){
c3x = w;
c3y = y.nextInt(rand);
}
c1x-=cloudSpeed * delta;
c2x-=cloudSpeed * delta;
c3x-=cloudSpeed * delta;
}
You could use a global FPS mechanism instead which forces a steady FPS on your game :)
If you track the FPS the game will run the same way on any device and you dont need to include delta-times on all update processes.
Here's a code snippet from a FpsTracker i used in an old project:
private static final long SECOND = 1000;
private static final long TARGET_FPS = 40;
private static final long FRAME_PERIOD = SECOND / TARGET_FPS;
private long time = System.currentTimeMillis();
/**
*
* #param startTime
* #return <code>true</code> if the interval between startTime and the time
* when this method was called is smaller or equal to the given
* frame period.
*
* Will return <code>false</code> if the interval was longer.
*/
public boolean doFpsCheck(long startTime) {
if (System.currentTimeMillis() - time >= SECOND) {
time = System.currentTimeMillis();
}
long sleepTime = FRAME_PERIOD
- (System.currentTimeMillis() - startTime);
if (sleepTime >= 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
//TODO handle this properly
e.printStacktrace()
}
return true;
} else {
return false;
}
}
If this method returns false it means that your operations took longer that the timeperiod you gave to one frame. You can react to this by checking the doFpsCheckreturn parameter.
Implementing this in your code would look like this:
#Override
public void run()
{
while(running)
{
if(!holder.getSurface().isValid())
{
continue;
}
startTime = System.currentTimeMillis();
cloudMovement();
canvas = holder.lockCanvas();
canvas.drawBitmap(bg, 0, 0, null);
canvas.drawBitmap(sun, 20, 20, null);
canvas.drawBitmap(cloud1, c1x, c1y, null);
canvas.drawBitmap(cloud2, c2x, c2y, null);
canvas.drawBitmap(cloud3, c3x, c3y, null);
holder.unlockCanvasAndPost(canvas);
doFpsCheck(startTime);
}
}
By the way - it is good practice to devide your game loop into pro sub processes, one being the update process, the other being the draw process.
For many different Bitmaps you should consider extracting the fields and functionalities into seperate classes containing a draw(Canvas c) and update() method. So you wont get a trillion fields on your main class.
I've just exported my Eclipse project to a runnable jar file. I have no lag issues when I run it in Eclipse, but have extreme lag when running it in the jar. (Running MacOSX 10.9.4)
According to my FPS counter, I'm getting over 900 frames a second, and the game is actually running, but nothing is actually being rendered for a while.
Not sure what code to show because there is a lot of it, as it is an almost completed game. But let me know what code you might need.
Here's my game loop:
public void run() {
long start = System.nanoTime();
final double numUpdates = 30.0;
double ns = 1000000000 / numUpdates;
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
while(running) {
long current = System.nanoTime();
delta += (current - start) / ns;
start = current;
if(delta >= 1) {
update();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer > 1000) {
timer = System.currentTimeMillis();
Window.frame.setTitle("Rage Mage UPS: " + updates + ", FPS: " + frames);
updates = 0;
frames = 0;
}
}
}
render() method:
private void render() {
statusHandler.render(g); // statusHandler is a class that handles the current state of the game
Graphics g2 = getGraphics();
g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);
}
I don't think the game loop is the problem though, because I was getting the same issues before with a different loop, and then I changed to this loop, and nothing has changed.
Thanks!
I am writing a game loop, I found the code in the example below here. I have also looked at other ways to do a game loop, such as from this article. I couldn't get any of those ones working though. So I kept with the one from the first link.
What I would like to know:
Is the way I wrote my game loop a good way to do this?
Any suggestions?
Should I be using Thread.sleep(); in my game loop?
Here is my current code:
public void run(){
long lastLoopTime = System.nanoTime();
final int TARGET_FPS = 60;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
long lastFpsTime = 0;
while(true){
long now = System.nanoTime();
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double)OPTIMAL_TIME);
lastFpsTime += updateLength;
if(lastFpsTime >= 1000000000){
lastFpsTime = 0;
}
this.updateGame(delta);
this.repaint();
try{
Room.gameTime = (lastLoopTime - System.nanoTime() + OPTIMAL_TIME) / 1000000;
System.out.println(Room.gameTime);
Thread.sleep(Room.gameTime);
}catch(Exception e){
}
}
Eventually you'll want to move to something like LWJGL, but let me stress, keep doing what you're doing here for now. It will teach you fundamentals.
Good job on your loop. Looks nice, let me offer a few pointers:
Repaint will not render the screen immediately. It tells the RepaintManager to render when its ready. Use invalidate paintImmediately instead. paintImmediately will block execution until the component has been redrawn so you can measure rendering time.
Thread.sleep typically has a few milliseconds drift. You should be using it to keep your loop from using too much CPU, but make sure you understand if you sleep 10 milliseconds you might sleep 5 milliseconds or you might sleep 20.
Lastly:
double delta = updateLength / ((double)OPTIMAL_TIME);
If updateLength is less than OPTIMAL_TIME, don't call update. In other words, if delta is less than one, don't update. This tutorial explains why better than I ever could.
Overall, it is a good loop, but there are a few missing aspects to what I have found in experience to be the best loop.
You will eventually want to move to LWJGL or some other java game API, but for now, learn the basics of how game-loops work, and what best suits your needs.
Firstly, in answer to one of your points, no. You will do better staying away from
Thread.sleep()
this can stray from the real amount of time you set it to sleep.
e.g. if you set it to sleep for 10 milliseconds, it could sleep the program for 5 to 20 milliseconds.
The second problem I cam immediately see is that you do not have any way to stop the game-loop for a custom stop() method. Try
boolean running = true;
while (running) {
// Your Code Here //
}
Thirdly, you may want to consider changing how you use your delta variable. The way in the code below may be a better use and construction for you.
This is an example of my game-loop that I use in my programs:
#Override
public void run() {
long initialTime = System.nanoTime();
final double timeU = 1000000000 / UPS;
final double timeF = 1000000000 / FPS;
double deltaU = 0, deltaF = 0;
int frames = 0, ticks = 0;
long timer = System.currentTimeMillis();
while (running) {
long currentTime = System.nanoTime();
deltaU += (currentTime - initialTime) / timeU;
deltaF += (currentTime - initialTime) / timeF;
initialTime = currentTime;
if (deltaU >= 1) {
getInput();
update();
ticks++;
deltaU--;
}
if (deltaF >= 1) {
render();
frames++;
deltaF--;
}
if (System.currentTimeMillis() - timer > 1000) {
if (RENDER_TIME) {
System.out.println(String.format("UPS: %s, FPS: %s", ticks, frames));
}
frames = 0;
ticks = 0;
timer += 1000;
}
}
}
the simplest way to refresh repainting could be like this:
public class GameLoop extends JPanel {
private final BufferedImage back_buffer;
bool state = true;
public void init() {
while (state) {
updatePlayer();
delay(5);
}
}
public void delay(int time) {
try {
Thread.sleep(time);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
repaint();
}
and the repaint function is who update all of the graphics
#Override
public void paint(Graphics g) {
super.paint(g);
grapicDoble.setColor(Color.white);
grapicDoble.fillRect(0, 0, 500, 500);
game.reset();
g.drawImage(back_buffer, 0, 0, this);
}
I have a JScrollPane with a moderately high block increment (125). I would like to apply smooth/slow scrolling to it so it doesn't jump (or skip) when scrolling. How can I do this?
I was thinking of scrolling like Windows 8.
Any help would be greatly appreciated!
You could use a javax.swing.Timer during the scroll to achieve the smooth scrolling effect. If you are triggering this from outside the component, somthing like this will work (where component is the component within the JScrollPane):
final int target = visible.y;
final Rectangle current = component.getVisibleRect();
final int start = current.y;
final int delta = target - start;
final int msBetweenIterations = 10;
Timer scrollTimer = new Timer(msBetweenIterations, new ActionListener() {
int currentIteration = 0;
final long animationTime = 150; // milliseconds
final long nsBetweenIterations = msBetweenIterations * 1000000; // nanoseconds
final long startTime = System.nanoTime() - nsBetweenIterations; // Make the animation move on the first iteration
final long targetCompletionTime = startTime + animationTime * 1000000;
final long targetElapsedTime = targetCompletionTime - startTime;
#Override
public void actionPerformed(ActionEvent e) {
long timeSinceStart = System.nanoTime() - startTime;
double percentComplete = Math.min(1.0, (double) timeSinceStart / targetElapsedTime);
double factor = getFactor(percentComplete);
current.y = (int) Math.round(start + delta * factor);
component.scrollRectToVisible(current);
if (timeSinceStart >= targetElapsedTime) {
((Timer) e.getSource()).stop();
}
}
});
scrollTimer.setInitialDelay(0);
scrollTimer.start();
The getFactor method is a conversion from linear to an easing function and would be implemented as one of these depending on how you want it to feel:
private double snap(double percent) {
return 1;
}
private double linear(double percent) {
return percent;
}
private double easeInCubic(double percent) {
return Math.pow(percent, 3);
}
private double easeOutCubic(double percent) {
return 1 - easeInCubic(1 - percent);
}
private double easeInOutCubic(double percent) {
return percent < 0.5
? easeInCubic(percent * 2) / 2
: easeInCubic(percent * -2 + 2) / -2 + 1;
}
This could probably be adapted to work within a component too so when the user scrolls it does something along these lines.
Or, if possible, you could use JavaFX which has much better support for animation than Swing.