How do I move objects by velocity correctly? - java

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.

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.

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

Delta-time lag compensation messing up my Spinner for my game

Inside my game, I have a spinner, just like the ones in Mario.
When the game doesn't lag, the spinner functions perfectly and rotates in a full 360 degrees circle at constant speed.
However, when it lags (which happens a lot for Android version), gaps start appearing between the different projectiles and it doesn't even rotate in a circle anymore, it just rotates in a distorted elliptical pattern.
Here is my Java code for the Spinner
helper += speedVariable * 1f;
speedY = (float) (speedVariable *helper2 * Math.cos(Math.toRadians(helper)));
speedX = (float) (speedVariable * -1 * helper2 * Math.sin(Math.toRadians(helper)));
setX(getX() + (speedX * delta) + ((Background.bg.getSpeedX() * 5) * delta));
setY(getY() + (speedY * delta));
float helper is the field that is inside the cosine and sine functions that make the spinner spin
speedVariable controls the speed of revolution
helper2 sets the radius of which it rotates
Am I doing anything wrong? Thanks!
Don't set the new position relative to the previous one. You want to stay on a circle around a central point, so it's a lot easier to reliably calculate the position from that point:
setX(getCenterX() + xFromCenter + (Background.bg.getSpeedX() * 5));
setY(getCenterY() + yFromCenter);
(I'm not sure what the Background part is about, I just assumed it should be kept.)
xFromCenter and yFromCenter are the cosine and sine of the rotation. In your original code, you had the rotation in helper, so let's keep it like that. But you should apply delta to its increment, since you want to rotate the same amount of degrees in the same amount of seconds.
helper += speedVariable * delta;
Think of it like this: The delta should directly influence how much it rotates, not how much it moves on the x and y axis (as those values don't change linearly with time).
Then you can just get xFromCenter as you got speedX, except there's no need for speed there now, and do the same for Y. Though I'd set the X by cos and Y by sin, not the other way around - that's the typical usage, with angles measured from 0 at the right side. (The difference is just a 90 degrees rotation.)
Also, a small matter: you could use the MathUtils.sinDeg() and cosDeg() functions when calculating, so you don't need to convert to radians and cast the result back to float. It's a bit more readable.
The entire code:
helper += speedVariable * delta;
xFromCenter = helper2 * MathUtils.cosDeg(helper));
yFromCenter = -1 * helper2 * MathUtils.sinDeg(helper));
setX(getCenterX() + xFromCenter + (Background.bg.getSpeedX() * 5));
setY(getCenterY() + yFromCenter);
You might need to play around with the constants (helper2, speedVariable, -1 multiplication) and possibly undo the swapping of X and Y to get exactly what you want, but this should keep the objects on a circle under all conditions.

Move an object based on acceleration

