Ways to smoothly zoom in LibGDX? - java

I'm building a LibGDX game and need to zoom the camera in and out. But when I do camera.zoom = 1.2f;, it just 'jumps' to that value. Instead, I want to have a smooth zoom.
My question:
What's the best way to smoothly zoom?
I want to zoom like this graph below. y=0 is the start cam.zoom, y=100 is the desired cam.zoom and the x-axis shows the time.
Should I use cam.zoom = f(time);? (With some modification to f(x))
time is the total time passed since the beginning of the zoom.
Or is it better to use:
cam.zoom += (desiredZoom - cam.zoom) * delta * scalar;
delta is the time between two frames and scalar is a float that will be adjusted in order to set the speed.
Or is there another, better way? What do you think?
Thanks a lot!

Simplest way I know is use a countdown with an Interpolation.
// member variables:
float timeToCameraZoomTarget, cameraZoomTarget, cameraZoomOrigin, cameraZoomDuration;
private float zoomTo (float newZoom, float duration){
cameraZoomOrigin = camera.zoom;
cameraZoomTarget = newZoom;
timeToCameraZoomTarget = cameraZoomDuration = duration;
}
// in render():
if (timeToCameraZoomTarget >= 0){
timeToCameraZoomTarget -= deltaTime;
float progress = timeToCameraZoomTarget < 0 ? 1 : 1f - timeToCameraZoomTarget / cameraZoomDuration;
camera.zoom = Interpolation.pow3out.apply(cameraZoomOrigin, cameraZoomTarget, progress);
}
You can replace Interpolation.pow3out with any of the functions pictured here.

You can use the MathUtils.lerp method.
Example:
MathUtils.lerp(currentZoom,targetZoom,progress);
Increase the progress value to change faster or lower to slow down speed the value changes.

Related

Why does my 1D gravity simulation not act like a pendulum?

My gravity simulation acts more like a gravity slingshot. Once the two bodies pass over each other, they accelerate far more than they decelerate on the other side. It's not balanced. It won't oscillate around an attractor.
How do other gravity simulators get around it? example: http://www.testtubegames.com/gravity.html, if you create 2 bodies they will just oscillate back and forth, not drifting any further apart than their original distance even though they move through each other as in my example.
That's how it should be. But in my case, as soon as they get close they just shoot away from each other to the edges of the imaginary galaxy never to come back for a gazillion years.
edit: Here is a video of the bug https://imgur.com/PhhRhP7
Here is a minimal test case to run in processing.
//Constants:
float v;
int unit = 1; //1 pixel = 1 meter
float x;
float y;
float alx;
float aly;
float g = 6.67408 * pow(10, -11) * sq(unit); //g constant
float m1 = (1 * pow(10, 15)); // attractor mass
float m2 = 1; //object mass
void setup() {
size (200,200);
a = 0;
v = 0;
x = width/2; // object x
y = 0; // object y
alx = width/2; //attractor x
aly = height/2; //attractor y
}
void draw() {
background(0);
getAcc();
applyAcc();
fill(0,255,0);
ellipse(x, y, 10, 10); //object
fill(255,0,0);
ellipse(alx, aly, 10, 10); //attractor
}
void applyAcc() {
a = getAcc();
v += a * (1/frameRate); //add acceleration to velocity
y += v * (1/frameRate); //add velocity to Y
a = 0;
}
float getAcc() {
float a = 0;
float d = dist(x, y, alx, aly); //distance to attractor
float gravity = (g * m1 * m2)/sq(d); //gforce
a += gravity/m2;
if (y > aly){
a *= -1;}
return a;
}
Your distance doesn't include width of the object, so the objects effectively occupy the same space at the same time.
The way to "cap gravity" as suggested above is add a normal force when the outer edges touch, if it's a physical simulation.
You should get into the habit of debugging your code. Which line of code is behaving differently from what you expected?
For example, if I were you I would start by printing out the value of gravity every time you calculate it:
float gravity = (g * m1 * m2)/sq(d); //gforce
println(gravity);
You'll notice that your gravity value skyrockets as your circles get closer to each other. And this makes sense, because you're dividing by sq(d). Ad d gets smaller, your gravity increases.
You could simply cap your gravity value so it doesn't go off the charts anymore:
float gravity = (g * m1 * m2)/sq(d);
if(gravity > 100){
gravity = 100;
}
Alternatively you could cap d so it never goes below a certain value, but the result is the same.
In the end you'll find that this is not going to be as easy as you expected. You're going to have to tune the parameters quite a bit so your simulation works how you want.
Working demo here: https://beta.observablehq.com/#shaunlebron/1d-gravity
I followed the solution posted by the author of the sim that inspired this question here:
-First off, shrinking the timestep is always helpful. My simulation runs, as a baseline, about 40 ‘steps’ per frame, and 30 frames per second.
-To deal with the exact issue you talk about, I think modeling the bodies not as pure point masses - but rather spherical masses with a certain radius will be vital. That prevents the force of gravity from diverging to infinity. So, for instance, if you drop an asteroid into a star in my simulation (with collisions turned off), the force of gravity will increase as the asteroid gets closer, up until it reaches the surface of the star, at which point the force will begin to decrease. And the moment it’s at the center of the star (or nearby), the force will be zero (or nearly zero) - instead of near-infinite.
In my demo, I just completed turned off gravity when two objects are close enough together. Seems to work well enough.

