libgdx - fixed timestep with interpolation - without box2d - libgdx

I am having some problems implementing fixed timestep with graphic interpolation in my game.
Here is part of the render method:
#Override
public void render(float delta)
{
double newTime = TimeUtils.millis() / 1000.0;
double frameTime = Math.min(newTime - currentTime, 0.25);
accumulator += frameTime;
currentTime = newTime;
while (accumulator >= step)
{
updateObjects(step);
accumulator -= step;
}
double alpha = accumulator / step;
interpolateObjects((float)alpha);
}
Here is updateObjects:
for (int i = 0; i < world.level.gameObjects.size(); i++)
{
GameObject go = world.level.gameObjects.get(i);
go.prevPosition.set(go.position);//save previous position
go.update(delta);
}
interpolateObjects:
for (int i = 0; i < world.level.gameObjects.size(); i++)
{
GameObject go = world.level.gameObjects.get(i);
go.position.lerp(go.prevPosition, alpha);
}
And then objects are rendered using position
As far as i can tell this should work, but it doesn't.
On high fps (200-400) everything is too slow, movement isn't even visible, i can just see that position is changing by 0.0001 or something like that
On low fps (10-20), movement is visible but again objects are very slow...
If i disable interpolation, than everything works as it should (on any fps), but then everything is jittery.
So the problem is somewhere in interpolation.

Your interpolation go.position.lerp(go.prevPosition, alpha) is set up to assume that prevPosition was last updated at an exact multiple of step, but then when you update prevPosition like this go.prevPosition.set(go.position) you are destroying that contract on the first update of the frame. It also looks like you are lerping backwards (from position to the previous position).
I think you need a third vector so the last interpolated value is guaranteed not to influence your fixed time updates. Here I'll call it interpPosition, and it will be used for drawing instead of position.
You actually seem to technically be extrapolating (not interpolating) the value, since you are not updating ahead of time and your alpha is calculated from time left in the accumulator. If you want to linearly extrapolate from the last two positions calculated, you can do it like this (note the 1+alpha to extrapolate):
for (int i = 0; i < world.level.gameObjects.size(); i++)
{
GameObject go = world.level.gameObjects.get(i);
interpPosition.set(go.prevPosition).lerp(go.position, 1 + alpha);
}
Depending on the speed of your simulation (and how fast objects can accelerate), this might still look jerky. I think a smoother, but computationally slower way to do this would be to do a fully calculated update using alpha instead of step time, and storing that in the interpPosition vector. But only do that if necessary.

Related

Gravity trajectory with already known destination

I have been spending several days trying to solve this problem and I am about to go mad. Your help would be very much appreciated.
I am using a 2D stage in libgdx. I want to move actors (or sprite) to this stage with a "gravity" display effect: for example for actor1, his initial coordinates are (0, 0), destination coordinates would be (100, 50), and I want to move this actor to this destination with a gravity trajectory effect. Then I want to use the same gravity for actor2 moving from (0, 0) to (25, 75), then actor3 from (0, 0) to (200, 75) etc.
I managed to apply a gravity trajectory to any actor based on this well known loop:
setX(getX() + velocity.x);
setY(getY() + velocity.y);
velocity.y += getGravity().y * delta;
So tweaking the gravity value would modify the trajectory. It works fine.
Now, as I said earlier I want to give every actors a unique trajectory given their predetermined destination.
So I have tried to find a formula to determine the x and y for each actor at every frame of their trajectory
I am using the following static parameters:
gravity.y : same for all actors
delay : the amount of frames during which each actor moves between his initial coordinates and his destination coordinates. Same value for all actors too
First I calculate the velocity with this SUPER UGLY formula that I am absolutely not proud of:
velocity = new Vector2 ( (destinationx - b.getX() )/time, initdisty/time + ( Math.sqrt(delta*1000)*time / ( 500/Math.abs(gravity) ) ));
where delta = Gdx.graphics.getDeltaTime();
Then I apply this velocity each frame to calculate the x and y of each actor:
public void act(float delta){
for (int i=0; i<delay; i++) {
setX(getX() + velocity.x);
setY(getY() + velocity.y);
velocity.y += gravity.y * delta;
}
}
It KIND OF work, but of course, this can not be a long term solid solution. Calculating the x and y for each frame for each actor (there can be 5-6 actors moving at the same time) doesn't look good at all.
The main problem is that the trajectories are good on computer with consistent 60FPS, they are okay on a tablet, but on a phone with limited memory and 30 < fps < 60, the trajectories become terribly wrong.
After reading several blog posts, it seems like I could avoid the multi device memory fps problems by removing the delta parameter from my formulas, but I haven't found how. And it still doesn't give me a strong long term solution to calculate the trajectory with predetermined destination coordinates.
Thanks for reading and for your time, please let me know if this is unclear I'll do my best to explain better.
Cause of the problem
Maintaining both position and velocity leads to discretization of the system resulting in quantization error. So you will experience inconsistent behavior by your current method when fps fluctuates.
Solution
All you need is to reduce your number of state variables to only two i.e. don't store current velocity. It is causing the errors in final position.
In stead use parametric form of the trajectory.
v = u + at
and
s = ut + ½at²
Implementation
Suppose you want to go from (sourceX, sourceY) to (targetX, targetY) in time 'totalTime'.
Calculate initial velocity.
Vector2 initialVelocity = new Vector2((targetX - sourceX) / totalTime,
(targetY - sourceY) / totalTime - gravity * totalTime / 2);
float currentTime = 0;
In each iteration, calculate position directly and keep track of currentTime.
public void act(float delta){
if (currentTime < totalTime) {
currentTime += delta;
setX((initialVelocity.x + gravity.x * currentTime / 2) * currentTime);
setY((initialVelocity.y + gravity.y * currentTime / 2) * currentTime);
} else {
setX(targetX);
setY(targetY);
}
}

