I'm having a trouble with making my Tank accelerate when I press a move button and decelerate when I let go of it. Don't be too harsh on me for not being a pro because I'm still learnig and Thanks in advance!
I also have variables called 'speed' and 'maxspeed' which I played around with and it didn't turn out to well, and the code below basicaly makes my tank move in an update function
if(TestName->Tank[0].up == true){
TestName->Tank[0].position_y = TestName->Tank[0].position_y + (TestName->Tank[0].speed + 0.06f);
}
if(TestName->Tank[0].down == true){
TestName->Tank[0].position_y = TestName->Tank[0].position_y - 0.06f;
}
if(TestName->Tank[0].right == true){
TestName->Tank[0].rotation = TestName->Tank[0].rotation + 0.6f;
}
if(TestName->Tank[0].left == true){
TestName->Tank[0].rotation = TestName->Tank[0].rotation - 0.6f;
}
}
In this bit of code:
TestName->Tank[0].position_y = TestName->Tank[0].position_y + (TestName->Tank[0].speed + 0.06f);
it looks horribly like you are trying to add an acceleration (0.06) to your speed while at the same time adding the speed to the position. The plus sign won't change the speed, it will just calculate the result and use it in the equation.
Do it in two steps:
TestName->Tank[0].speed += 0.06f;
TestName->Tank[0].position_y += TankName->Tank[0].speed;
When you have that bit working your next issue will be inability to steer. I think you would be well advised to google vectors (maths, not C++) and the difference between velocity and speed.
Logically,.....Supposing your defaultspeed is 1 and maxspeed is 2. You probably want another variable, lets call it deltaSpeed.
Then your totalSpeed(Change in Tank's Position in a direction) is defaultspeed * deltaSpeed.
You would also need to check if the user is pressing down on the button, if so increase(either by a fixed amount or exponentially) the deltaSpeed. When the user release the button then decrease the deltaSpeed. This would give the illusion of tank speeding up and slowing down.
Then, when the user presses another direction you could reset the deltaSpeed.
some useful Newtonian equations:
f = ma
v^2 = u^2 + 2as
s = ut + 0.5 * a * t^2
where:
s = distance travelled (m)
u = initial velocity (m/s)
t = time (seconds)
a = acceleration
f = force
m = mass
Rearranging these equations allows you to compute the change in acceleration, and therefore velocity, and therefore distance for a given time period.
Games will model these rules with varying degrees of simplification (e.g. you could code that pressing 'W' will generate a Force of X Newtons in the direction the tank is facing, or you could choose to limit the maximum speed, for make force inversely proportional to current speed (to model for the power equation).
Or you could calculate F as the integral of the power equation over the time period between each simulation pass (which is what a flight sim might do).
Hopefully some ideas to guide you to some useful literature.
Related
I have a function that moves a planet around a star. This function takes a parameter t, which is the time in milliseconds since the last update. In other movement functions I've written, I like to use time to dictate movement so the movement will always be the same on all computers and instances instead of based on processing power. However, all methods I have tried for including time in this physics equation have resulted in erratic results. Any ideas?
void Planet::update(int t){
double grav_const = 6.6742e-11;
double earth_mass = 5.975e24;
double starX = 1920/2 * 10000;
double starY = 1080/2 * 10000;
double diffX = xPos - starX;
double diffY = yPos - starY;
double radius = sqrt(pow(diffX,2) + pow(diffY,2));
double grav_accel = (grav_const * (earth_mass / pow(radius,2)));
double angle = atan2(diffX, diffY);
xVel += (sin(angle) * grav_accel);
yVel += (cos(angle) * grav_accel);
xPos -= xVel;
yPos -= yVel;
}
It's been a while since I dealt with physics at this level, but I think you can go back to fundamental reasoning about the units involved.
Acceleration is distance over time squared (m/s^2 or whatever your units are). So to get velocity (distance over time) then you need to multiply by time.
m/s = (m/s^2) * s
And then after that you want to turn your velocity into a specific change in distance. So multiply it by the time again and there you go.
m = (m/s) * s
If things still don't seem right afterwards, then you may need to check over the rest of your equations and constants. Make sure the units match up (seconds vs minutes, metere vs kilometers, etc). Make sure you aren't suffering rounding in places you didn't intend. And so on.
In the worst case, work the math yourself for a few iterations (perhaps with larger time values) and maybe even plot the results on a piece of paper to make sure it looks sensible.
When you describes the results as "erratic" exactly what do you mean?
If you mean:
A. "t changes by a varying amount between each call". Then you need to look at the architecture of the calling application since that will vary with processing power and other work going on in the system (assuming a preemptive multitasking OS).
B. "the floating point values have strange rounding characteristics". Then welcome to using floating point numbers. The representations of double, float and the like are simply imperfect and exhibit rounding areas in certain circumstances and you may have problems if you are taking deltas that are too small relative to the size of the other values you are combining.
C. "t has no effect on my results". I don't see any references to the input parameter in your example.
You should post the entire Planet class, or at least more of it.
EDIT: The best way to calculate position based on times like this is to come up with an absolute function that returns position based on time and NOT accumulate position, but only accumulate time. For example:
timeAbsolute += tDelta;
xPos = fxPos(timeAbsolute);
yPos = fyPos(timeAbsolute);
xVel = fxVel(timeAbsolute);
yVel = fyVel(timeAbsolute);
My orbital mechanics fu is not strong enough to give you those functions in general, but in your case (where you seem to be assuming a circular orbit), you can simply take the arc angle instead. So, assuming 1 orbit every 360 seconds (and using degrees), you would get
angle = (timeAbsolute % 360);
then calc velocity and position from angle.
P.S. Be careful with fmod ...
How to use fmod and avoid precision issues
I have a ground set up of various points, some of which are flat and others are at an angle, I'm trying to check if there is a collision between the angled points (non-axis aligned).
I have a vector array consisting of two floats at each point - This is each of the points of the ground.
Here's an image representation of what the ground looks like.
http://i.imgur.com/cgEMqUv.png?1?4597
At the moment I want to check collisions between points 1 and 2 and then go onto the others.
I shall use points 1 and 2 as an example.
g1x = 150; g2x = 980;
g2x = 500; g2y = 780;
The dxdy of this is dx = 350 and dy = -200
The normal x of this is dy and the normal y is -dx
nx = -200;
ny = -350;
normalized it is the length between points 1 and 2 which is 403.11
nx/normalized = -0.496
ny/normalized = -0.868
//get position of object - Don't know if its supposed to be velocity or not
float vix = object->getPosition().x;
float viy = object->getPosition().y;
//calculate dot product - unsure if vix/viy are supposed to be minused
float dot = ((-vix * nrmx) + (-viy * nrmy)) * nrmx; //= -131.692
Is this information correct to calculate the normal and dot product between the two points.
How can I check if there is a collision with this line and then reflect according to the normal.
Thanks :) any and all changes are welcome.
Say you have a particle at position x travelling at velocity v and a boundary defined by the line between a and b.
We can find how far along the boundary (as a fraction) the particle collides by projecting c-a onto b-a and dividing by the length ||b-a||. That is,
u = ((c-a).((b-a)/||b-a||))/||b-a|| == (c-a).(b-a) / ||b-a||2.
If u > 1 then the particle travels past the boundary on the b side, if u < 0 then the particle travels past the boundary on the a side. The point of collision would be
c = a + u b.
The time to collision could be found by solving
x + t v = a + s (b-a)
for t. The reflection matrix can be found here. But it will need to be rotated by 90 deg (or pi/2) so that you're reflecting orthogonal to the line, not across it.
In terms of multiple boundaries, calculate the time to collision for each of them, sort by that time (discarding negative times) and check for collisions through the list. Once you've found the one that you will collide with then you can move your particle to the point of collision, reflect it's velocity, change the delta t and redo the whole thing again (ignoring the one you just collided with) as you may collide with more than one boundary in a corner case (get it? It's a maths pun).
Linear algebra can be fun, and you can do so much more with it, getting to grips with linear algebra allows you to do some powerful things. Good luck!
I'm currently working on a 3D Rigid Body simulation program. I have currently managed to get the rigid bodies colliding with the floor and bouncing correctly using the impulse. However, my problem is once they have bounced they accelerate constantly despite using a friction vector to try and slow them.
This is the code when you hit the ground
Rvector fDirection(m_Bodies[i].Vel.x,0.0,m_Bodies[i].Vel.z);
Rvector relativeVelocities = m_Bodies[i].Vel - floorVelocity;
fDirection.normalize();
Real impulse = -(1+e) * (Rvector::dotProduct(relativeVelocities,floorNormal))
/ (1/m_Bodies[i].mass + floorMass);
Rvector friction = fDirection*mu*gravity.length()*m_Bodies[i].mass;
Rvector collision_forces = Rvector(0,1,0)*impulse;
collision_forces += friction ;
m_Bodies[i].Vel += (collision_forces/m_Bodies[i].mass);
Thanks
Edit:
Here is the integration code.
void RigidBodySimulation::eulerIntegration(float dTime)
{
Rvector newVel;
Rvector newPos;
Rvector zero(0.0, 0.0, 0.0);
Real one_over_mass;
Rvector accel;
for( unsigned int i = 0 ; i < m_Bodies.size(); i++)
{
one_over_mass = 1/m_Bodies[i].mass;
newVel = m_Bodies[i].Vel + m_Bodies[i].force*one_over_mass*dTime;
newPos = m_Bodies[i].Pos + m_Bodies[i].Vel*dTime;
accel = m_Bodies[i].force / m_Bodies[i].mass;
m_Bodies[i].acceleration = accel;
m_Bodies[i].newPos = newPos;
m_Bodies[i].Vel = newVel;
m_Bodies[i].Pos = newPos;
}
}
I have to say, this is a pretty terrible piece of code you have there, and I've been doing this for more than 10 years. You should get a basic textbook on dynamics (like Hibbeler).
Real impulse = -(1+e) * (Rvector::dotProduct(relativeVelocities,floorNormal))
/ (1/m_Bodies[i].mass + floorMass);
This equation sort of looks like you are trying to calculate the restitution impulse from the impact (although the calculation is wrong). First, you must understand that an impulse is not the same thing as a force. An impulse is the integral of the force over a certain interval of time. During an impact, you can assume that period of time to be really small and that's why you perform an instantaneous velocity change. And that's why you should have specified that the integration code has nothing to do with the collision calculation, because it is by-passed for that instant, or at least, it should be if you do an impulse-based calculation. This is what the actual calculation should look like:
Real momentum_before = Rvector::dotProduct(m_Bodies[i].Vel * m_Bodies[i].mass + floorVelocity * floorMass, floorNormal);
Real rel_vel_after = -e * Rvector::dotProduct(relativeVelocities,floorNormal);
// conservation of momentum in normal direction gives this:
Real body_vel_after = (momentum_before + floorMass * rel_vel_after) / (m_Bodies[i].mass + floorMass);
Real floor_vel_after = body_vel_after - rel_vel_after;
Which actually simplifies to one line as follows:
Real body_vel_after = ( (m_Bodies[i].mass - e * floorMass) * Rvector::dotProduct(m_Bodies[i].Vel, floorNormal)
+ (1.0 + e) * floorMass * Rvector::dotProduct(floorVelocity, floorNormal)
) / (m_Bodies[i].mass + floorMass);
However, if you assume the floor to have infinite mass (or much larger than that of the body), then you would simply have:
Real body_rel_vel_after = -e * Rvector::dotProduct(relativeVelocities, floorNormal);
Real body_vel_after = Rvector::dotProduct(floorVelocity, floorNormal) + body_rel_vel_after;
It's that simple. But, under that assumption, you do not have conservation of momentum. But in any case, the restitution impulse from the impact can be calculated as:
Real impulse = m_Bodies[i].mass * (body_vel_after - Rvector::dotProduct(m_Bodies[i].Vel, floorNormal));
Now, because the restitution impulse is the integral of the normal force over the small time period of impact, the impulse from the friction during the impact can be calculated from that restitution impact. The friction force is equal to "mu" times the normal force, i.e., |Ff| = mu * |Fn|, this is also valid for the impulse, i.e., |If| = mu * |In|. So, you can compute it directly:
Real friction_impulse = mu * fabs(impulse);
But that's just the magnitude of the friction impulse. It's direction is opposite from the relative tangential velocity, which is:
Rvector tangent_rel_vel = relativeVelocities - Rvector::dotProduct(relativeVelocities, floorNormal) * floorNormal;
And it's direction is:
Rvector dir_rel_vel = tangent_rel_vel;
dir_rel_vel.normalize();
(Notice that I need to keep the tangential velocity intact, because it will be needed later)
At this point, you could compute the tangential velocity after impact as follows (again, under the assumption of an infinite-mass floor, otherwise, it is more complicated than that):
Rvector tangent_rel_vel_after = tangent_rel_vel - dir_rel_vel * friction_impulse / m_Bodies[i].mass;
However, what if the friction impulse causes the tangential relative velocity to get to zero? That's a problem, because, with the above formula, part of the friction impulse could wind up reversing the direction of the tangential relative velocity, which would mean that during the latter part of the impact, the friction force is actually acting in the direction of the velocity (not good). The most friction can do is stop the relative motion. So, you need to check for that condition:
Real tang_rel_vel_change = friction_impulse / mBodies[i].mass;
Rvector tangent_rel_vel_after = tangent_rel_vel - dir_rel_vel * tang_rel_vel_change;
if ( tang_rel_vel_change > tangent_rel_vel.length() )
tangent_rel_vel_after = Rvector(0.0, 0.0, 0.0); // stop relative motion.
At this point, all you need to do is combine the two final velocities:
m_Bodies[i].Vel = floorVelocity + tangent_rel_vel_after + body_rel_vel_after * floorNormal;
And that's it, at least, for this very simple problem (infinite mass of the floor). In reality, this impulse-based approach become increasingly difficult to deal with as you complicate things: two finite-mass objects, multiple objects, and actual rigid-body dynamics (because you are just doing particle dynamics here). The impulse-based approach is rarely seen anywhere beyond simple schoolyard examples of balls bouncing on the floor. Btw, you shouldn't really call this a "rigid-body" simulator since all you are really doing a particle dynamics (and 3D rigid-body dynamics is way more complicated than this). Also, your integration law is terrible, but that's a whole different story.
Friction is backwards, isn't it?
Rvector fDirection(m_Bodies[i].Vel.x,0.0,m_Bodies[i].Vel.z);
This is in the direction of the velocity. You then multiply it by some constants then add it to the velocity (along with the impulse)
collision_forces += friction ;
m_Bodies[i].Vel += (collision_forces/m_Bodies[i].mass);
So that will act to increase the velocity along the floor.
There are some other odd things:
your impulse term has: (1/m_Bodies[i].mass + floorMass) This is adding 1/mass to mass. Shouldn't it be (1/(m_Bodies[i].mass + floorMass))
And then in the integrator, you calculate acceleration from force, but then never integrate it, you also apply force to velocity directly. So what's the acceleration member for?
I need to create a circle of regularly spaced doors around a reference point in space. Each time the program is run, a different number of items will be in the circle, and thus it automatically rescales to accommodate the increased number of items. I know this is more of a mathematic question rather than a programming question, but I am stumped! The following code is as close as I got:
block->setPosition(core::vector3df(sin(segdeg*itemnumber)*radius+referencepoint.X,
0,
cos(segdeg*itemnumber)*radius+referencepoint.Z));
block is the object, and this code is run for each item.
segdeg is 360/the number of items. that is to say, the SEGment DEGrees.
Radius is how far away from the central point the items need to be.
Itemnumber is the index of the item in question - Which number item it is.
For some reason, this code makes each door pretty close, but still about 10-15 degrees off. (The first door is always spot on though) The items are all the correct distance away from the centre point though. I'm sure that this is a really obvious answered question, but I haven't been able to solve it for hours and I've googled my arse off.
Can anyone fix my algorithm?
sin and cos take their arguments in radians, not degrees. You want segrad = 2 * pi / the_number_of_items (SEGment RADians) instead of segdeg = 360.0 / the_number_of_items (SEGment DEGrees).
Your implementation might provide a value for pi, but there's none in the standard so you might have to use boost::math::constants::pi<double>() or put your own in.
Your first item is correct because 0 is 0 in any measure.
int doors = 5;
double rotation = 2 * M_PI / doors;
for(int door = 0; door < doors; ++door)
{
double door_rotation = door * rotation;
double door_x = reference_x + cos(door_rotation);
double door_y = reference_y + sin(door_rotation);
}
I have this simple euler integrator. For finding precise collision times it should handle negative dt's as well (I divide the frame time and simulate back when I detect a collision).
For some reason
someBody.update(1.0);
someBody.update(-0.3);
someBody.update(-0.3);
someBody.update(0.6);
gives different results than:
someBody.update(1.0);
It might be because I use euler instead of RK4 or verlet?
Here's the code for the integrator:
void Body::update(double dt)
{
if (dt > 0) velocity += acceleration * (dt*dt);
else velocity -= acceleration * (dt*dt);
pos += velocity * dt;
rotation += angularVelocity * dt;
}
Thanks a lot!
Maarten
The reason is math. Let's focus on velocity variable:
If you call:
someBody.update(1.0)
you'll get:
velocity += acceleration * 1
But if you call:
someBody.update(1.0);
someBody.update(-0.3);
someBody.update(-0.3);
someBody.update(0.6);
you will get:
velocity += acceleration * (1 - 0.3^2 - 0.32^2 + 0.6^2)
(which gives velocity += acceleration * 1.18)
You should have simply velocity += acceleration * dt;
Suspect floating point error accumulation in Body::update.
Example: 1 + 2 + 3 + 4 = 10 but 1.0 + 2.0 + 3.0 + 4.0 = something else.
The easiest fix is to round the results after each major computation step.
Thanks, the acceleration was indeed wrong.
I use a fixed timestep for each frame. This function is to run the simulation for partial frames. So I ignore the accaleraion completely now. This way everything within a frame happens linearly and the acceleration is only added in between frames.
The problem persists though. I think it might be the floating point errors as poited out by Magicianeer.
Although I think these errors would not add up so dramatically. In around 50 frames a bouncing object goes from it's full height (100) to about half that height. In around 200 frames it's still on the floor. When I don't call multiple the updates within the frame it's all fine.
I'll try keeping the position at the end of the frame and putting it back after doing the partial frame simulations. That way the errors won't add up over multiple frames.