How do I move objects by velocity correctly?

I recently started playing around with android and decided to try make a basic physics simulator, but I have encountered a small issue.
I have my object of Ball, each ball has a velocity vector, and the way I move it is by adding said vector to the ball's location with each tick.
It worked quite well until I noticed an issue with this approach.
When I tried applying gravity to the balls I noticed that when two balls got close to each other one of the balls gets launched great velocity.
After some debugging I found the reason for that happening.
Here is an example how I calculate force of gravity and acceleration:
//for each ball that isn't this ball
for (Ball ball : Ball.balls)
if (ball != this) {
double m1 = this.getMass();
double m2 = ball.getMass();
double distance = this.getLocation().distance(ball.getLocation());
double Fg = 6.674*((m1*m2)/(distance * distance));
Vector direction = ball.getLocation().subtract(this.getLocation()).toVector();
Vector gravity = direction.normalize().multiply(Fg / mass);
this.setVeloctity(this.getVelocity().add(gravity));
}
Here's the problem - when the balls get really close, the force of gravity becomes really high (as it should) thus the velocity also becomes incredibly high, but because I add vector to location each tick, and the vector's value is so high, one of the balls gets ejected.
So that brings me to my questions - is there a better way to move your objects other than just adding vectors? Also, is there a better way to handle gravity?
I appreciate any help you guys could offer.
you can try this :
acceleration.y = force.y / MASS; //to get the acceleration
force.y = MASS * GRAVITY_Constant; // to get the force
displacement.y = velocity.y * dt + (0.5f * acceleration.y * dt * dt); //Verlet integration for y displacment
position.y += displacement.y * 100; //now cal to position
new_acceleration.y = force.y / MASS; //cau the new acc
avg_acceleration.y = 0.5f * (new_acceleration.y + acceleration.y); //get the avg
velocity.y += avg_acceleration.y * dt; // now cal the velocity from the avg
(acceleration,velocity,displacment ,and position) are vectors for your ball .
*note (dt = Delta Time which is the difference time between current frame and the previous one.
You need to study the physics of the problem. Obviously it is a bad idea to add a force or acceleration to a velocity, the phyisical units can not match.
Thus in the most primitive case, with a time step dt, you need to implement
velocity = velocity + acceleration * dt
The next problem to consider is that you need to first accumulate all the forces resp. the resulting accelerations for the state (positions and velocities) of all objects at a given time before changing the the whole state simultaneously for their (approximate) state at the next time step.

Animating translation between two fixed points (Libgdx)

I'm making a 2d game in libgdx and I would like to know what the standard way of moving (translating between two known points) on the screen is.
On a button press, I am trying to animate a diagonal movement of a sprite between two points. I know the x and y coordinates of start and finish point. However I can't figure out the maths that determines where the texture should be in between on each call to render. At the moment my algorithm is sort of like:
textureProperty = new TextureProperty();
firstPtX = textureProperty.currentLocationX
firstPtY = textureProperty.currentLocationY
nextPtX = textureProperty.getNextLocationX()
nextPtX = textureProperty.getNextLocationX()
diffX = nextPtX - firstPtX
diffY = nextPtY - firstPtY
deltaX = diffX/speedFactor // Arbitrary, controlls speed of the translation
deltaX = diffX/speedFactor
renderLocX = textureProperty.renderLocX()
renderLocY = textureProperty.renderLocY()
if(textureProperty.getFirstPoint() != textureProperty.getNextPoint()){
animating = true
}
if (animating) {
newLocationX = renderLocX + deltaX
newLocationY = renderLocY + deltaY
textureProperty.setRenderPoint(renderLocX, renderLocY)
}
if (textureProperty.getRenderPoint() == textureProperty.getNextPoint()){
animating = false
textureProperty.setFirstPoint(textureProperty.getNextPoint())
}
batch.draw(texture, textureProperty.renderLocX(), textureProperty.renderLocY())
However, I can foresee a few issues with this code.
1) Since pixels are integers, if I divide that number by something that doesn't go evenly, it will round. 2) as a result of number 1, it will miss the target.
Also when I do test the animation, the objects moving from point1, miss by a long shot, which suggests something may be wrong with my maths.
Here is what I mean graphically:
Desired outcome:
Actual outcome:
Surely this is a standard problem. I welcome any suggestions.
Let's say you have start coordinates X1,Y1 and end coordinates X2,Y2. And let's say you have some variable p which holds percantage of passed path. So if p == 0 that means you are at X1,Y1 and if p == 100 that means you are at X2, Y2 and if 0<p<100 you are somewhere in between. In that case you can calculate current coordinates depending on p like:
X = X1 + ((X2 - X1)*p)/100;
Y = Y1 + ((Y2 - Y1)*p)/100;
So, you are not basing current coords on previous one, but you always calculate depending on start and end point and percentage of passed path.
First of all you need a Vector2 direction, giving the direction between the 2 points.
This Vector should be normalized, so that it's length is 1:
Vector2 dir = new Vector2(x2-x1,y2-y1).nor();
Then in the render method you need to move the object, which means you need to change it's position. You have the speed (given in distance/seconds), a normalized Vector, giving the direction, and the time since the last update.
So the new position can be calculated like this:
position.x += speed * delta * dir.x;
position.y += speed * delta * dir.y;
Now you only need to limit the position to the target position, so that you don't go to far:
boolean stop = false;
if (position.x >= target.x) {
position.x = target.x;
stop = true;
}
if (position.y >= target.y) {
position.y = target.y;
stop = true;
}
Now to the pixel-problem:
Do not use pixels! Using pixels will make your game resolution dependent.
Use Libgdx Viewport and Camera instead.
This alows you do calculate everything in you own world unit (for example meters) and Libgdx will convert it for you.
I didn't saw any big errors, tho' i saw some like you are comparing two objects using == and !=, But i suggest u to use a.equals(b) and !a.equals(b) like that. And secondly i found that your renderLock coords are always being set same in textureProperty.setRenderPoint(renderLocX, renderLocY) you are assigning the same back. Maybe you were supposed to use newLocation coords.
BTW Thanks for your code, i was searching Something that i got by you <3