steering behavior and passed time

I'm trying to implement steering behaviors but I have a problem with "including" the passed time in the calculation and then allowing me to control the speed of the game. I have seen various sources of steering behaviors and I've come up with this (Arrive behavior):
var toTarget:Vector2D = new Vector2D( mEntity.targetPosition.x, mEntity.targetPosition.y );
toTarget.subtract( mEntity.position );
var dist:Number = toTarget.length;
toTarget.normalize().scale( mEntity.maxSpeed );
if( dist < slowDownDist ) {
toTarget.scale( dist / slowDownDist );
}
return toTarget.subtract( mEntity.velocity );
And here's the advanceTime method of MovingEntity:
var steeringForce:Vector2D = mSteering.calculate();
steeringForce.x /= mMass;
steeringForce.y /= mMass;
steeringForce.scale( time );
mVelocity.x += steeringForce.x;
mVelocity.y += steeringForce.y;
x += mVelocity.x * time;
y += mVelocity.y * time;
The steering force should be (at some point) directly opposite to the entity's velocity and thus making it stop. The problem I see and do not understand is that in order to simulate acceleration the force needs to be divided by mass and also to consider the passed time it needs to be multiplied by that time - but this scales the steering force down a lot and causes the entity to overshoot the target spot instead of stopping there so then it returns back which effectively causes oscillation. If I do not multiply the force with the time then the moving entity behaves slightly differently depending on the game speed.
By second (?) Newton's law F = m * a where F is force, m is mass and a is acceleration. Force is measured in newtons, mass in kilograms and acceleration in meters per second per second. So, you just need to apply bigger force, and lave the calculation as is.

Nape Moving Platform

Okay Im relatively new to nape and Im in the process of making a game, I've made a Body called platform of type KINEMATIC, and I simply want to move it back a forth in a certain range on the stage. Can somebody please see where im going wrong , thanks.
private function enterFrameHandler(ev:Event):void
{
if (movingPlatform.position.x <= 150 )
{
movingPlatform.position.x += 10;
}
if (movingPlatform.position.x >= 260)
{
movingPlatform.velocity.x -= 10;
}
}
First of in one of the if blocks you are incrementing position.x by 10 in the other one you are decrementing velocity.x by 10. I guess you meant position.x in both.
Secondly, imagine movingPlatform.position.x is 150 and your enterFrameHandler runs once. movingPlatform.position.x will become 160 and on the next time enterFrameHandler is called none of the if blocks will execute since 160 is neither less than or equal to 150 or greater than or equal to 260.
You can use the velocity to indicate the side its moving and invert it once you go beyond an edge, something like :
// assuming velocity is (1,0)
private function enterFrameHandler(ev:Event):void {
if (movingPlatform.position.x <= 150 || movingPlatform.position.x >= 260) {
movingPlatform.velocity.x = -movingPlatform.velocity.x;
}
movingPlatform.position.x += movingPlatform.velocity.x;
}
Obviously this might cause problems if the object is already at let's say x=100, it will just keep inverting it's velocity, so either make sure you place it between 150-260 or add additional checks to prevent it from inverting it's direction more than once.
This might be a better way of doing it :
// assuming velocity is (1,0)
private function enterFrameHandler(ev:Event):void {
if (movingPlatform.position.x <= 150) {
movingPlatform.velocity.x = 1;
} else if (movingPlatform.position.x >= 260) {
movingPlatform.velocity.x = -1;
}
movingPlatform.position.x += movingPlatform.velocity.x;
}
In general:
Kinematic bodies are supposed to be moved solely with velocity, if you change their position directly then they are not really moving as much as they are 'teleporting' and as far as the physics is concerned their velocity is still exactly 0 so things like collisions and friction will not work as you might expect.
If you want to still work with positions instead of velocities, then there's the method setVelocityFromTarget on the Body class which is designed for kinematics:
body.setVelocityFromTarget(targetPosition, targetRotation, deltaTime);
where deltaTime is the time step you're about to use in the following call to space.step();
All this is really doing is setting an appropriate velocity and angularVel based on the current position/rotation, the target position/rotation and the amount of time it should take to get there.

