Particle system physics acting weird - c++

My particle system's physics update function seems to be incorrect. I'm aiming for all the particles to be attracted towards the mouse.
The particles move towards the mouse pointer just as expected, until they go very near. When they are near, they speed up so much, that they fly far away from the pointer and never return.
Here's the update function:
void updateParticle(particle& p,double time){
const double G=0.000000000066726;
const double POINTERMASS=1000000000000;
double squareDistance=pow(p.coords.x-pointerDevice.x,2)+pow(p.coords.y-pointerDevice.y,2)+pow(p.coords.z-pointerDevice.z,2);
if(squareDistance<0.001)
squareDistance=0.001;//to fix the possible division by zero
coords_3d_f accelerationVector={p.coords.x-pointerDevice.x,p.coords.y-pointerDevice.y,p.coords.z-pointerDevice.z};
accelerationVector=vector_scalar_multiplication(vector_unit(accelerationVector),((G*POINTERMASS)/squareDistance));
accelerationVector=vector_scalar_multiplication(accelerationVector,time);
p.velocity=vector_addition(p.velocity,accelerationVector);
p.coords.x-=p.velocity.x*time;
p.coords.y-=p.velocity.y*time;
p.coords.z-=p.velocity.z*time;
}
When the squareDistance is constant, the program looks OK, but I know it's false.
So, what am I doing wrong?

Force is inversely proportional to the square of the distance, so as the distance approaches 0, force (and acceleration) approach infinity. In other words, if the particles get very close, they also get very fast.
If you want to be physically accurate, make your pointer-object have a finite size, so that particles bounce off of it.
If you don't need to be accurate, you can make the force decrease when the particles are very close.

It's very simple: when particles get in touch with the mouse pointer squareDistance becomes 0 and produces undefined behavior for your particles by ((G*POINTERMASS)/squareDistance) because dividing by zero is illegal.
This might work better for you:
if (squareDistance >= 1.0) // 1.0 is the zero tolerance for your context of pixel distances
{
// proceed normally
accelerationVector=vector_scalar_multiplication(vector_unit(accelerationVector),((G*POINTERMASS)/squareDistance));
accelerationVector=vector_scalar_multiplication(accelerationVector,time);
}
else
{
// no acceleration
accelerationVector=/*{0, 0}*/;
}

When your particle gets very close to the mouse pointer the particle is going to have a very high velocity. When this velocity is multiplied by the time this is when the particle will jump very far away.
You can try to fix this by setting a maximum velocity.

Simulating the equation of motion involve the integration of a function at finite intervals, and so one can only approximate the function. This give rise to instability in the system. An easy and fast solution is to use a fixed-step verlet integration:
void integrate(particle& p, double t2, const particle& mouse)
{
// universal gravitational constant
const double G = 0.000000000066726;
// artificial drag
// set it to 1.0 to not have any drag
// set it to 0.0 to not have any momentum
const double drag = 0.99;
// get direction and distance between the particle and the mouse
dvec3 dir = p.pos - mouse.pos;
double dist2 = dot(dir, dir);
double dist = sqrt(dist2);
dir /= dist;
// calculate relative acceleration vector
dvec3 a = -dir * G * (p.mass + mouse.mass) / dist2;
// verlet integration
dvec3 tmp = p.pos;
p.pos += (p.pos - p.prev_pos) * drag + a * t2;
p.prev_pos = tmp;
}
void update(particle& p, double elapsed, const particle& mouse, double& accumulator)
{
// fixed timestep (arbitrary)
const double timestep = 1.0 / 120.0;
const double timestep2 = timestep * timestep;
// "accumulate" time
accumulator += elapsed;
// "consume" time
while(accumulator > timestep)
{
// perform integration
integrate(p, timestep2, mouse);
accumulator -= timestep;
}
}
Note: It use the GLM math library for clarity.

Related

How to decrement a float variable until it's exactly zero