LibGDX - Orthogonal Camera won't stop at edge of map

i'm fairly new to libgdx and java but am learning everything I can! I'm working on an orthogonal 2d tiled game. Basically, i've got my character walking around and the camera following him. I am trying to set it up so the camera stops when the character is near the edge of the map so no black space is seen. The camera should stop at the edge and the player should continue moving.
This is my code for moving the camera. It's a bit messy now as i've been trying everything. The map is 30x30.
if (mainPlayer.getPosition().x >= 15 && mainPlayer.getPosition().x <= 30) {
camera.position.x = mainPlayer.getPosition().x;
}
camera.position.y = mainPlayer.getPosition().y;
camera.update();
camera.apply(gl);
This is inside my render method. I've only messed with the x part, so ignore the y for now.
EDIT I think I need to reword that. I know how to make it stop..It works using my code, but I can't figure out how to determine where to stop it. I'm using 15 in my code above which is half the map..which of course doesn't work.
EDIT I think I need to reword that. I know how to make it stop..It
works using my code, but I can't figure out how to determine where to
stop it. I'm using 15 in my code above which is half the map..which of
course doesn't work.
The minimum x would need to be:
map.position.x + camera.viewportWidth/2;
And the maximum x would need to be:
map.position.x+mapwidth-camera.viewportWidth/2;
Assuming the map position is in (0,0) you can just ignore the map.position.x part.
You need to fix the bounds of the Camera rectangle to inside the world rectangle. A function such as below would help:
public void fixBounds() {
float scaledViewportWidthHalfExtent = viewportWidth * zoom * 0.5f;
float scaledViewportHeightHalfExtent = viewportHeight * zoom * 0.5f;
// Horizontal
if (position.x < scaledViewportWidthHalfExtent)
position.x = scaledViewportWidthHalfExtent;
else if (position.x > xmax - scaledViewportWidthHalfExtent)
position.x = xmax - scaledViewportWidthHalfExtent;
// Vertical
if (position.y < scaledViewportHeightHalfExtent)
position.y = scaledViewportHeightHalfExtent;
else if (position.y > ymax - scaledViewportHeightHalfExtent)
position.y = ymax - scaledViewportHeightHalfExtent;
}

