I posted this question on the stack math site, but they are not too excited to see questions including programming. Anyway, now I am here :)
I have an object, which is located at MyTransform.position (Vector)
That object should follow the DesiredPosition (Vector), which is
moving in varius directions with a changing velocity
MaxDelayDistance (float) is the maximum distance my object is allowed
to have to the DesiredPosition.
DelayRecovery (float) are the seconds my object has in order to move
to the DesiredPosition if the MaxDelayDistance is reached.
Time.deltaTime (float) is a term we use to describe the time the last
frame update took. It's value is usually about 0.025f.
private Vector3 GetLerpedPosition(Vector3 DesiredPosition) {
//DesiredPosition now contains the goal
Vector3 dirToDesiredPosition = (DesiredPosition - MyTransform.position).normalized; //Direction to desired position
Vector3 lerpStart = DesiredPosition + (-dirToDesiredPosition * MaxDelayDistance); //Start the lerp at my farthest allowed position
float lerpCurrentT = Vector3.Distance(MyTransform.position, DesiredPosition) / Vector3.Distance(lerpStart, DesiredPosition); //My current fragtion (t) of the lerp
//Now I lerp from lerpStart to DesiredPosition using a calculated fraction
Vector3 result = Vector3.Lerp(
lerpStart,
DesiredPosition,
lerpCurrentT + (DelayRecovery / MaxDelayDistance * Vector3.Distance(MyTransform.position, DesiredPosition) * Time.deltaTime)
);
return result;
}
The main problem is that my object is not following the DesiredPosition smoothly.
It jumps from MaxDelayDistance to DesiredPosition and back.
Somehow the fraction (t) in my Lerp function always results in about 1.005 or about 0.001. Can you spot any problems in my approach?
I see several issue, and I'm not sure of the direction you have choose (at least never seen anything similar before).
Preamble:
Vector3.Distance(lerpStart, DesiredPosition)
this is a constant and is the radius around DesiredPosition.
Here's some problems I noticed:
Problem 1
lerpT is always >=1 for every point more distant more than MaxDelayDistance (outside the radius). So when an object is further distant than MaxDelayDistance it will be immediately moved to DesiredPosition.
Problem 2
DelayRecovery (float) are the seconds my object has in order to move
to the DesiredPosition if the MaxDelayDistance is reached.
Not sure of having completely understood your intricate lerp, btw the statement above seems to be false. DelayRecover is always taken in consideration while lerping despite its distance.
Problem 3
Possible division by zero when you are closed to DesiredPosition (* operator is evaluated before /)
Some considerations
I'll read with more attention your code, and I'll try to figure out the logic behind that. It's something I've never seen.
Some general approach for moving toward a given position:
If start and destination are known and fixed, than lerping (or an ease function) allow to control exactly the travel time incrementing t parameter from 0 to 1.
If destination is moving you can still smooth follow the target using lerp (maybe not the more correct use from a theoretical point of view, nor physical realistic but in any case effective). Lerp factor in this case is a kind of "speed toward the goal". Effectively linear proportional to the distance from the target.
Some physic based approach. Integrate. Define some kind of max speed and max acceleration and calculate the next position (eventually clamp to prevent overshooting and oscillation)
PID controllers. Lot of power, but I always found them hard to tweak.
Consider also to use Vector3.MoveToward, here's how it's implemented:
public static Vector3 MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta)
{
Vector3 a = target - current;
float magnitude = a.magnitude;
if (magnitude <= maxDistanceDelta || magnitude == 0f)
{
return target;
}
return current + a / magnitude * maxDistanceDelta;
}
Hope this helps.
Related
In a game that I'm making, the idea is that the mouse cursor is representing a center of mass and there are objects orbiting around it.
The cursor itself is not an object and there is no collision between the cursor and the objects.
The ideal method would be the one where I can enter x and y coordinates of the target, and maybe the orbit radius, and the object would go into orbit and would be accelerating constantly.
When I move the mouse, the object would follow; and when I stop it, it would fall back into the same orbit radius.
The objects acceleration should also be able to change so it gives an effect that the mouse has more or less mass.
I thought the best way to achieve this would probably be with vectors.
In the Projectile class I gave each projectile 4 fields: x coordinate, y coordinate, PVector speed and PVector acc (acceleration).
The first thing I tried is giving the object constant acceleration. Speed vector starts as zero, the acceleration vector is calculated, its magnitude is then set to some value and it is added to the speed vector.
This produced very large orbits, slow acceleration and when the mouse is moved, the orbit just became larger.
Next I tried making the acceleration increase when it is closer to the center of mass inspired by the actual planets. This did not work either. The orbits were still too large and not controllable. Kind o disappointed I tried making the acceleration constantly increasing which gave the best results. The orbit is now getting smaller, but it just keeps decreasing to some radius and then stops. This would be perfect if the radius at which it stops decreasing would be configurable, but no matter the parameters the orbit is always too large and the orbit size keeps decreasing frustratingly slowly. My guess is that the acceleration and speed somehow end up in an equilibrium, and then the orbit stops decreasing.
void move(float x,float y,float accm)
{
PVector target = new PVector(x, y);
PVector ball = new PVector(this.x, this.y);
acc = PVector.sub(target, ball);
acc.setMag(accm);
speed.add(acc);
println(acc.mag() + " " + speed.mag());
this.x = this.x + speed.x;
this.y = this.y + speed.y;
}
This is the function that gave the best results. The function is called in draw():
if(mousePressed == true)
{
for(i=0;i<nproj;i++)
{
a[i].move(mouseX, mouseY,k);
}
k += n;
}
k is the number given to the function and n is the rate at which acceleration increases. I tried many different acceleration magnitudes and different rates of acceleration and just couldn't figure it out. Nproj is the number of projectiles and a is the name of the projectile array.
I found that for k = 0 and n = 0.002 it gave the most reliable and stable results. If n is larger (around 0.01 or larger), the objects sometimes randomly flies off way further than expected, and it actually increases the orbit radius. As the acceleration increases this happens more often, and the object sometimes ends up off the screen and then it never comes back. I know that orbit radius doesn't exist because the orbits are elliptical, but it just makes it easier to explain.
Update:
This is what I came up with so far
void move(float x,float y,float accm)
{
PVector target = new PVector(x, y);
PVector ball = new PVector(this.x, this.y);
acc = PVector.sub(target, ball);
acc.setMag(accm);
vel.add(acc);
this.x = this.x + vel.x;
this.y = this.y + vel.y;
vel.limit(15);
}
and in draw()
if(mousePressed == true)
{
for(i=0;i<nproj;i++)
{
a[i].move(mouseX, mouseY,k);
}
k += n;
if(k > 25)
n = 0;
}
By limiting the velocity vector and constantly increasing acceleration vector, magnitude difference begins to increase and since the acceleration vector is increasing and the velocity vector is constant and acceleration vector points towards the target the sum of the vectors starts slowly pointing more and more towards the target. The condition if(k < 25) is there to limit the acceleration and by changing the limit of the acceleration you can change the orbit radius.
I think you probably would have gotten a quicker answer over at https://physics.stackexchange.com/ and framing your question as "how do I simulate gravity in a game?"
Anyway I will give it a try...
In the class Projectile I gave each projectile 4 fields: x coordinate, y coordinate PVector speed and PVector acc
Sounds good so far, except you don't need to store the acceleration vector with the object. You'll calculate a new acceleration vector for each object on each "tick" of your simulation.
I would use standard physics terminology here and call each object's vector velocity instead of speed.
Next I tried making the acceleration increase when it is closer to the center of mass inspired by the actual planets.
This behavior should just happen naturally if you use the right algorithm for calculating an orbit.
Your "sun" and the "planets" each have a mass. Masses attract each other with a gravitational force, which is F = G * m1 * m2 / d^2 from Wikipedia.
Remember that Force = mass * acceleration, and you have the acceleration as a = m * G / d^2 where m is the mass of the other object (in this case the sun), and d is the distance between the two objects. So the important part is that the acceleration of an object due to gravity varies with the distance squared between the two objects.
So your basic algorithm for a single tick of your simulation looks like this:
For each planet, calculate an acceleration vector
Use the distance to the "sun" squared as the magnitude of the acceleration vector (times a constant value that depends on your "gravitational constant" and your sun's mass), and the direction is of course the direction from the planet towards the sun
Add this acceleration vector to the velocity vector for the planet
Add the velocity vector to the (x, y) position of the planet to move it forward.
With appropriate values for the length of each simulation tick, the gravitational constant, and the sun's mass, this should start to look pretty reasonable.
Since you're assuming that the planets are all independent and cannot collide, you can just do the calculation independently for each planet.
However, I think the game might be more interesting with a more accurate simulation. In reality, every object has a gravitational force on every other object. This would mean looping over all pairs of objects and doing the same kind of acceleration/velocity/position calculation.
The point of the game could then be to get the "planets" from one side of the screen to the other, in increasingly difficult starting configurations as they are all affecting each other and moving in different directions.
In order to make all of this work well, it would be important to set the mass of the "sun" many times higher than the mass of the planets, and to choose a reasonable value for your gravitational constant based on the timing of your simulation. You'll have to play with these constants to find the best results.
So I am making a program, where you can have two objects (circles). I want them to orbit like planets around each other, but only in 2D.
I know that using Newtons Universal Law of Gravitation I can get the force between the two objects. I also know A = F / M. My question is how would I take the A from the previous equation and change it into a vector?
You need to use vector equations:
// init values (per object)
double ax=0.0,ay=0.0,az=0.0; // acceleration [m/s^2]
double vx=0.0,vy=0.0,vz=0.0; // velocity [m/s]
double x=0.0, y=0.0, z=0.0; // position [m]
double m=1.0; // mass [kg]
// iteration inside some timer (dt [seconds] period) ...
int i; double a,dx,dy,dz; // first compute acceleration
for (ax=0.0,ay=0.0,az=0.0,i=0;i<obj.num;i++)
if (obj[i]!=this) // ignore gravity from itself
{
dx=obj[i].x-x;
dy=obj[i].y-y;
dz=obj[i].z-z;
a=sqrt((dx*dx)+(dy*dy)+(dz*dz)); // a=distance to obj[i]
a=6.67384e-11*(obj[i].m*m)/(a*a*a); // a=acceleration/distance to make dx,dy,dz unit vector
ax+=a*dx; // ax,ay,az = actual acceleration vector (integration)
ay+=a*dy;
az+=a*dz;
}
vx+=ax*dt; // update speed via integration of acceleration
vy+=ay*dt;
vz+=az*dt;
x+=vx*dt; // update position via integration of velocity
y+=vy*dt;
z+=vz*dt;
Code is taken from here
obj[] is list of all your objects
obj.num is the count of them
I recommend to create object class with all the variables inside (ax,ay,az,...m), init them once and then continuously update (iteration) in some timer. If you want more accuracy then you should compute ax,ay,az for all objects first and only after update speed and position (to avoid change of position of objects during gravity computation). If you want to drive an object (like with truster) then just add its acceleration to ax,ay,az vector)
Now to setup an orbit just:
place planet object
must be massive enough and also set its position / velocity to what you want
place satellite
Initial position should be somewhere near planet. It should not be too massive. Init also speed vector with tangent direction to orbiting trajectory. If speed is too low it will collapse into planet and if speed is too high it will escape from planet otherwise will be orbiting (circle or ellipse)
timer
lower the interval better the simulation usually 10ms is OK but for massive and far objects is also 100ms and more OK. If you want particles or something then use 1ms (very dynamic sceene).
I strongly recommend to read this related QA:
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
especially [edit3] about the integration precision and creating orbital data.
With two objects you are probably best using an ellipse which is the path the objects will follow about their common center of mass. Read Kepler's laws of planetary motion which gives the background.
If one object is a much greater mass than the other, i.e. a sun and a planet you can have one stationary and the other taking an elliptical path. The equation of the ellipse is given by
r = K e / ( 1 + e cos(theta))
K is a constant giving the size and e is the eccentricity. If you want an elliptical orbit have 0 < e < 1 the smaller it is the more circular the orbit. To get x, y coordinates from this use, x = r cos(theta), y = r sin(theta). The missing bit is time and how the angle is dependant on time. This is where the second and third laws come in. If a and b are the semi-major and semi-minor lengths of the ellipse, and P is the period then
0.5 * P * r^2 theta'= pi a b
theta' is the rate of change of angle with respect to time (d theta/d t). You can use this to get how much theta will change given a increase in time. First work out the current radius r0 given the current angle th0 if the time increment is δt then the angle increment δtheta is
δtheta = 2 pi * a * b / r^2 * δt
and the next angle is th0 + δtheta.
If the masses are of similar magnitude then see two body problem. Both objects will have elliptical orbits, there are two patterns which you can see in animations on that page. The ellipses will follow the same formula as above with the focus at the common center of mass.
If you have three object things get considerably harder and there are not generally neat solutions. See three body problem for this.
I am working on a yet another 3d game engine and have following Problem. In order to improve Performance I want to check if two Objects are coming closer or moving away from each other. Only if they are coming closer to each other collision detection should happen.
At the moment the code calculates the current distance using the positions of both Objects. Then moves both positions about the velocity vector and calculates the expected distance. etc.
public class Model{
public boolean isApproaching(Model model) {
float [] otherPosition = model.getPosition();
float [] otherVelocity = model.getVelocity();
double currentDistance = Math.pow(this.position[0] - otherPosition[0], 2)
+ Math.pow(this.position[1] - otherPosition[1], 2)
+ Math.pow(this.position[2] - otherPosition[2], 2);
double expectedDistance = Math.pow(this.position[0] + this.velocityVector[0]/1000 - otherPosition[0] - otherVelocity[0]/1000, 2)
+ Math.pow(this.position[1] + this.velocityVector[1]/1000 - otherPosition[1] - otherVelocity[1]/1000, 2)
+ Math.pow(this.position[2] + this.velocityVector[2]/1000 - otherPosition[2] - otherVelocity[2]/1000, 2);
if(currentDistance < expectedDistance)
return false;
return true;
}
}
As you can see in the Picture it does not work for fast moving objects. Is there any good way to show if two points are moving towards each other?
Given the two straight line trajectories, there's a unique time when they're going to be closest, which may have been in the past. If it's in the past then it means that the objects are moving away from each other, but if its in the future it means that they're moving closer together. But even it they're moving closer together, I would guess that knowing how soon is the information that you need, so that you can schedule when to worry about it. This methodology should cater for both slow moving and fast moving objects.
It appears that you are using the /1000 versus any other value for some unstated reason that may negate the following.
By changing this scale factor you get better detection with a fast object. Perform your same isApproaching() calculation except with a /(1000*N) instead of /1000. You will then has N times better sensitivity to know if they are approaching or not.
I was wondering if there was a target leading formula for target leading in 3d with a ballistic trajectory and if not i was wondering if it is possible to convert a 2d target leading formula to work as a 3d target leading formula by aligning the plane it is on along the motion of the target?
Leading the target can be easily calculated at the time the projectile is fired, however there is no guarantee the target won't change course, accelerate, or decelerate while the projectile is in the air. In any case, when you're leading the target you can either assume that the target's velocity will remain the same, or if it is accelerating, that its acceleration will remain the same.
Here's a blog post I wrote about predicting a traveled distance over time, accounting for acceleration. And here is the code I use.
This will calculate a distance traveled, over time, given a constant acceleration. In this case constantAccel is a speed, and if your target isn't accelerating then you would just use 0.0f for that parameter.
float CalcDistanceOverTime(const float initVelocity,
const float constantAccel,
const float timeDelta)
{
return (initVelocity * timeDelta)
+ (0.5f * constantAccel * (timeDelta * timeDelta);
}
Here's an example:
Vector3 targetsVelocity(3.0f, 0.0f, 3.0f);
float targetsAcceleration = 1.0f;
float distanceTraveled = CalcDistanceOverTime(targetsVelocity, targetsAcceleration, timeDelta)
Vector3 finalPosition = targetsVelocity * distanceTraveled;
You may notice that you'll need a timeDelta to pass to this formula. This means, based on your projectile's trajectory and speed, you'll need to know about how long it will take to reach the target, however it is made more difficult by the fact that you don't know exactly how long that may take until you know where it will be. I'm not sure of the exact formula for this, but I believe using Calculus you could calculate, based on the speed and velocity of your projectile, and the speed and velocity of your target, accounting for gravity with both, that you should be able to find a point in space where these two can collide.
If the above method isn't feasible then you may be able to choose a fixed timeDelta, if you can guarantee your projectile can go at whatever angle and speed that you would like. For example, pick 3 seconds as your timeDelta, you know where the target will be in 3 seconds, and immediately fire a projectile that you know will reach that point in space within 3 seconds.
And just in case, here's a blog post about calculating ballistic trajectory in 3D. Calculating the time to target with this method should be simple, based on outgoing vertical velocity and position, just use gravity to calculate how many seconds until that position reaches the elevation at the target position.
So I have a ship, that has thrusters at the bottom and that can only use these to move forward. It can also rotate itself around its center. Its thrusters gives it acceleration, so it doesn't move at a constant velocity. What I want to do is to tell it "move to point B".
I have come up with a solution but it doesn't work very well and it doesn't rotate smoothly, it moves jerkily and it doesn't end up exactly where it should be, so I have to have a big margin of error.
Is this a normal problem, and if so is there a "standard" way of doing it? Is this an easy problem? I want to make it look like the ship is steering itself to that point, using the constraints (thrusters, rotation) the player has. This excludes just lerping it from point A to B. Or does it?
I'd love some help in solving this problem. Positions are stored in vectors, and it's a 2D problem. Just for reference I'm including my solution, which basically is accelerating the ship until and rotating it to point to the point. I think my implementation of this idea is the problem:
Vector diff = vector_sub(to_point, pos);
float angle = vector_getangle(diff);
float current_angle = vector_getangle(dir);
float angle_diff = rightrange(angle) - rightrange(current_angle);
float len = vector_getlength(diff);
// "Margin of error"
float margin = 15.0;
// Adjust direction, only if we're not stopping the next thing we do (len <= margin)
if ( len > margin && fabs(angle_diff) > 2.0 )
{
dir = vector_setangle(dir, current_angle + (angle_diff)*delta*(MY_PI) - MY_PI/2);
}
else if ( len > margin )
{
dir = vector_normalize(diff);
}
// accelerate ship (if needed)
acc.x = acc.y = speed;
acc = vector_setangle(acc, vector_getangle(dir));
if ( len <= margin )
{
// Player is within margin of error
}
If you are not looking for a very general solution that works online, then there is a simple solution. What I mean by online is continuously re-calculating the actions along the complete trajectory.
Assuming the ship is at rest at start, simply rotate it towards your target point (while still at rest). Now, your ship can reach the target by accelerating for t seconds, rotating back while in motion (for 0.5 seconds as per your constraint), and decelerating for another t seconds. If the distance between current point and destination is d, then the equation you need to solve is:
d = 0.5*a*t^2 + 0.5*a*t + 0.5*a*t^2
The first term is distance traveled while accelerating. The second term is distance traveled while rotating (v*t_rot, v=a*t, t_rot=0.5). The final term is the distance traveled while decelerating. Solve the above for t, and you have your trajectory.
If the ship is moving at start, I would first stop it (just rotate in opposite direction of its speed vector, and decelerate until at rest). Now we know how to reach destination.
The problem with offline trajectory calculation is that it is not very accurate. There is a good chance that you will end up in the vicinity of the target, but not exactly on top of it.
Let's make the problem a little more interesting: the ship cannot rotate without acceleration. Let's call this acceleration vector a_r, a vector that is at a certain angle against the ship's direction (somewhat like having a thruster at an angle at the back). Your task now is to rotate the ship and accelerate in such a direction that the speed component perpendicular to the vector connecting the current position to the target is canceled out. Instead of trying to calculate the vectors offline, I would go with an online approach with this.
The easiest thing to do would be to add the following algorithm calculated at every time interval:
Calculate the vector pointing from ship to destination.
Split your current speed vector into two components: towards the destination, and perpendicular to it.
If perpendicular speed is zero, skip 4
Start rotating towards the negative of the perpendicular vector's direction. If already looking away from it (not exact opposite, but just looking away), also fire main thruster.
This will oscillate a bit, I suspect it will also stabilize after a while. I must admit, I don't know how I would make it stop at destination.
And the final approach is to model the ship's dynamics, and try to linearize it. It will be a non-linear system, so the second step will be necessary. Then convert the model to a discrete time system. And finally apply a control rule to make it reach target point. For this, you can change your state-space from position and speed to error in position and (maybe) error in speed, and finally add a regulation control (a control loop that takes the current state, and generates an input such that the state variables will approach zero).
This last one is fairly difficult in the maths compartment, and you'd probably need to study control engineering a bit to do it. However, you'll get much better results than the above simplistic algorithm - which admittedly might not even work. In addition, you can now apply various optimization rules to it: minimize time to reach target, minimize fuel consumption, minimize distance traveled, etc.