I am making keyboard controls for a camera, and I am experiencing issues regarding the innacuracy of floats. I have the variables "float Xvelocity" and "float Zvelocity", I increment these velocities by adding or subtracting the acceleration speed every time the respective direction keys are being held (A, D, W or S).
However, everything I try to make the float go towards zero simply work in very strange ways. I thought it would be as simple as:
if (keys[GLFW_KEY_A] & xVelocity >-speed)
xVelocity -= acceleration / deltaTime;
if (keys[GLFW_KEY_D] & xVelocity < speed)
xVelocity += acceleration / deltaTime;
if (keys[GLFW_KEY_W] & zVelocity >-speed)
zVelocity -= acceleration / deltaTime;
if (keys[GLFW_KEY_S] & zVelocity < speed)
zVelocity += acceleration / deltaTime;
if (!keys[GLFW_KEY_A] & xVelocity<0.f) {
xVelocity+=deceleration / deltaTime;
}
if (!keys[GLFW_KEY_D] & xVelocity>0.f) {
xVelocity-=deceleration / deltaTime;
}
if (!keys[GLFW_KEY_W] & zVelocity<0.f) {
zVelocity+=deceleration / deltaTime;
}
if (!keys[GLFW_KEY_S] & zVelocity>0.f) {
zVelocity-=deceleration / deltaTime;
}
origin -= right * xVelocity;
origin -= vec3(front.x, 0.f, front.z) * zVelocity;
This doesn't make my camera completely stop moving! It does decelerate, but it rarely becomes exactly zero, thus it keeps slowly moving after releasing the key, either towards the direction it was going before or away from it. Can someone explain why doesn't this deceleration work as I intended, (probably because of float inaccuracy, but maybe it's another reason) and how may I proceed to fix it?
This is not a floating-point accuracy issue unless your acceleration, deltaTime, and velocity are calibrated so that, mathematically, velocity reaches zero exactly after an integer number of steps. More likely, the problem is simply that your velocity is not an exact multiple of acceleration/deltaTime, even allowing for floating-point issues. For example, suppose velocity is 3.7 and acceleration/deltaTime is .2. Then velocity will step through 3.5, 3.3, 3.1,… .5, .3, .1, and −.1.
So velocity never becomes zero because it crosses right through it. When velocity is at .1 units, you can never decelerate by .2 units; an object stops when its velocity reaches zero.
A solution is to detect this and clamp the result to zero:
if (!keys[GLFW_KEY_A] & xVelocity<0.f)
{
xVelocity = xVelocity >= - deceleration / deltaTime ? 0 : xVelocity + deceleration / deltaTime;
}

Function fails to detect event at random times

I have these functions:
void apply_gravity(double delta){
velocity.y+=9.8*100*delta;
}
void move_body(double delta){
location.x+=velocity.x*delta;
location.y+=velocity.y*delta;
}
void processPhysics(double delta){
apply_gravity(delta);
move_body(delta);
if(location.y>=SCREENY){
velocity.y=-coefficient_of_restitution*velocity.y;
}
}
delta is the time elapsed between two calls to the function.
velocity contains two parts, x and y which represent the increment per second to the location's x and y.
coefficient of restitution represents how much of the original velocity the body retains after collision.
Basically, here is what I want this code to do:
Accelerate downwards by 9.8*100px per second. When the body goes below a limit (SCREENY px), it should bounce back, just like if it is hitting a floor. The collision should be perfectly elastic, and for now, SCREENY does not vary at all.
The code perfectly works for most of the times. BUT, sometimes, instead of "bouncing", the body just passes through the "floor". Basically, it seems that velocity.y does NOT negate even when the body crosses SCREENY. The comparision (location.y>=SCREENY) just fails at random times. Sometimes it works, sometimes it doesn't.
That should not happen.
What is going wrong here?
Your object is still allowed to pass through the floor, only updating the velocity. However, in the next frame, the object may still be in the floor, and have its velocity negated a second time (and third, and fourth). I imagine it would look like it gets stuck and vibrates a bit of a distance within the wall. If the object is within the wall, you should instead calculate where it should be if it had bounced, and update the position AND velocity accordingly. Not just the velocity.
A possible solution could look like:
void apply_gravity(double delta){
velocity.y += 9.8 * 100 * delta;
}
void move_body(double delta){
location.x += velocity.x * delta;
location.y += velocity.y * delta;
}
void processPhysics(double delta){
apply_gravity(delta);
move_body(delta);
if(location.y > SCREENY) // change to > from >= (eliminates edge case)
{
location.y = SCREENY - coefficient_of_restitution * (location.y - SCREENY); // update position as well, accounting for loss of velocity due to bounce
velocity.y = -coefficient_of_restitution * velocity.y;
}
}
The above solution will yield an error if the object is moving greater than one screen length in a single physics tic, so be careful about high speeds. You'll need a slightly more complicated algorithm to handle those situations.

Applying gravity to objects

I'm having trouble with implementing gravity on objects.
I have a bunch of circle objects that I'm drawing using OpenGL. I'm using delta-x and delta-y to move the circles (balls). I'm trying to add a gravitational constant to the y coordinate each frame so it simulates being pulled downward, but I'm not sure exactly how to do that.
Here's the relevant code:
class Ball
{
public:
double x;
double y;
double radius;
double deltaX;
double deltaY;
};
std::vector<Ball> gAllBalls; // a vector of balls with random positions and delta-values
double gGravity = ?; // gravitational constant - I know it's 92 m/s^s, but I don't know how to apply that to deltaX and deltaY
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
for (auto &thisBall : gAllBalls)
{
// draw
glColor3d(thisBall.red, thisBall.green, thisBall.blue);
DrawCircle(thisBall.x, thisBall.y, thisBall.radius);
// gravity - not working
if (thisBall.y + thisBall.radius < gScreenHeight - gGravity)
{
thisBall.y += gGravity;
}
// wall bouncing
if (thisBall.y + thisBall.radius + thisBall.deltaY >= gScreenHeight) // up
{
thisBall.deltaY = -thisBall.deltaY;
}
if (thisBall.y + thisBall.deltaY - thisBall.radius < 0) // down
{
thisBall.deltaY = -thisBall.deltaY;
}
if (thisBall.x + thisBall.deltaX - thisBall.radius < 0) // left
{
thisBall.deltaX = -thisBall.deltaX;
}
if (thisBall.x + thisBall.radius + thisBall.deltaX >= gScreenWidth) // right
{
thisBall.deltaX = -thisBall.deltaX;
}
// move
thisBall.x += thisBall.deltaX;
thisBall.y += thisBall.deltaY;
}
glutSwapBuffers();
glutPostRedisplay();
}
A big problem I'm having is that I don't know how to calculate the gravity, since I'm using deltaX and deltaY instead of having separate speed and distance variables to calculate 92 m/s^2. However, no matter what I set the gravity to, the balls don't behave like they should - regardless of gravity strength, so there has to be something else wrong, but I don't know what.
I think the problem here is the physics, rather than the programming technique.
In your case, I would change the 'delta' members of your Ball class to 'speed', since they are a unit of distance that change the position of your object per cycle (time), however this is just a suggestion to make it easier to visualize...
class Ball
{
public:
double x;
double y;
double radius;
double speedX;
double speedY;
};
In second place, I in your code, you are changing the 'y' member, rather than the speed, and since gravity changes speed, hence the problem.
Try doing that, and for debugging purposes, I would try with punctual objects (no radius, just plain (x,y) coordinates).
So, to conclude, I would simply change your gravity code to the following:
// gravity - 'fixed'
if (thisBall.y + thisBall.radius < gScreenHeight - gGravity)
{
thisBall.speedY -= gGravity; //notice the '-'
}
The value of gravity should be absolute and positive, so as to keep things as simple as posible. If you try this, you should have an ideal simple physics simulator, with a ball of constant speedX (only changing its direction, not magnitude).
Please try this, and let me know how it went, good luck, keep it up =)

