In the ApplicationAdapter class, I override the render() method and draws my game from there.
I also have an own method: update() inside the render method which is where I want to update everything.
The render() method is as I know called ~60 times a second.
I dont want my update method to be dependent on how many times the render method is called.
I want the render method to render as much as the device can handle below 60 FPS.
I also want the update method to get called at a fixed rate: 60 times a second.
I´ve tried to call my update method 60 times a second in my render method like this:
double beforeMillis;
double FPS=4; //test
double millisPassed=0;
int x=0;
#Override
public void render () {
beforeMillis=System.currentTimeMillis();
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(tex, x, 10);
batch.end();
millisPassed+=(System.currentTimeMillis()-beforeMillis);
if(millisPassed>=1000/FPS){
update();
millisPassed=0;
}
}
...but it updates like once every four seconds.
Any good way of doing this? Thanks in advance!
The code you posted won't compare accurate time, because it ignores all the time spent doing things between batch.end() and the next call to render(), some of which occurs in the libgdx backend. And if you update after drawing in the render method, you will always have at least one visual frame of lag.
A basic implementation of a fixed timestep update in libgdx would be like this:
static final double DT = 1/60.0;
static final int MAX_UPDATES_PER_FRAME = 3; //for preventing spiral of death
private long currentTimeMillis;
public void create() {
currentTimeMillis = System.currentTimeMillis();
}
public void render() {
long newTimeMillis = System.currentTimeMillis();
float frameTimeSeconds = (newTimeMillis - currentTimeMillis) / 1000f;
currentTimeMillis = newTimeMillis;
int updateCount = 0;
while (frameTimeSeconds > 0f && updateCount <= MAX_UPDATES_PER_FRAME) {
float deltaTimeSeconds = Math.min(frameTimeSeconds, DT);
update(deltaTimeSeconds);
frameTimeSeconds -= deltaTimeSeconds;
++updateCount;
}
draw();
}
This does result in update occurring more than 60 times a second, because it gets updated with the "leftovers" to keep the visuals current (no stuttering). The update(float delta) method must be designed to use a variable delta time.
You can do a truly fixed update as described here in the last two sections, but that has the disadvantage of visual stuttering or needing to implement a one-frame delay and interpolating ahead.
Related
I am beginner when it comes to java and I have come across with a problem that I haven't found a solution to just yet. The thing is that I have working methods for drawing an letter and also for rotating it - when I set rotation it works the way it should. However I would like to make this slightly more interactive, at school we were given the basic framework for that - I was able to create a button that when you click on it the letter's angle changes and the letter is redrawed correctly. But I would like to make it an animation, for example you click on the button and for 10 seconds (or until you press the button again) the letter will be rotating.
On the internet I found a way to perform an action after certain period of time, and I thought I will use this. I wanted to add an angle and redraw an image, after let's say 1 second, then it would repeat - I thought this would make it look like it is animated. But I was wrong. I tried so many ways to do this, the best thing was that after few seconds that I set I want the animation to go for, it changed an angle and redraw, unfortunately it was the final state and it didn't draw states in between to create an animation. And this latest code doesn't even do that, the program just freezes.
int animation = 0;
int steps = 0;
public void G_draw() {
graphic.clear();
if (animace==1)
{
animation();
}
letter('a', G_Color.G_cBlack, 2, 2);//drawing an letter
}
public void G_mousePressed(G_Button button, int x, int y) {
if (button.equals(G_Button.B_LEFT)&&x>700&&x<750&&y>500&&y<520){
animation=1;
G_draw();
}
}
public void animation() {
long start = System.currentTimeMillis();
long end = start + 2 * 1000;
while (System.currentTimeMillis() < end) {
}
langle+=30; // adding an angle
steps++;
G_repaint();
G_draw();
if (steps<4) animace();
}
instead of this
long start = System.currentTimeMillis();
long end = start + 2 * 1000;
while (System.currentTimeMillis() < end) {
}
use
Thread.sleep(# in milliseconds);
We can't see where you use langle
This line if (steps<4) animace(); only runs when animace<>1 otherwise it's skipped because animate()calls G_draw() calls animate and so on.
Sorry, I couldn't word my title properly but I will explain my problem with more clarity here.
I am using libgdx.
When I want to move a Texture so that it covers the same distance with all FPS I will do this:
//...define Player class with x property up here.
Player player = new Player();
int SPEED = 100
public void render() {
player.x += SPEED * Gdx.graphics.getDeltaTime();
}
Now I want to know how to do this to have the same affect on a body in box2d. Here is an example(the render method of a class that extends ApplicationAdapter):
public void render() {
//clear screen, ... do other stuff up here.
playerBody.applyForce(new Vector2(0.5f / PIXEL_PER_METER, 0.0f), playerBody.getWorldCenter(), true);
//PIXEL_PER_METER -> applied to scale everything down
//update all bodies
world.step(1/60f, 6, 2);
}
This applies a force on the playerBody so that it's acceleration increases. How do I make shore, just like with my first example, that how fast the body is travelling stays constant across at 30fps, 10fps, 60fps, etc. I know the timeStep parameter of the world.step is the amount of time to simulate but this value shouldn't vary.
Thankyou in advance.
You can update all bodies with delta (not 1/60 fixed delta)
world.step(Gdx.graphics.getDeltaTime(), 6, 2);
EDIT:
As #Tenfour04 mentioned, in order to prevent high delta values (causes huge jumps), we mostly set a cap for delta.
world.step(Math.min(Gdx.graphics.getDeltaTime(), 0.15f), 6, 2);
I wouldn't use a variable timestep - this is the approach I've used:
private float time = 0;
private final float timestep = 1 / 60f;
public void updateMethod(float delta) {
for(time += delta; time >= timestep; time -= timestep)
world.step(timestep, 6, 2);
}
Basically a variable-ish timestep, but uniformly updating.
If you run your game at very low FPS, or if you force it to with the application configuration (e.g. for testing) this will keep updating at roughly the same speed as a normal 60 FPS instance.
Take a look at Fix your timestep!
I'm almost done with my project to make a very simple game. The point of the game is to count how many times the user touches the screen and compare it to the number of times an object popped up on the screen to be touched. It's considered a win if number of touches = number of objects. All of this is to happen in 10 seconds, and I have no idea where to start on how to time the game? (It's supposed to be a veryyy very simple game just to learn how to use libgdx)
As of now, there's a start screen that the user touches to start and then the game starts where objects pop up to be touched. The problem is that it's basically an endless game right now... Here's how I make an object "randomly pop up":
if (Gdx.input.isTouched()) {
touchCount++;
Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
visual.x = MathUtils.random(10, 700);
visual.y = MathUtils.random(100, 400);
}
Do I have to put some built in timer function in this part of the code? And how to I go about tracking the touches to compare with the number of objects that popped up?
Thanks, any advice would be very appreciated!
System.currentTimeMillis() gives you current time in milliseconds
For example you can do something like this
private long time;
public void show(){
//...
time = System.currentTimeMillis();
}
public void draw(){
//...
if(System.currentTimeMillis()>time+10000){
System.out.println("after 10 seconds");
}
}
You may use old fashioned java way either:
Timer timer = new java.util.Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
// Pause your game or just close it...
Gdx.app.exit();
}
}, 10000L);
Of course it's better to use synchronous timer approach, but for fast debuging or prototyping needs its sometimes more convenient.
Use the delta value inside your render() method.
render(float delta){
// ......
if (yeni_oyun_mesaji_var_MI)// a boolean value to increment my time value only it is needed.
oyun_uyari_zamani_tutucu += delta;
if (oyun_uyari_zamani_tutucu > oyun_uyari_zaman_siniri) {// after some time (**oyun_uyari_zaman_siniri**) do what you need
oyun_uyari_zamani_tutucu = 0f;
oyun_durum_mesaji_str = "";
yeni_oyun_mesaji_var_MI = false;
Gdx.app.log(TAG, "Uyarı Kapatıldı!");
}
}
Alright, i'm making a small game, and I need to limit my FPS, because, when I play on my really fast computer, I have around 850 FPS, and the game will go like, REALLY fast, and when I switch to my older computer, it goes alot slower, so I will need to limit my FPS to get this right. How do I limit my FPS?
My main game loop:
public void startGame(){
initialize();
while(true){
drawScreen();
drawBuffer();
plyMove();
//FPS counter
now=System.currentTimeMillis();
framesCount++;
if(now-framesTimer>1000){
framesTimer=now;
framesCountAvg=framesCount;
framesCount=0;
}
try{
Thread.sleep(14);
}catch(Exception ex){}
}
}
How I draw the screen, and draw all of the other things, players, the ball, etc.
The game is a pong remake, btw.
public void drawBuffer(){
Graphics2D g = buffer.createGraphics();
g.setColor(Color.BLACK);
g.fillRect(0,0,600,500);
g.setColor(Color.GREEN);
g.fillRect(ply1.getX(),ply1.getY(),ply1.getWidth(),ply1.getHeight());
g.setColor(Color.RED);
g.fillRect(ply2.getX(),ply2.getY(),ply2.getWidth(),ply2.getHeight());
g.setColor(Color.WHITE);
g.fillOval(ball1.getX(),ball1.getY(),ball1.getWidth(),ball1.getHeight());
g.drawString("" + framesCountAvg,10,10);
}
public void drawScreen(){
Graphics2D g = (Graphics2D)this.getGraphics();
g.drawImage(buffer,0,0,this);
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
It sounds like your display it tied to your game engine. Make sure the two are disconnected. You want the game playing at the same speed no matter what the framerate is. If your fast computer repaints the screen faster that is ok if the engine is causing the game to play at a constant speed.
Rather than limiting your FPS, make it so that the game doesn't go really fast when the fps is high.
Presumably you have code that does certain things each frame e.g. moves the character forward if a button is pressed. What you want to do instead is move the character forward an amount dependent on the amount of time that has passed since the previous frame.
As aforementioned you need to separate your display loop from your update loop somewhere at the core of you game there is probably something like this
while (1)
{
drawscene();
update();
}
and in update you are advancing the time by a fixed amount e.g 0.17 sec. If you drawing at exactly 60fps then the animations are running in realtime, at 850fps everything would be sped by a factor 14 (0.17*850) to prevent this you should make your update time dependent. e.g.
elapsed = 0;
while (1)
{
start = time();
update(elapsed);
drawscene();
sleep(sleeptime);
end = time();
elapsed = end - start;
}
A description of what I meant in my comment. Inside your method plyMove(); I suspect there is something like
plyMove() {
ply1.x += amountX;
ply1.y += amountY;
}
Instead make the movement depending on the time elapsed
plyMove(double timeElapsed) {
ply1.x += amountX * timeElapsed;
ply1.y += amountY * timeElapsed;
}
where you calculate timeElapsed from the time difference inside your main loop and scale it accordingly to get a decent movement.
double timePrev = System.currentTimeMillis();
while(true) {
double timeNow = System.currentTimeMillis();
double elapsed = timeNow - timePrev;
drawScreen();
drawBuffer();
plyMove(0.001 * elapsed);
timePrev = timeNow;
}
If you really want a fixed timeframe game you could do this:
float timer = 0;
float prevTime= System.currentTimeMillis();
while(true)
{
draw();
float currentTime = System.currentTimeMillis();
timer += currentTime - prevTime;
while (timer > UPDATE_TIME) // To make sure that it still works properly when drawing takes too long
{
timer -= UPDATE_TIME;
update();
}
}
Where UPDATE_TIME is at what interval you want to update at in milliseconds.
One way would be using
try {
Thread.sleep(20);
} catch(InterruptedException e) {}
at the end of the main loop. Adjust the number to your needs.
I'm using the Animator class from the Timing Framework to perform a simple animation (moving a JPanel), roughly based on examples in Filthy Rich Clients
I've overridden timingEvent() as follows
public void timingEvent(float arg0) {
float fraction = this.animator.getTimingFraction();
fraction = Math.min(1.0f, fraction);
if (fraction >= 1.0f) {
this.mainGUI.initiateSwitchToMainScreenTransition();
} else if (fraction < 0.5f) {
this.translateY = (int) (MAX_Y * (2 * fraction));
repaint();
}
}
I've overriden paint() to use the value in translateY to move the panel down.
The animation itself works perfectly.
My problem is the call to initiateSwitchToMainScreenTransition(), which I want to be performed when the animation is complete. Whilst this works, there is a sizeable delay between the animation ending and the call firing (debugging has confirmed that the problem isn't the length of the time taken by initiateSwitchToMainScreenTransition() - the delay occurs before the call).
Is there a better way to achieve this, i.e. without the delay?
If you are implementing org.jdesktop.animation.timing.TimingTarget then there is an end() method that should be called when your animation is complete - that seems to work for me when I have used it. I do not think you can guarantee that timingEvent will get called after the fraction has reached 1.0.
Your implementation does not seem to do anything for the second half of the timer (when fraction is between 0.5 and 1.0). If you want the animation to continue for the entire duration of the timer you could rearrange your code like this:
#Override
public void timingEvent(float fraction) {
translateY = (int) (MAX_Y * fraction);
repaint();
}
#Override
public void end() {
mainGUI.initiateSwitchToMainScreenTransition();
}