2D Elastic Collisions 'Sticking' Issue - c#

I have a simulation with multiple circles moving in 2D space.
There is collision detection between them, and the elastic collisions work 95% of the time. Occasionally however, when two balls hit each other, they stick to each other and overlap, often orbiting each other while being stuck together.
I'm unsure how to solve this problem.
My collision management function looks like this:
void manageCollision(Particle particleA, Particle particleB)
{
float distanceX = particleA.Position.X - particleB.Position.X;
float distanceY = particleA.Position.Y - particleB.Position.Y;
double collisionAngle = Math.Atan2(distanceY, distanceX);
double pA_magnitude = Math.Sqrt(particleA.Velocity.X * particleA.Velocity.X + particleA.Velocity.Y * particleA.Velocity.Y);
double pB_magnitude = Math.Sqrt(particleB.Velocity.X * particleB.Velocity.X + particleB.Velocity.Y * particleB.Velocity.Y);
double pA_direction = Math.Atan2(particleA.Velocity.Y, particleA.Velocity.X);
double pB_direction = Math.Atan2(particleB.Velocity.Y, particleB.Velocity.X);
double pA_newVelocityX = pA_magnitude * Math.Cos(pA_direction - collisionAngle);
double pA_newVelocityY = pA_magnitude * Math.Sin(pA_direction - collisionAngle);
double pB_newVelocityX = pB_magnitude * Math.Cos(pB_direction - collisionAngle);
double pB_newVelocityY = pB_magnitude * Math.Sin(pB_direction - collisionAngle);
double pA_finalVelocityX = ((particleA.Mass - particleB.Mass) * pA_newVelocityX + (particleB.Mass + particleB.Mass) * pB_newVelocityX) / (particleA.Mass + particleB.Mass);
double pB_finalVelocityX = ((particleA.Mass + particleA.Mass) * pA_newVelocityX + (particleB.Mass - particleA.Mass) * pB_newVelocityX) / (particleA.Mass + particleB.Mass);
double pA_finalVelocityY = pA_newVelocityY;
double pB_finalVelocityY = pB_newVelocityY;
particleA.Velocity = new Vector2((float)(Math.Cos(collisionAngle) * pA_finalVelocityX + Math.Cos(collisionAngle + Math.PI / 2) * pA_finalVelocityY), (float)(Math.Sin(collisionAngle) * pA_finalVelocityX + Math.Sin(collisionAngle + Math.PI / 2) * pA_finalVelocityY));
particleB.Velocity = new Vector2((float)(Math.Cos(collisionAngle) * pB_finalVelocityX + Math.Cos(collisionAngle + Math.PI / 2) * pB_finalVelocityY), (float)(Math.Sin(collisionAngle) * pB_finalVelocityX + Math.Sin(collisionAngle + Math.PI / 2) * pB_finalVelocityY));
}
Each ball or particle spawns with a random mass and radius.
The function is called within an update type of method, like this:
Particle pA = particles[i];
for (int k = i + 1; k < particles.Count(); k++)
{
Particle pB = particles[k];
Vector2 delta = pA.Position - pB.Position;
float dist = delta.Length();
if (dist < particles[i].Radius + particles[k].Radius && !particles[i].Colliding && !particles[k].Colliding)
{
particles[i].Colliding = true;
particles[k].Colliding = true;
manageCollision(particles[i], particles[k]);
particles[i].initColorTable(); // Upon collision, change the color
particles[k].initColorTable();
totalCollisions++;
}
else
{
particles[i].Colliding = false;
particles[k].Colliding = false;
}
}

This situation stems from the discrete computation and big step size of duration.
When you observe the objects with some time interval dt, you can observe some intersection between two circles and call your collision method but in the next time step they may still overlap although they are going in different directions after the collision in the previous step.
To reduce this effect, you can try a lower time step size so that the overlap ratio between objects may be reduced.
As a more complicated solution, you can keep a list of your collided objects for every step and during iterations you can check this list if current intersected circles had any "affairs" in the previous step.

Related

