Framerate-independent sine wave pattern - java

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.

Related

Air Resistance In this simulation causes the velocity to rise drastically

The issue I have is that I'm attempting to add drag to an object in this basic physics simulation (Java [Processing]), but once I add the appropriate formula, it causes the objects velocity to increase drastically in the opposite direction. Of course the problem is that drag for some reason is being calculated too high but I'm not sure why thats happening as I'm using the real world equation.
void setup(){size(1280,720);}
class Circle{
float x,y,r,m,dx,dy,ax,ay,fx,fy;
Circle(float xPos, float yPos, float Radius, float Mass){
x = xPos;
y = yPos;
r = Radius;
m = Mass;
}
void ADD_DRAG(){
fx -= 0.5 * 1.225 * dx * dx * 0.5 * r * PI;
fy -= 0.5 * 1.225 * dy * dy * 0.5 * r * PI;
}
void update(){
ADD_DRAG();
ax = fx / m;
ay = fy / m;
dx += ax / frameRate;
dy += ay / frameRate;
x += dx / frameRate;
y += dy / frameRate;
}
}
Circle[] SceneObjects = {new Circle(50,50,20,20000),new Circle(50,50,2,20)};
void draw(){
background(51);
for (Circle c : SceneObjects){
c.update();
circle(c.x * 3,c.y * 3,c.r * 3);
}
}
void mouseClicked(){
if(SceneObjects[1].fx != 2000)
SceneObjects[1].fx = 2000;
else
SceneObjects[1].fx = 0;
}
This is the code, essentially there is a Circle class which stores the objects properties and then the forces applies are updated each draw loop. The mouseClicked void is just for testing by adding a force to the objects. All and any help is appreciated, thanks!
Maths I am Using:
Rearranged F=ma for ax = fx / m;
Acceleration * time = Speed for dx += ax / frameRate; (frameRate is 1/time)
Distance = Speed * time = for x += dx / frameRate; (same as above)
For drag im using this equation https://www.grc.nasa.gov/WWW/K-12/rocket/drageq.html with the constants eg air density etc added as seen.
There are a couple of things wrong here.
You haven't given us numbers (or a minimal complete example), but the vector algebra is off.
Yes, the acceleration is f = -kv2, and |v|2 = vx2 + vy2, but that doesn't mean that you can decompose f into fx=kvx2 and fy=kvy2. Not only is your magnitude off, but your acceleration is now not (in general) aligned with the motion; the path of your projectile will tend to curve toward a diagonal between the axes (e.g. x=y).
Also, your code always gives acceleration in the negative x and negative y directions. If your projectile happens to start out going that way, your version of air resistance will speed it up.
Finally, your time interval may simply be too large.
There is a better way. The differential equation is v' = -k v|v|, and the exact solution is v = (1/kt) z, (with appropriate choice of the starting time) where z is the unit direction vector. (I don't know how to put a caret over a letter.) This leads to v(t) = (1/t)v(t=1.0)
So you can either work out a fictional time t0 and calculate each new velocity using 1/(kt), or you can calculate the new velocity from the previous velocity: vn+1 =vn/(kd vn + 1), where d is the time interval. (And then of course you have to decompose v into vx and vy properly.)
If you're not familiar with vector algebra, this may seem confusing, but you can't get an air-resistance sim to work without learning the basics.

Why sprite twitches?

Wrote a function that calculates the coordinates of a sprite that moves in a circle.
Well, the formula is elementary
x = x0 + R * cos (t * a + a0)
y = y0 + R * sin (t * a + a0)
The function looks like this
public void pointDefinition(float delta)
{
sprite.setPosition(Gdx.graphics.getWidth()/2+(85*(float)Math.cos(i*delta)), Gdx.graphics.getHeight()/2+(85*(float)Math.sin(i*delta)));
i = i + 0.5;
}
I call it in render ()
In the end, everything works, but the sprite somehow nervously twitches.
Here is a video that demonstrates this.
video
The following content is sent to the delta variable.
pointDefinition(Math.min(Gdx.graphics.getDeltaTime(), 1 / 60f));
I can not understand the reason
You should increment i with delta instead of using delta as a parameter in the formula:
public void pointDefinition(float delta)
{
sprite.setPosition((Gdx.graphics.getWidth() / 2) + (85 * (float)Math.cos(i)), (Gdx.graphics.getHeight() / 2) + (85 * (float)Math.sin(i)));
i = i + delta;
}
After all, delta will have different values each time, which is what is causing your little icon to move back sometimes.
To understand what is happening, imagine these two iterations:
First iteration
i = 2
delta = 0.0025
In this case, i * delta will be 0.005.
Second iteration
i = 2.5
delta = 0.0017
In this case, i * delta will be 0.00425.
As you can see, the value of i * delta, which you are using as the parameter of Math.sin() and Math.cos(), decreased in the second iteration, causing your icon to jitter.
To keep the animation consistent, it is important to make sure the parameter you pass to these two functions (Math.sin() and Math.cos()) only increases with each iteration!

Android game fixed timestep gitter

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 :)

Frame rate independent gravity

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.

How to avoid undesired additional velocity from time step change with Verlet integrator

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.

Categories

Resources