C++/SDL Gravity in Platformer

I'm currently trying to get a form of gravity (it doesn't need to be EXACTLY gravity, no realism required) into my platformer game, however I'm stumbling over logic on this.
The following code is what I use when the up arrow or W is pressed, (jumping)
if (grounded_)
{
velocity_.y -= JUMP_POWER;
grounded_ = false;
}
In my Player::Update() function I have
velocity_.y += GRAVITY;
There's more in that function but it's irrelevant to the situation.
Currently the two constants are as follows: GRAVITY = 9.8f; and JUMP_POWER = 150.0f;
The main issue I'm having with my gravity that I cannot find the proper balance between my sprite being able to make his jumps, and being way too floaty.
Long story short, my questions is that my sprite's jumps as well as his regular falling from one platform to another are too floaty, any ideas on how to scale it back to something a tad more realistic?
Instead of thinking in terms of the actual values, think in terms of their consequences.
So, the initial velocity is -jump_power, and the acceleration gravity. A little calculus gives
y = -Height = -jump_power * t + 1/2 * gravity * t^2
This assumes a small time step.
Then, the
time_in_flight = 2 * time_to_vertex = jump_power/gravity
and the vertex is
height(time_to_vertex) = jump_power^2/(4 * gravity)
Solving these, and adjusting for time step and fixing negatives
jump_power = (4 * height / time) * timestep_in_secs_per_update
gravity = (2 * jump_power / time) * timestep_in_secs_per_update
That way, you can mess with time and height instead of the less direct parameters. Just use the equations to gravity and jump_power at the start.
const int time = 1.5; //seconds
const int height = 100 //pixels
const int jump_power = (4 * height / time) * timestep_in_secs_per_update;
const int gravity = (2 * jump_power / time) * timestep_in_secs_per_update;
This is a technique from maths, often used to rearrange a family of differential equations in terms of 'dimensionless' variables. This way the variables won't interfere when you try to manipulate the equations characteristics. In this case, you can set the time and keep it constant while changing the power. The sprite will still take the same time to land.
Of course 'real' gravity might not be the best solution. You could set gravity low and just lower the character's height while they are not grounded.
You need think unit system correctly.
The unit of the gravity is meter per second squared. ( m/(s*s) )
The unit of a velocity is meter per second. ( m/s )
The unit of a force is Newton. ( N = kg*m/(s*s) )
Concept example:
float gravity = -9.8; // m/(s*s)
float delta_time = 33.333333e-3f; // s
float mass = 10.0f; // Kg
float force = grounded_ ? 150.0f : gravity; // kg*m/(s*s)
float acceleration = force / mass; // m/(s*s)
float velocity += acceleration * delta_time; // m/s
float position += velocity * delta; // m
It is based on the basic Newton's Equation of motion and Euler's Method.