LWJGL first person camera using jBullet

I've got a camera set up, and I can move with WASD and rotate the view with the mouse. But now comes the problem: I want to add physics to the camera/player, so that it "interacts" with my other jBullet objects. How do I do that? I thought about creating a RigidBody for the camera and storing the position there, so that jBullet can apply its physics to the camera. Then, when I need to change something (the position), I could simply change it in the RigidBody. But I didn't find any methods for editing the position.
Can you push me in the right direction or maybe give me an example source code?
I was asking the same question myself a few days ago. My solution was as Sierox said. To create a RigidBody of BoxShape and add that to the DynaicsWorld. To move the camera arund, apply force to its rigidbody. I have damping set to .999 for linear and 1 for angular to stop the camera when no force is applied, i.e. the player stops pressing the button.
I also use body.setAngularFactor(0); so the box isn't tumbling all over the place. Also set the mass really low as not to interfere too much with other objects, but still be able to jump on then and run into them, and otherwise be affected by them.
Remember to convert your x,y, and z coordinates to cartesian a plane so you move in the direction of the camera. i.e.
protected void setCartesian(){//set xyz to a standard plane
yrotrad = (float) (yrot / 180 * Math.PI);
xrotrad = (float) (xrot / 180 * Math.PI);
float pd = (float) (Math.PI/180);
x = (float) (-Math.cos(xrot*pd)*Math.sin(yrot*pd));
z = (float) (-Math.cos(xrot*pd)*Math.cos(yrot*pd));
//y = (float) Math.sin(xrot*pd);
}//..
public void forward(){// move forward from position in direction of camera
setCartesian();
x += (Math.sin(yrotrad))*spd;
z -= (Math.cos(yrotrad))*spd;
//y -= (Math.sin(xrotrad))*spd;
body.applyForce(new Vector3f(x,0,z),getThrow());
}//..
public Vector3f getThrow(){// get relative position of the camera
float nx=x,ny=y,nz=z;
float xrotrad, yrotrad;
yrotrad = (float) (yrot / 180 * Math.PI);
xrotrad = (float) (xrot / 180 * Math.PI);
nx += (Math.sin(yrotrad))*2;
nz -= (Math.cos(yrotrad))*2;
ny -= (Math.sin(xrotrad))*2;
return new Vector3f(nx,ny,nz);
}//..
to jump just use body.setLinearVelocity(new Vector3f(0,jumpHt,0)); and set jumpHt to whatever velocity you wish.
I use getThrow to return a vector for other objects i may be "throwing" on screen or carrying. I hope I answered your question and didn't throw in too much non-essential information.I'll try and find the source that gave me this idea. I believe it was on the Bullet forums.
------- EDIT ------
Sorry to have left that part out
once you have the rigid body functioning properly you just have to get it's coordinates and apply that to your camera for example:
float mat[] = new float[16];
Transform t = new Transform();
t = body.getWorldTransform(t);
t.origin.get(mat);
x = mat[0];
y = mat[1];
z = mat[2];
gl.glRotatef(xrot, 1, 0, 0); //rotate our camera on teh x-axis (left and right)
gl.glRotatef(yrot, 0, 1, 0); //rotate our camera on the y-axis (up and down)
gl.glTranslatef(-x, -y, -z); //translate the screen to the position of our camera
In my case I'm using OpenGL for graphics. xrot and yrot represent the pitch and yaw of your camera. the code above gets the world transform in the form of a matrix and for the purposes of the camera you need only to pull the x,y, and z coordinates then apply the transform.
from here, to move the camera, you can set the linear velocity of the rigid body to move the camera or apply force.
Before you read this answer I would like to mention that I have a problem with the solution stated in my answer. You can follow my question about that problem so that you can have the solution too if you use this answer.
So. First, you need to create a new BoxShape:
CollisionShape cameraHolder = new BoxShape(SIZE OF CAMERAHOLDER);
And add it to your world so that it interacts with all the other objects. Now you need to change all the methods about camera movement (not rotation) so that the methods move your cameraHolder but not your camera. Then set the position of your Camera to the position of the cameraHolder.
Again, if you have a problem where you can't move properly, you can check my question and wait for an answer. You also can find a better way of doing this.
If you have problems or did not understand something about the answer, please state it as a comment.

Categories

Resources