How to code an audio envelope (attack time, fade in) for a Sound object?

I made a simple sine wave tone generator. The problem is that when the tone is played a strong click can be heard, and I need to implement a fast fade in (attack time) to avoid this.
I tried using tweening (like tweenmax) but it induces distortion to the audio (maybe the steps in the tweening?). I found some vague tutorials on the subject but nothing regarding attack time specifically.
How can I do this?
For the fade to sound smooth, it has to be incremented on a per-sample basis, inside your synthesis loop. A tween engine may update many times a second, but your ear can still hear the changes as a click.
In your sampleData event handler, you will have to multiply the individual samples by a volume modifier, with the range of 0 to 1, incrementing for every sample.
To quickly fade in the sound, start by setting the volume to 0, and adding a small value to it for each sample, until it reaches 1.0. You can later expand this into a more complex envelope controller.
This is a rough example of what you might start with:
for( i = 0; i < length; i++ ) {
_count++;
factor = _frequency * Math.PI * 2 / 4400;
volume += 1.0 / 4400;
if( volume > 1.0 ) volume = 1.0; //don't actually do it like this, ok?
n = Math.sin( (_count) * factor ) * volume;
_buffer.writeFloat(n);
_buffer.writeFloat(n);
}
NOTE: I haven't tested this snippet, nor would I recommend using it for production. It's just to show you roughly what I mean.
Another technique that may work for you is to put an ease / delay on the volume. Use a volumeEase variable that always 'chases' the target volume at a certain speed. This will prevent clicks when changing volumes and can be used to make longer envelopes:
var volume:Number = 0; // the target volume
var volumeEase:Number = 1.0; // the value to use in the signal math
var volumeEaseSpeed:Number = 0.001; // tweak this to control responsiveness of ease
for( i = 0; i < length; i++ ) {
_count++;
// bring the volumeEase closer to the target:
volumeEase += ( volume - volumeEase ) * volumeEaseSpeed;
factor = _frequency * Math.PI * 2 / 4400;
//use volumeEase in the maths, rather than 'volume':
n = Math.sin( (_count) * factor ) * volumeEase;
_buffer.writeFloat(n);
_buffer.writeFloat(n);
}
If you wanted to, you could just use a linear interpolation, and just go 'toward' the target at a constant speed.
Once again, the snippet is not tested, so you may have to tweak volumeEaseSpeed.

How to make smooth moving using as3?

I have loaded some images through XML and attached into dynamically created MovieClips named mc0,mc1,mc2...etc.
_loader.removeEventListener(ProgressEvent.PROGRESS, onLoadingAction);
count++;
var img:Bitmap = Bitmap(e.target.content);
img.cacheAsBitmap = true;
img.smoothing = true;
img.alpha = 0;
TweenLite.to(MovieClip(my_mc.getChildByName("mc"+count)).addChild(img),1, {alpha:1,ease:Quint.easeIn});
and within ENTER_FRAME handler
for (i=0; i < mc.numChildren; i++)
{
my_mc.getChildAt(i).x -= Math.round((mouseX-stage.stageWidth/2)*.006);
}
Everthing works fine. But it is shaking so that it was not looking good.
How do I achieve smooth movement?
One solution I've used is to round the (x,y) position to the closest integer. No matter that you've added smoothing to your bitmap and cached it, rounding could make it feel less choppy and way smoother.
Another thing you need to be careful is the dimensions of the images. Images that have an odd dimension won't be smoothed the same way as images with even dimensions. Check how to workaround this in my blog post Flash Smoothing Issue.
Since Flash has a variable frame rate (in the sense that it will drop frames), one shouldn't depend on the entering of a frame as a unit of action. Rather, it would be wiser to calculate the elapsed time explicitly.
For instance, in the enter frame handler:
var currentTime:Number = (new Date()).time;
for (i=0; i < mc.numChildren; i++)
{
my_mc.getChildAt(i).x -= speed * (currentTime - lastTime); // speed is in px/ms
}
lastTime = currentTime;
where you have the variable lastTime declared somewhere in a persistent scope:
var lastTime:Number = (new Date()).time;
I don't know if this addresses what you are calling "shaking", but it's at least something to consider.