C++ how to make velocity equal in all directions?

I feel this is a difficult question to articulate, so I have illustrated on this graph (I am using SDL in C++).
Each square represents a pixel on the screen, I want the red pixel to move at the same speed regardless of direction.
If the speed is 8 pixels/sec then after 1 second:
If the user input is right OR down the pixel will arrive at the position marked in blue
If the user input is right AND down it will arrive at the position marked green.
In both cases the pixel has been displaced by 8 pixels, however.. The euclidean distance between red and blue = 8.00 and red and green = 11.31. I want the pixel to arrive at yellow instead.
So I have tried to correct this by declaring a constant speed, then I divide this by the actual displacement, giving me a number I use to multiple the X and Y coordinates and travel back along the trajectory, limiting my speed.
The code looks sorta like this (I have commented the area of interest):
float velX = 0, velY = 0, currentX, currentY;
int time = 0, speed = 300;
//Events
void handleInput(){
if( event.type == SDL_KEYDOWN ){
switch( event.key.keysym.sym ){
case SDLK_UP: {velY -= speed;} break;
case SDLK_DOWN: {velY += speed;} break;
case SDLK_LEFT: {velX -= speed;} break;
case SDLK_RIGHT: {velX += speed;} break;
}
}
else if( event.type == SDL_KEYUP ){
//do the opposite
}
}
//Logic
void move(){
//float dist = sqrt( (velX*velX) + (velY*velY) );
//
//if(dist > 0){
// velX *= speed / dist;
// velY *= speed / dist;
//}
currentX += velX * (get_delta_ticks(&time) / 1000.f);
currentY += velY * (get_delta_ticks(&time) / 1000.f);
set_delta_ticks(&time);
}
//Render
void Player::render(){
apply_surface(currentX, currentY, spriteSheet, screen, &currentClip);
}
So here is my question, I am new to programming games and I'm unsure if this is the CORRECT way to be doing movement.. It seems a bit inefficient in ways, should I be trying to deduce the position based on an angle and the length of the hypotenuse instead? I don't know very much about trigonometry but of course I am keen to learn.
Separate the logical position from the display position.
The logical position will probably need to use floating-point coordinates, and you'll round them to integer pixel coordinates for the display position. You can even do anti-aliasing with this if you want to smooth the movement.
So:
right would have logical unit vector (x,y)=(1.0,0.0)
down would have logical unit vector (x,y)=(0.0,-1.0)
down+right would have logical unit vector (x,y)=(1/sqrt(2),-1/sqrt(2))
every 1/8th of a second, you add the unit vector to your current logical location, and select which pixel to draw. Obviously you can choose different units and update frequencies, but this will give the numbers you asked for.
You need to get the speed in a 2D Space. To get it you have to do a sqrt with both speeds.
curSpeed = sqrt( ( velX * velX ) + (velY * velY ) );
The point is: You counted 8-x and 8-y key press events, which lead to a shortest distance from the origin of v=sqrt(8*8+8*8)=11.31, exactly as you observed.
You should be aware, that, within the time you are measuring, either 8 (only x OR y) or 16 (x plus y) key press events might be sampled, resulting in different "speeds", where speed=number_of_key_events/period_of_time
If you want to travel to the "yellow" spot, there should be only 6 X key press events plus 6 Y key press events in the same period of time in which you sampled the 8 key presses in one of the basic directions.
So there is nothing wrong with your code, and, as the other posters pointed out, your euclidian speed can be calculated using the euclidian distance divided by the sampling period, resulting in v=8 or v=11.31, respectively.
I would start with different user controls: namely absolute speed and direction.
Given speed velAbs and the angle theta, you have
velX = velAbs * cos(theta);
velY = velAbs * sin(theta);
When updating the position, it is typically most convenient to decompose the absolute speed in its X and Y components, update the X and Y positions for the given time interval dt
currentX = velX * dt;
currentY = velY * dt;
whereas for collision impact computations the absolute speed is more relevant.
This will avoid your yellow/green problem because maximum throttle in both the X and Y directions will get you to green. Just let the user set the throttle from 0 to 8 and also set a direction, then you will get to yellow or blue.
Well, it looks like most people forgot about analog input...
Anyway, It should work like this:
velX, velY are floats within [-1.0..1.0] range.
In case of digital input (keyboard, dpad), pressing "left" sets velX to -1, pressing "right" sets velX to 1, etc.
However, if you use analog stick, you put floating point values, where velX == 1.0 corresponds to rightmost position of analog stick, velX == -1.0 corresponds to leftmost position, and so on.
maxSpeed is maximum game movement speed, also float.
With all this in mind, you could calculate next position of object like this:
void move(){
float curVelX = velX, curVelY = velY;
float moveSquared = (curVelX*curVelX + curVelY*curVelY);
if (moveSquared > 1.0f){
float d = sqrtf(moveSquared);
curVelX /= d;
curVelY /= d;
}
currentX += curVelX * maxSpeed * (get_delta_ticks(&time) / 1000.f);
currentY += curVelY * maxSpeed * (get_delta_ticks(&time) / 1000.f);
set_delta_ticks(&time);
}
It seems a bit inefficient in ways,
Look, you have ONE object. When you'll have few hundreds of thousands of them, then you can start worrying about efficiency.
should I be trying to deduce the position based on an angle and the length of the hypotenuse instead?
If your object is torpedo-like and can slowly turn left/right and accelerate/decelerate (you can steer it a bit and make it go faster/slower), then you probably need to store movement direction and linear movement speed.
If your object is some kind of flying orb or rolling ball that can go in any direction it wants, then you should use method similar to the one I described. Have separate velocity for x/y and limit maximum linear velocity using sqrtf.