How can I program a changing gravitational force with the altitude of an object (Unity/c#)?

What I currently tried using the gravitational force equation (GMm/(r + h)^2):
public double gravityForce()
{
double earthMass = 5.97 * Mathf.Pow(10,24);
double earthRadius = 6.38 * Mathf.Pow(10,6);
double distanceSquared = ((Projectile.transform.position.y) + earthRadius) * (( Projectile.transform.position.y) + earthRadius);
double G = 6.67 * Mathf.Pow(10,-11);
double force = (G * 1 * earthMass) / distanceSquared;
return (force);
}
and then
float gForce = (float)gravityForce();
Projectile.AddForce(-transform.up * gForce);
This created a parabolic path, but when I tried using this for a low altitude projectile or when I tried setting the force to a constant number (9.8) it also looked very different from the path when I just set gravity scale to 1.

Parsing LSM6DSL raw values

I'm trying to parse the values given from a device with a LSM6DSL chip (gyroscopic and acc.) and I'm having a hard time parsing the data properly for positioning and angle.
From the vendor I've received the information that the unit is running on a resolution of 2000 for the gyro, 8g for the acc.
I receive the data in bytes that are converted by the following to shorts;
public int[] BufferToMotionData(byte[] buffer, int segments = 2)
{
int[] motionDataArray = new int[segments * 3];
int offset = Constants.BufferSizeImage + Constants.CommandLength;
for (int i = 0; i < 6; i++)
{
motionDataArray[i] = BitConverter.ToInt16(buffer, offset + (i * 2));
if (motionDataArray[i] >= Int16.MaxValue)
motionDataArray[i] -= 65535;
}
return motionDataArray;
}
(Edit; Cleaned up version)
This returns values in the range of (example) 961, -16223, -1635, 664, -269, -597.
According to the spec sheet I'm supposed to multiply each vector with it's corresponding value.. * 70f for gyro, .448f for acc.
From the documentation I understand that for the G forces these are in milliG's and gyro in millidegrees per sec?
// Gyro X,Y,Z
gx = Mathf.Deg2Rad * (motionData[0] * 70f / 1000f);
gy = Mathf.Deg2Rad * (motionData[1] * 70f / 1000f);
gz = Mathf.Deg2Rad * (motionData[2] * 70f / 1000f);
// Acc X,Y,Z
ax = motionData[3] * 0.488f / 1000f;
ay = motionData[4] * 0.488f / 1000f;
az = motionData[5] * 0.488f / 1000f;
Update(gx, gy, gz, ax, ay, az);
Update(..) is Madgwick's quaternion formul, although for velocity I use the acceleration vectors.
G force values that I'm getting at this moment after calculation;
X 0.047824 Y -0.320128 Z 0.006344
X 0.07076 Y -0.2562 Z 0.020008
X 0.099552 Y -0.063928 Z -0.13664
These look awfully low, and if applied as velocity it just runs off in a given direction, I know I'm missing a gravity correct although not entirely sure how to apply this.
I'm under the assumption that I do not need to apply drag to my velocity vector since values should be negated by the acceleration values received?
Anyone with experience with this type of chip and actually applying the values to yaw/pitch/roll (or quaternion) and applying the G forces as linear acceleration.
By looking on existing code on GitHub, it's looks like the sensitivity factor for 8g is 244 µg/digit and not 488 µg/digit as you coded it.
Also it look's like raw values are shifted and are in [-r/2,r/2] instead of [0, r]. So you have to add 500µg or 500µdps to it. (But maybe it's linked to a uint/int issue, anyway are you sure about the endianness?)
See here for acc data and here for gyro data.
Based on that, the code should look likes this:
// Gyro X,Y,Z (in rad/s)
gx = Mathf.Deg2Rad * (motionData[0] * 70000f + 500) / 1000000;
gy = Mathf.Deg2Rad * (motionData[1] * 70000f + 500) / 1000000;
gz = Mathf.Deg2Rad * (motionData[2] * 70000f + 500) / 1000000;
// Acc X,Y,Z (in g)
ax = (motionData[3] * 244f + 500) / 1000000;
ay = (motionData[4] * 244f + 500) / 1000000;
az = (motionData[5] * 244f + 500) / 1000000;
Update(gx, gy, gz, ax, ay, az);