i want to use an acceleration algorithm to change the speed of a supposed aicraft to move it in a 2D environment. In example:
positionX = positionX + (speed based on acceleration);
positionY = positionY + (speed based on acceleration);
My problem is that if i do it like that the result, assuming the speed is 50, will be position += 50, which is entirely wrong since i don't want to use speed as the number of X it will move on the axis. I want the speed to be some sort of basis for the axis numbers.
In example, if lets say speed is 50 and 50 speed means 3 X per movement then that means
positionX + speed = positionX+3;
I want to create that in code along with an acceleration method that will increase the speed by a percentage.
So my question is how to make speed as a reference point kind of thing.
Keep it simple. The physics aren't difficult. The "math" is no more difficult than multiplying and adding.
You want to deal with changes in velocity and position over increments of time.
Position, velocity, and acceleration are vector quantities. In your 2D world, that means that each one has a component in the x- and y-directions.
So if you increment your time:
t1 = t0 + dt
Your position will change like this if the velocity is constant over that time increment dt:
(ux1, uy1) = (ux0, uy0) + (vx0*dt, vy0*dt)
The velocity will change like this if the acceleration is constant over that time increment dt:
(vx1, vy1) = (vx0, vy0) + (ax0*dt, ay0*dt)
Update your accelerations if there are forces involved using Newton's law:
(ax0, ay0) = (fx0/m, fy0/m)
where m is the mass of the body.
Update the positions, velocities, and accelerations at the end of the time step and repeat.
This assumes that using the values for acceleration and velocity at the start of the step is accurate enough. This will limit you to relatively smaller time steps. (It's called "explicit integration".)
Here's an example. You have a cannon at (x, y) = (0, 0) with a cannonball of mass 20 lbm inside it. The cannon is inclined up from the horizontal at 30 degrees. We'll neglect air resistance, so there's no force in the x-direction acting on the cannonball. Only gravity (-32.2 ft/sec^2) will act in the y-direction.
When the cannon goes off, it'll launch the cannonball with an initial speed of 40 ft/sec. The (vx, vy) components are (40*cos(30 degrees), 40*sin(30 degrees)) = (34.64 ft/sec, 20 ft/sec)
So if you plug into our equations for a time step of 0.1 second:
(ax0, ay0) = (0, -32.2 ft/sec^2)
(vx1, vy1) = (vx0, vy0) + (ax0, ay0)*dt = (34.64 ft/sec, 20 ft/sec) + (0, -3.22 ft/sec) = (34.64, 16.78)
(ux1, uy1) = (ux0, uy0) + (vx0, vy0)*dt = (3.464 ft, 1.678 ft)
Take another time step of 0.1 seconds with these values as the start. Rinse, repeat....
You do this individually for both x- and y- axes.
You can make this slightly more real by making the initial height of the cannon ball equal to half the diameter of your cannon wheels.
You can add a small negative acceleration in the x-direction to simulate wind resistance.
Assume your target is off to the right along the x-axis.
If you fire with the cannon pointing straight up the equations will show the ball going up, slowing down under it reaches its apex, and then coming straight down. No hit, except perhaps on your head and the cannon.
If you fire with the cannon horizontal, the equations say the ball with move with constant velocity in the x-direction and only fall the initial height of the cannon. Your enemies will taunt you: "Air ball! Air ball!"
So if you want the ball to intersect with the ground (a.k.a. reach position y = 0) within some blast radius of your target's location, you'll have to play with the initial speed and the angle of the cannon from the horizontal.
You just need to use the equations of movement for each axis:
x(t)= x0 + v0*t + 1/2*a*t^2 #where x0 is the initial position, v0 is the initial velocity and a is the acceleration you are considering, all relatively to an axis
Now you need to define the instants at which you are calculating the position, as well as writing the values of velocity and acceleration in the correct units as #markusw suggested.
like this:
double calculateSpeed(double value) {
return value / 16.66;
}
And call it like this:
positionX = positionX + calculateSpeed(50);

Mathematical Vectors and Rotations (Topdown java game dev - physics problem)

I've been working on a top down car game for quite a while now, and it seems it always comes back to being able to do one thing properly. In my instance it's getting my car physics properly done.
I'm having a problem with my current rotation not being handled properly. I know the problem lies in the fact that my magnitude is 0 while multiplying it by Math.cos/sin direction, but I simply have no idea how to fix it.
This is the current underlying code.
private void move(int deltaTime) {
double secondsElapsed = (deltaTime / 1000.0);// seconds since last update
double speed = velocity.magnitude();
double magnitude = 0;
if (up)
magnitude = 100.0;
if (down)
magnitude = -100.0;
if (right)
direction += rotationSpeed * (speed/topspeed);// * secondsElapsed;
if (left)
direction -= rotationSpeed * (speed/topspeed);// * secondsElapsed;
double dir = Math.toRadians(direction - 90);
acceleration = new Vector2D(magnitude * Math.cos(dir), magnitude * Math.sin(dir));
Vector2D deltaA = acceleration.scale(secondsElapsed);
velocity = velocity.add(deltaA);
if (speed < 1.5 && speed != 0)
velocity.setLength(0);
Vector2D deltaP = velocity.scale(secondsElapsed);
position = position.add(deltaP);
...
}
My vector class emulates vector basics - including addition subtraction, multiplying by scalars... etc.
To re-iterate the underlying problem - that is magnitude * Math.cos(dir) = 0 when magnitude is 0, thus when a player only presses right or left arrow keys with no 'acceleration' direction doesn't change.
If anyone needs more information you can find it at
http://www.java-gaming.org/index.php/topic,23930.0.html
Yes, those physics calculations are all mixed up. The fundamental problem is that, as you've realized, multiplying the acceleration by the direction is wrong. This is because your "direction" is not just the direction the car is accelerating; it's the direction the car is moving.
The easiest way to straighten this out is to start by considering acceleration and steering separately. First, acceleration: For this, you've just got a speed, and you've got "up" and "down" keys. For that, the code looks like this (including your threshold code to reduce near-zero speeds to zero):
if (up)
acceleration = 100.0;
if (down)
acceleration = -100.0;
speed += acceleration * secondsElapsed;
if (abs(speed) < 1.5) speed = 0;
Separately, you have steering, which changes the direction of the car's motion -- that is, it changes the unit vector you multiply the speed by to get the velocity. I've also taken the liberty of modifying your variable names a little bit to look more like the acceleration part of the code, and clarify what they mean.
if (right)
rotationRate = maxRotationSpeed * (speed/topspeed);
if (left)
rotationRate = maxRotationSpeed * (speed/topspeed);
direction += rotationRate * secondsElapsed;
double dir = Math.toRadians(direction - 90);
velocity = new Vector2D(speed * Math.cos(dir), speed * Math.sin(dir));
You can simply combine these two pieces, using the speed from the first part in the velocity computation from the second part, to get a complete simple acceleration-and-steering simulation.
Since you asked about acceleration as a vector, here is an alternate solution which would compute things that way.
First, given the velocity (a Vector2D value), let's suppose you can compute a direction from it. I don't know your syntax, so here's a sketch of what that might be:
double forwardDirection = Math.toDegrees(velocity.direction()) + 90;
This is the direction the car is pointing. (Cars are always pointing in the direction of their velocity.)
Then, we get the components of the acceleration. First, the front-and-back part of the acceleration, which is pretty simple:
double forwardAcceleration = 0;
if (up)
forwardAcceleration = 100;
if (down)
forwardAcceleration = -100;
The acceleration due to steering is a little more complicated. If you're going around in a circle, the magnitude of the acceleration towards the center of that circle is equal to the speed squared divided by the circle's radius. And, if you're steering left, the acceleration is to the left; if you're steering right, it's to the right. So:
double speed = velocity.magnitude();
double leftAcceleration = 0;
if (right)
leftAcceleration = ((speed * speed) / turningRadius);
if (left)
leftAcceleration = -((speed * speed) / turningRadius);
Now, you have a forwardAcceleration value that contains the acceleration in the forward direction (negative for backward), and a leftAcceleration value that contains the acceleration in the leftward direction (negative for rightward). Let's convert that into an acceleration vector.
First, some additional direction variables, which we use to make unit vectors (primarily to make the code easy to explain):
double leftDirection = forwardDirection + 90;
double fDir = Math.toRadians(forwardDirection - 90);
double ldir = Math.toRadians(leftDirection - 90);
Vector2D forwardUnitVector = new Vector2D(Math.cos(fDir), Math.sin(fDir));
Vector2D leftUnitVector = new Vector2D(Math.cos(lDir), Math.sin(lDir));
Then, you can create the acceleration vector by assembling the forward and leftward pieces, like so:
Vector2D acceleration = forwardUnitVector.scale(forwardAcceleration);
acceleration = acceleration.add(leftUnitVector.scale(leftAcceleration));
Okay, so that's your acceleration. You convert that to a change in velocity like so (note that the correct term for this is deltaV, not deltaA):
Vector2D deltaV = acceleration.scale(secondsElapsed);
velocity = velocity.add(deltaV).
Finally, you probably want to know what direction the car is headed (for purposes of drawing it on screen), so you compute that from the new velocity:
double forwardDirection = Math.toDegrees(velocity.direction()) + 90;
And there you have it -- the physics computation done with acceleration as a vector, rather than using a one-dimensional speed that rotates with the car.
(This version is closer to what you were initially trying to do, so let me analyze a bit of where you went wrong. The part of the acceleration that comes from up/down is always in a direction that is pointed the way the car is pointed; it does not turn with the steering until the car turns. Meanwhile, the part of the acceleration that comes from steering is always purely to the left or right, and its magnitude has nothing to do with the front/back acceleration -- and, in particular, its magnitude can be nonzero even when the front/back acceleration is zero. To get the total acceleration vector, you need to compute these two parts separately and add them together as vectors.)
Neither of these computations are completely precise. In this one, you compute the "forward" and "left" directions from where the car started, but the car is rotating and so those directions change over the timestep. Thus, the deltaV = acceleration * time equation is only an estimate and will produce a slightly wrong answer. The other solution has similar inaccuracies -- but one of the reasons that the other solution is better is that, in this one, the small errors mean that the speed will increase if you steer the car left and right, even if you don't touch the "up" key -- whereas, in the other one, that sort of cross-error doesn't happen because we keep the speed and steering separate.

Categories

Resources