After reading about implementing a fixed timestep as set out here:
http://gafferongames.com/game-physics/fix-your-timestep/
I've implemented my own version of the algorithm in Java on Android, which has given me a great improvement, but I can't seem to eradicate an occasional gitter.
My GameTimer class contains a method called tick() which is called every frame to advance time, along with canStep() and step() methods as below:
public void tick() {
long thisTime = System.nanoTime();
frameTime = thisTime - lastTime;
frameTimeMs = (int)(frameTime / NANO_TO_MILLI);
frameCounter++;
secondCounter -= frameTime;
if(secondCounter <= 0) {
fps = frameCounter;
frameCounter = 0;
secondCounter += NANO_TO_SECOND;
}
lastTime = thisTime;
accumulator += Math.min(frameTimeMs, deltaTime * 5);//frameskip >5 frames
alpha = accumulator / deltaTime;
}
public boolean canStep() {
return accumulator >= deltaTime;
}
public void step() {
accumulator -= deltaTime;
alpha = accumulator / deltaTime;
}
NB: deltaTime holds 1000f divided by the desired number of updates per second (e.g. 30).
My main game loop runs this logic per frame as follows:
gameTimer.tick();
while(gameTimer.canStep()) {
update();
gameTimer.step();
}
draw();
To calculate a game object's movement I do the following:
float time = gameTimer.getDeltaTime() / 1000f; //how much of a second per update
velocity = speed * direction * time; //simplified: velocity, speed & direction are vec3s
previousPosition = position;//simplified: values are copied
position += velocity;
A moving game object's draw position is then calculated at draw time using linear interpolation:
lerp(drawPosition, previousPosition, position, 1.0f + gameTimer.getAlpha());
The game draws at between 50 to 60 fps. Experimenting with the updates per second hasn't yielded any better results. Also, increasing or decreasing the frameskip doesn't remove the gitter either. The object suddenly jumps forward a few pixels every now and again and I'm struggling to iron the issue out.
Can anyone see any obvious problems with the above code that I'm missing?
Any help would be greatly appreciated :)
Related
What is the proper way to implement framerate-independent oscillation? I am using the LibGDX library which provides delta time for the update loop.
Currently, my sine wave pattern works as expected as long as the FPS is at a healthy 60 average (May have skewed slightly, due to slight gif-capture software lag):
http://i.stack.imgur.com/ZcKmX.gif
However, when the framerate drops, the pattern becomes skewed and acts rather strangely:
http://i.stack.imgur.com/tn82J.gif
Here is the sine wave method:
private void sineWaveDown(float speed, float amplitude, boolean mirror, float delta){
HitBox hitBox = getTransform().getHitBox();
int mirrored = 1;
if (mirror)
mirrored = -1;
Vector2 current = new Vector2(hitBox.x, hitBox.y);
current.y -= speed * delta;
current.x += MathUtils.sin(current.y * delta) * amplitude * mirrored;
hitBox.setPosition(current);
}
In this example, speed is 60 and amplitude is 2.
HitBox is derived from com.badlogic.gdx.math.Circle, and is used to logically represent the circles you see in the images above.
Edit: Question has been answered. Here is my working code:
private void sineWaveDown(float delta){
HitBox hitBox = getTransform().getHitBox();
int mirrored = 1;
if (config.mirrored)
mirrored = -1;
Vector2 current = new Vector2(hitBox.x, hitBox.y);
current.y -= config.speed * delta;
elapsedTime = (elapsedTime + delta) % (1/config.frequency);
float sinePosition = mirrored * config.amplitude * MathUtils.sin(MathUtils.PI2 * config.frequency * elapsedTime + config.phase);
current.x = config.spawnPosition.x + sinePosition;
hitBox.setPosition(current);
}
I haven't found a good way to do this without using elapsed time. You can "clean" the elapsed value with a modulus to avoid losing precision after a long time elapses.
elapsed = (elapsed + deltaTime) % (1/FREQUENCY);
float sinePosition = amplitude * MathUtils.sin(MathUtils.PI2 * FREQUENCY * elapsed + PHASE);
I'm not sure what you're doing basing the sine of x off of what y is, but you can adapt the above.
I am developing a 2D platformer game in Java/Slick2D.
Up until now my character moved a constant amount of pixels per frame.
I've tried switching to using the 'delta' variable (amount of time between frames), like advised, but the character's movement seems awfully jittery.
What can I do to smoothen the character's movement?
private static final float DEFAULT_SPEED = 0.15f;
Vector2f trans = new Vector2f();
Input i = gc.getInput();
boolean run = false;
// X-Axis Movement
if (i.isKeyDown(Input.KEY_D)){
trans.x += DEFAULT_SPEED * delta;
lastMoveDirection = Direction.RIGHT;
}
if (i.isKeyDown(Input.KEY_A)){
trans.x -= DEFAULT_SPEED * delta;
lastMoveDirection = Direction.LEFT;
}
if (i.isKeyDown(Input.KEY_LSHIFT)){
trans.x *= RUN_SPEED_MULTIPLIER;
run = true;
}
How are you defining delta? It should be how long it took a frame to draw / distance moved per second, or similar.
I am having an issue with gravity varying severely with frame rate shifts. When I run at 160 fps, my player jumps a few meters in the air then falls, but at about 10 fps, my player jumps half a meter then falls. My code for gravity is as follows:
public void fall(long delta) {
float increase = acceleration * ((delta) / 1000000000); //changes delta time (nanoseconds) to seconds
if(player.y + velocity + increase < -1.15f) {
if(velocity + inc < terminal_velocity) {
velocity += inc;
}
player.y += velocity;
}else{
player.y = -1.15f;
velocity = 0;
}
}
And where I call it:
while(!close_request) {
now = getTime();
int delta = getDelta(now);
player.fall(delta);
........other functions.........
}
I thought implementing the delta would keep the player from changing velocity too fast or too slow, but it actually made it a bit worse. I think this is due to the fact that as the time between frames increases, so does the increase in velocity which causes the player to fall abnormally fast. This comes from the fact that as the FPS increases, the player jumps much, much higher. Any ideas?
Your problem is in this line:
player.y += velocity;
which fails to take into account that velocity is "distance divided by time".
You're correctly modelling acceleration:
v = u + a * t // v = current velocity, a = acceleration, t = time
but not distance, which for small enough delta is:
delta_s = v * delta_t
You need to multiply velocity by delta before adding it to to the position.
You are not modeling the physics correctly. Assuming dt is small enough this will provide a "good enough" approximation.
curV // current velocity
accel // global acceleration constant
terminal // terminal velocity for object
dt // delta time in seconds
fall(dt):
deltaV = accel * dt // Change in velocity in a vacuum
newV = curV + deltaV // New velocity
if (newV < terminal) newV = terminal // Don't exceed downwards terminal velocity
y = y + dt * (curV+newV)/2 // New position
curV = newV // Save new velocity as current
It ignores the complexities of things like acceleration decreasing as you approach terminal velocity. The big difference between this and yours is the appearance of dt twice, once in calculating deltaV and then again in calculating the new vertical position.
I try to create a very simple physics engine for my study (processing used for a interactive installation).
The target is to have a ground covered with balls that you can throw around with gestures (based on Kinect information).
Therefor I need to do some basic physic simulation like bouncing and thats what I started with. So there are just balls falling down and bouncing. I simulated the air resistance with a simple 0.995f multiplication on the speed if the ball moves up. Works nice and looks realistic. The main problem is, that the balls never stay calm on the ground. Instead they start to tremble on the ground. That means there is a movement of 1 or 2 pixels up and down.
How can I prevent that without implementing some "borders" on which I set the position directly to the bottom and the speed to zero?
My applet:
public class BubblePhysicApplet extends PApplet {
public static int width = 640;
public static int height = 480;
long lastTime = -1;
Bubble[] mBubbles = new Bubble[10];
Random mRandom = new Random();
public void setup() {
// size(width, height, OPENGL);
size(width, height, P2D);
for (int i = 0; i < mBubbles.length; i++) {
mBubbles[i] = new Bubble(mRandom.nextInt(width), mRandom.nextInt(height), 50);
}
lastTime = System.currentTimeMillis();
}
public void draw() {
background(0);
long tmp = System.currentTimeMillis();
long elapsed = tmp - lastTime;
for (Bubble bubble : mBubbles) {
bubble.animate(elapsed);
bubble.draw(this);
}
lastTime = System.currentTimeMillis();
}
}
The ball/bubble:
public class Bubble {
float mX;
float mY;
float mSize;
float mSpeedX = 0;
float mSpeedY = 0;
public Bubble(int x, int y, int size) {
mX = x;
mY = y;
mSize = size;
}
public void draw(PApplet applet) {
applet.stroke(255);
applet.noFill();
applet.ellipseMode(PApplet.CENTER);
applet.ellipse(mX, mY, mSize, mSize);
}
public void animate(long elapsed) {
updateSpeedY(elapsed);
if (mSpeedX != 0 || mSpeedY != 0) {
checkBorders();
}
}
private void checkBorders() {
if (mY > BubblePhysicApplet.height - mSize / 2) {
mY = 2 * BubblePhysicApplet.height - (mY + mSize);
mSpeedY = -mSpeedY;
}
if (mX > BubblePhysicApplet.width) {
mX = BubblePhysicApplet.width - (mX - BubblePhysicApplet.width);
mSpeedX = -mSpeedX;
}
}
private void updateSpeedX() {
}
private void updateSpeedY(long elapsed) {
mSpeedY += (elapsed / 1000f) * 9.81f;
if (mSpeedY < 0) {
mSpeedY *= 0.95f;
}
mY += mSpeedY;
}
}
It's not only air resistance that slows the ball down, but the fact that it's not perfectly elastic as this line suggests: mSpeedY = -mSpeedY;
The ball absorbs energy when it squishes against the floor before it bounces back, so it doesn't bounce as high. Try a real super ball. I seem to remember it only bounces 80% as high or so. You might try:
mSpeedY = - (0.8 * mSpeedY);
you have to fix your check borders method, read this answer I just gave a complete formulas needed for realistic physical simulation. and it's also more realistic if you move objects using hist method (p = v*dt + 1/2*adtdt)
The problem is that in updateSpeedY we have mSpeedY += (elapsed / 1000f) * 9.81f; even when there is a collision. That said collision is detected later in checkBorders where the speed is flipped mSpeedY = -mSpeedY;. The problem is that if the ball is hitting the floor with a speed near 0, it bounces with a speed of 0 + (elapsed / 1000f) * 9.81f;!!
You have to rethink your code.
in the same fashion you used a friction factor for the air, you can also include a friction factor for the contact with the ground, and which even higher values, so at each contact, it starts to lose eneger rapidly and finally stops
I know the title is an eyebrow raiser but it's mostly a side effect problem. I'm writing an Android app that I can use with the math I've been learning in my physics class. It's a 2D bouncing ball app. I'm using the time corrected Verlet integrator with an impulse on the floor which is the bottom of the screen. I'm adding friction and bounciness so that the ball eventually reaches 0 velocity.
The problem shows up when the ball is resting on the floor and a "significant" time step change happens. The integrator adjusts the velocities flawlessly and ends up firing the impulse on the floor. The impulse fires when the velocity's abs value is greater than 2.5. Android's GC usually causes a time adjusted -18 velocity.
Any help is appreciated. I realize the code structure could be better but I'm just trying to visualize and apply physics for fun. Thank you.
// The loop
public void run() {
if(mRenderables != null) {
final long time = SystemClock.uptimeMillis();
final long timeDelta = time - mLastTime;
if(mLastTime != 0) {
final float timeDeltaSeconds = timeDelta / 1000.0f;
if(mLastTimeDeltaSec != 0) {
for(short i = 0; i < mRendLength; i++) {
Ball b1 = mRenderables[i];
// Acceleration is gauged by screen's tilt angle
final float gravityX = -mSV.mSensorX * b1.MASS;
final float gravityY = -mSV.mSensorY * b1.MASS;
computeVerletMethod(b1, gravityX, gravityY, timeDeltaSeconds, mLastTimeDeltaSec);
}
}
mLastTimeDeltaSec = timeDeltaSeconds;
}
mLastTime = time;
}
}
/*
* Time-Corrected Verlet Integration
* xi+1 = xi + (xi - xi-1) * (dti / dti-1) + a * dti * dti
*/
public void computeVerletMethod(Renderable obj, float gravityX, float gravityY, float dt, float lDT) {
mTmp.x = obj.pos.x;
mTmp.y = obj.pos.y;
obj.vel.x = obj.pos.x - obj.oldPos.x;
obj.vel.y = obj.pos.y - obj.oldPos.y;
// Log "1." here
resolveScreenCollision(obj);
obj.pos.x += obj.FRICTION * (dt / lDT) * obj.vel.x + gravityX * (dt * dt);
obj.pos.y += obj.FRICTION * (dt / lDT) * obj.vel.y + gravityY * (dt * dt);
obj.oldPos.x = mTmp.x;
obj.oldPos.y = mTmp.y;
// Log "2." here
}
// Screen edge detection and resolver
public void resolveScreenCollision(Renderable obj) {
final short xmax = (short) (mSV.mViewWidth - obj.width);
final short ymax = (short) (mSV.mViewHeight - obj.height);
final float x = obj.pos.x;
final float y = obj.pos.y;
// Only testing bottom of screen for now
if (y > ymax) {
// ...
} else if (y < 0.5f) {
if(Math.abs(obj.vel.y) > 2.5f) {
float imp = (obj.MASS * (obj.vel.y * obj.vel.y) / 2) * obj.RESTITUTION / obj.MASS;
obj.vel.y += imp;
// Log "bounce" here
} else {
obj.vel.y = obj.pos.y = obj.oldPos.y = mTmp.y = 0.0f;
}
}
}
Output while ball is resting on the floor and sudden impulse happens
(see code for "log" comments)
1. vel.y: -0.48258796
2. pos.y: -0.42748278 /oldpos.y: 0.0 /dt: 0.016 /ldt: 0.017
1. vel.y: -0.42748278
dalvikvm GC_FOR_MALLOC freed 8536 objects / 585272 byte s in 74ms
2. pos.y: -0.48258796 /oldpos.y: 0.0 /dt: 0.017 /ldt: 0.016
1. vel.y: -0.48258796
2. pos.y: -18.061148 /oldpos.y: 0.0 /dt: 0.104 /ldt: 0.017
1. vel.y: -18.061148
bounce imp: 124.35645
2. pos.y: 13.805508 /oldpos.y: -18.061148 /dt: 0.015 /ldt: 0.104
you shouldn't use a timestep based on how much time occured from the previous calculation because that can cause problems such as this and errors in collision detection, if you don't have that yet. Instead for every update, you should set a "time chunk" or a max amount of time for each update. For example: say you want 30fps and the which is in nanotime about 33333333. so 33333333 = 1 timechunk. so then you could do a while loop
long difftime = System.nanoTime() - lastTime;
static long fpstn = 1000000000 / 30;
static int maxtimes = 10;// This is used to prevent what is commonly known as the spiral of death: the calcutaions are longer that the time you give them. in this case you have to increase the value of a base timechunk in your calculations
for (int i = 0; i < maxtimes; i++) {
if (difftime >= fpstn) {
world.updateVerlet(1);
} else {
world.updateVerlet((float)diffTime / (float)fpstn);
}
difftime -= fpstn;
if (difftime <= 0)
break;
}
It's hard to be sure, but it looks as if the problem isn't the increase in the time step, it's the large time step. You do Verlet integration and bouncing as separate processes, so if the ball start from a resting position on the floor with a large time step, it falls far into the floor, picking up speed, before being reflected into the air. Keep the time step small and you won't have this problem... much.