Spline path - prevent overshoot

I have a NavAgent that follows the mouse cursor. A line renderer draws the nav path from a fixed starting point to the cursor with some smoothing achieved by what - with my limited math brain - I understand to be a cubic Hermit spline, adapted from this great Wikibook page:
IEnumerator IDrawPath(Vector3 endPos)
{
while (drawLine)
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 1500))
{
transform.position = endPos;
nav.SetDestination(hit.point);
line.positionCount = numberOfPoints * (nav.path.corners.Length - 1);
for (int j = 0; j < nav.path.corners.Length - 1; j++)
{
p0 = nav.path.corners[j];
p1 = nav.path.corners[j + 1];
if (j > 0)
{
m0 = 0.5f * (nav.path.corners[j + 1] - nav.path.corners[j - 1]);
}
else
{
m0 = nav.path.corners[j + 1] - nav.path.corners[j];
}
if (j < nav.path.corners.Length - 2)
{
m1 = 0.5f * (nav.path.corners[j + 2] - nav.path.corners[j]);
}
else
{
m1 = nav.path.corners[j + 1] - nav.path.corners[j];
}
pointStep = 1.0f / numberOfPoints;
if (j == nav.path.corners.Length - 2)
{
pointStep = 1.0f / (numberOfPoints - 1.0f);
}
for (int i = 0; i < numberOfPoints; i++)
{
t = i * pointStep;
position = (2.0f * t * t * t - 3.0f * t * t + 1.0f) * p0
+ (t * t * t - 2.0f * t * t + t) * m0
+ (-2.0f * t * t * t + 3.0f * t * t) * p1
+ (t * t * t - t * t) * m1;
line.SetPosition(i + j * numberOfPoints,
position);
}
}
}
yield return new WaitForFixedUpdate();
}
}
Sometimes the line 'overshoots' an optimal curve position and doubles back on itself:
I'd like the line to proceed smoothly round those corners instead.
From reading up on splines from various sources I gather this behaviour is correlated to the values of the tangents being calculated (the variables M0 and M1 in this case). I have adjusted these without success; larger values cause the line to stray away from the path points (and increase the size of the overshoot), smaller values cause a 'knot' rather than a loop as the resultant positions are very close together.
I tried filtering out some of the extreme points, hoping this might lead to a smoother line by adding some logic to say if the angle of the next point is above a certain threshold don't add it to the line renderer positions but this yields some odd behaviour. I've tried filtering out points that are very close together, again leading to some odd results.
This is probably simple stuff for a math wizz but I'm all out of ideas. How can I avoid the line doubling back on itself. I've readthrough Monotone cubic interpolation which seems promising but frankly it's way over my head. Can the calculation above be adjusted to do this, or do I need to look for another way?

C# Projectile Simulator - Applying Speed to X Position of Projectile Physics

First of all, here is the code:
decimal gravity = decimal.Parse(gforce.Text) / 1000;
decimal speed = decimal.Parse(initialSpeed.Text) / 1000;
decimal newAngle = DegreesToRadians(decimal.Parse(angle.Text));
double doubleNewAngle = (double)newAngle;
decimal Px = Math.Round(((decimal)Math.Cos(doubleNewAngle) * 0.001M), 13);
decimal Py = Math.Round(((decimal)Math.Sin(doubleNewAngle) * 0.001M), 13);
string PxTemp = "";
for(decimal t = 0.001M; Py > 0; t = t + 0.001M)
{
gravity = gravity + gravity * 0.001M;
Py = Py + speed - gravity;
Px = (Px + speed * Px);
graphics.DrawArc(new Pen(Color.Magenta, 10f), 45 + (float)Px, 475 - (float)Py, 1, 1, 0, 360);
try
{
graphics.DrawArc(new Pen(Color.Magenta, 10f), 45 + (float)Px, 475 - (float)Py, 1, 1, 0, 360);
}
catch
{
MessageBox.Show("Error Px: " + Px.ToString() + " Py: " + Py.ToString(), "Error");
this.Close();
}
I am attempting to create a projectile simulator, I have successfully created the effect of gravity and acceleration on the y-axis. But however when applying the speed to the x axis(making the speed depend on the angle) I am having trouble. I can make it so every second the projectile moves 1 metre but for it to be correct the projectiles' speed across the x-axis should depend on the speed AND THE ANGLE.
To achieve this I have done:
Px = Px + (speed * Px)
Where Px is the value of distance across the axis Cosine of the angle:
decimal Px = Math.Round(((decimal)Math.Cos(doubleNewAngle) * 0.001M), 13);
When I do
Px = Px + (speed * Px)
The value returns some huge number for example 4412651515851.41214244121, I at first assumed this was because Px was going beyond its precision point but any rounding attempts I have made have failed, How should I achieve a correct Px number?
Here is an image to visualise it:
Any help would be greatly appreciated, I have been struggling all day and I couldn't find anything on-line. Thanks in advance.
The laws of motion are very different to the ones you use:
y'' = -g --> y(t) = y0 + vy0*t - g/2*t*t
x'' = 0 --> x(t) = x0 + vx0*t
These are the solutions for motion without air friction. Most complications of the equation of motion require numerical integration of the ODE.
The initial velocities vx0,vy0 are what you initially compute in Px,Py. But probably you should use
vx0 = speed*cos(angle)
vy0 = speed*sin(angle)
to get the initial velocity compatible with the inputs. Some additional unit conversions may be required.
For a useful tool to help workout the proper calculations.http://www.mrmont.com/teachers/physicsteachershelper-proj.html

Calculating Lean Angle with core motion of iOS device

I am trying to calculate the lean angle/inclination of my iOS device.
I did a lot of research and found a way how to give me the most accurate lean angle/inclincation. I used quaternion to calculate it.
This is the code that I use to calculate it.
public void CalculateLeanAngle ()
{
motionManager = new CMMotionManager ();
motionManager.DeviceMotionUpdateInterval = 0.02; // 50 Hz
if (motionManager.DeviceMotionAvailable) {
motionManager.StartDeviceMotionUpdates (CMAttitudeReferenceFrame.XArbitraryZVertical, NSOperationQueue.CurrentQueue, (data, error) => {
CMQuaternion quat = motionManager.DeviceMotion.Attitude.Quaternion;
double x = quat.x;
double y = quat.y;
double w = quat.w;
double z = quat.z;
double degrees = 0.0;
//Roll
double roll = Math.Atan2 (2 * y * w - 2 * x * z, 1 - 2 * y * y - 2 * z * z);
Console.WriteLine("Roll: " + Math.Round(-roll * 180.0/Constants.M_PI));
degrees = Math.Round (-applyKalmanFiltering (roll) * 180.0 / Constants.M_PI);
string degreeStr = string.Concat (degrees.ToString (), "°");
this.LeanAngleLbl.Text = degreeStr;
});
}
public double applyKalmanFiltering (double yaw)
{
// kalman filtering
if (motionLastYaw == 0) {
motionLastYaw = yaw;
}
float q = 0.1f; // process noise
float r = 0.1f; // sensor noise
float p = 0.1f; // estimated error
float k = 0.5f; // kalman filter gain
double x = motionLastYaw;
p = p + q;
k = p / (p + r);
x = x + k * (yaw - x);
p = (1 - k) * p;
motionLastYaw = x;
return motionLastYaw;
}
This works perfect when you walk and tilt your device. But when I drive my car something happens that the quaternion isn't giving me the correct lean angle/inclincation. It just gives me 180°.. Or suddenly it shows me a totally wrong lean angle/inclination.. Like when I am standing still (at trafic lights) it shows me 23°... Then after driving a bit it works again or it shows again 180°.
Could it be that the quaternion is effected by the acceleration of my car? So that because my car is driving at a certain speed it isn't giving me the correct value?
Does anyone have any solution for this?
I would like to calculate my lean angle/inclincation when I drive my bike/car. So I really want to know how to calulate the right lean angle/inclincation independent if I drive my car/bike or not.
Thanks in advance!

Categories

Resources