Separating two colliding circles - c#

I'm trying to separate two circles that are colliding. Thanks to the help of others, I'm right there!
This is my code:
var totalRadius : Number = _circle1.radius + _circle2.radius;
var x : Number = _circle1.position.x - _circle2.position.x;
var y : Number = _circle1.position.y - _circle2.position.y;
var distanceSquared : Number = (x * x) + (y * y);
if (distanceSquared < totalRadius * totalRadius)
{
var distance : Number = Math.sqrt(distanceSquared);
var separation : Number = totalRadius - distance;
var unitVectorX : Number = (_circle1.position.x - _circle2.position.x) / distance;
var unitVectorY : Number = (_circle1.position.y - _circle2.position.y) / distance;
_circle1.position.x += unitVectorX * (separation / 2);
_circle1.position.y += unitVectorY * (separation / 2);
_circle2.position.x -= unitVectorX * (separation / 2);
_circle2.position.y -= unitVectorY * (separation / 2);
}
It works great if the circles have the same velocity. The problem occurs when they have different velocities and the problem is because I'm splitting the separation evenly (separation / 2) I think!
Everything works perfectly if circle1 has a velocity of 1,0 and circle2 has a velocity of -1,0. The two circles hit each other and stop.
If circle1 has a velocity of 2,0 and circle2 has a velocity of -1,0, the circles gradually move to the right. I imagine this is what's happening:
frame1:
circle1 (99, 100)
circle2 (101, 100)
frame2:
circle1 (101, 100)
circle2 (100, 100)
collision detected, corrected position of -0.5 and +0.5 respectively.
circle1 (100.5, 100)
circle2 (100.5, 100)
frame3:
circle1 (102.5, 100)
circle2 (99.5, 100)
collision detected, corrected position of -1.5 and +1.5 respectively.
circle1 (101, 100)
circle2 (101, 100)
frame4:
circle1 (103, 100)
circle2 (100, 100)
collision detected, corrected position of -1.5 and +1.5 respectively.
circle1 (101.5, 100)
circle2 (101.5, 100)
As you can see, both circles are gaining +0.5 to the right because of the difference of velocity.
So finally, my question: How can I factor in their velocity into the equation so that it doesn't play a factor in their separation?
Thanks!

You need to calculate the point of impact, rather than just (arbitrarily) moving them both backwards equally.
A quick search found these links:
http://www.t3hprogrammer.com/research/circle-circle-collision-tutorial#TOC-Static-Circle-Circle-Collision-Dete (the section "Dynamic Circle-Circle Collision")
http://nonlinear.openspark.com/tutorials/vectors/index.htm (section "Impact, not intersection")

to make the answer short: you will have to get momentum in there ;)
As I guess you want to have the masses the same it comes down to "v1_before^2 + v2_before^2 = v1_after^2 + v2_after^2". As the wiki-article suggests I just "switch" the velocities.
What I do not understand is why you think the circles will both move to the right? Isn't this suppost to be a elastic collision? Then they should go in different directions if you want them to have same mass.

Related

Changing the Scale of an object in one direction only

I have a cylinder that moves with Translate, I need the platform to always be near the center of the cylinder. As in the photo below.
.
To do this, I wrote a script that calculates how far the cylinder has moved, and adds this number to the platform scale and adds this number divided by two to the platform Z position.
if (transform.position.z > spawnedBlocks[spawnedBlocks.Count - 1].transform.position.z)
{
zDistance = transform.position.z - spawnedBlocks[spawnedBlocks.Count - 1].transform.position.z;
spawnedBlocks[spawnedBlocks.Count - 1].transform.position = new Vector3(spawnedBlocks[spawnedBlocks.Count - 1].transform.position.x, spawnedBlocks[spawnedBlocks.Count - 1].transform.position.y, spawnedBlocks[spawnedBlocks.Count - 1].transform.position.z + (zDistance / 2));
spawnedBlocks[spawnedBlocks.Count - 1].transform.localScale = new Vector3(1, 0.1f, spawnedBlocks[spawnedBlocks.Count - 1].transform.localScale.z + zDistance);
}
This code is in my Fixed Update and it turns out that the platform moves much faster and goes forward.
The main mistake you make is in the positioning.
Cleaned up a bit your code looks somewhat like this:
var myPos = transform.position;
var block = spawnedBlocks[spawnedBlocks.Count - 1].transform;
var blockPos = block.position;
if (myPos.z > blockPos.z)
{
zDistance = myPos.z - blockPos.z;
block.position += Vector3.forward * zDistance / 2f;
block.localScale = new Vector3(1, 0.1f, block.z + zDistance);
}
But now have a careful look what you are doing here:
if the myPos.z is bigger than blockPos.z you will move it forward
Now wait a minute! Shouldn't it rather stay at the center between its original spot and the myPos?
Example:
Let's say both objects start at z=0.
Now the myPos moves to z=2 (just example numbers)
zDistance = 2
you move the block to current position + half of distance so 0 + 1
This frame you don't move at all (easier to explain this way)
myPos.z (2) is still bigger than blockPos.z (1)
so distance is now 1
you move the block to current position + half of distance 1 + 0.5
Etc. You see what you are doing is basically interpolating the block position towards the transform.position, not how it should be to the center between the original position and transform.position!
So one solution (I don't see your full code) would be to store the original z position and make sure you are always at the center between that original position and the current transform.position
block.position = originalBlockPosition + Vevtor3.forward * (transform.position - originalBlockPosition).z / 2f;
As mentioned way simpliest though would be to rather just create a parent for your object and make an offset this that the parent is exactly the pivot you want to scale around. Somewhat like
*-ParentPivot
[*****Your Block*****]
This way you can simply scale the parent and the block will always be stretched correctly! You would only have to make sure your block by default has a width of 1 unit the you could directly use something like
parent.localScale = new Vevtor3(1, 1, transform.position.z - parent.position.z);

Counting Rotations [Unity C#]

Trying to figure out a way to increment the rotation count after the spinner passes 360 degrees. Though this never works because eulerAngles reset at 360. What's a good way to do this? I tried doing > 355, but that is't a great solution.
Spinner.transform.Rotate(0, 0, speed);
float angleZ = Spinner.rotation.eulerAngles.z;
if (angleZ > 360)
{
Rotations++;
}
You would need to check the angle before & after the rotation.
According to the documentation "speed" is the number of degrees to rotate, so add the integer division of speed by 360 to Rotations & then check for the final value having been reset.
float angleZ1 = Spinner.rotation.eulerAngles.z;
Spinner.transform.Rotate(0, 0, speed);
float angleZ2 = Spinner.rotation.eulerAngles.z;
float angleZDiff = angleZ2 - angleZ1;
Rotations += (int)(speed / 360);
if ((speed >0) && (angleZDiff < 0))
Rotations++
else if ((speed < 0) && (angleZDiff > 0))
Rotations--;
You can divide the angle by 360 and cast it to an int to get the number of rotations.
Rotations = (int)(angle / 360f);
You could add two colliders at varying distances then the collision check points will give extra data points that are correct regardless of the speed

Randomly pick two numbers in a range so that the sum of their squares is constant

I'm currently developing a 2-player Ping-Pong game (in 2D - real simple) from scratch, and it's going good. However Theres a problem I just can't seem to solve - I'm not sure if this should be located here or on MathExchange - anyway here goes.
Initially the ball should be located in the center of the canvas. When pressing a button the ball should be fired off in a completely random direction - but always with the same velocity.
The Ball object has (simplified) 4 fields - The position in X and Y, and the velocity in X and Y. This makes it simple to bounce the ball off the walls when it hits, simple by inverting the velocities.
public void Move()
{
if (X - Radius < 0 || X + Radius > GameWidth)
{
XVelocity = -XVelocity;
}
if (Y - Radius < 0 || Y + Radius > GameHeight)
{
YVelocity = -YVelocity;
}
X+= XVelocity;
Y+= YVelocity;
}
I figured the velocity should be the same in each game, so I figures I would use Pythagoras - the square of the two velocities should always be the same.
SO for the question:
Is there a way to randomly select two numbers (doubles) such that the sum of their squares is always a specific number - more formally:
double x = RandomDouble();
double y = RandomDouble();
if (x^2 + y^2 = 16) {/* should always be true */ }
Any help appreciated :)
Randomly pick an angle theta and multiply that by the magnitude of the distance d you want. Something like:
double theta = rand.NextDouble() * 2.0 * Math.PI;
double x = d * Math.Cos(theta);
double y = d * Math.Sin(theta);
If the constant is C, pick a number x between 0 and sqrt(C).
Solve for the other number y using simple algebra.
why not try this:
double x = RandomDouble();
double y = square(16-x^2);
as your application allow double type.
does this solve your problem?
if not, please let me know

How to "round" a 2D Vector to nearest 15 degrees

I'm working on a simple game and I'm trying to simplify part of the 2D collision reaction in the game. When certain objects hit walls, I'm calculating a collision normal (collisionPoint - objectCenter) and reflecting based on that normal. I'm interested in rounding that normal vector to its nearest 15° but I'm not sure of a good way to go about that.
My current thought is doing something like this
float angle = atan2(normal.Y, normal.X) * Rad2Deg;
float newAngle = ((int)(angle + 7.5f) / 15) * 15.0f * Deg2Rad;
vector2 newNormal = vector2(cos(newAngle), sin(newAngle));
Is this a reasonable way to do it? Is there a better way?
Try this:
float roundAngle = 15 * Deg2Rad;
float angle = (float)Math.Atan2(normal.Y, normal.X);
Vector2 newNormal;
if (angle % roundAngle != 0)
{
float newAngle = (float)Math.Round(angle / roundAngle) * roundAngle;
newNormal = new Vector2((float)Math.Cos(newAngle), (float)Math.Sin(newAngle));
}
else
{
newNormal = Vector2.Normalize(normal);
}
You don't need to add 7.5, take this example:
// 4 degrees should round to 0
(4 + 7.5) / 15 == 11.5 / 15 == 0.77
// When this gets rounded up to 1 and multiplied by 15 again, it becomes 15 degrees.
// Don't add 7.5, and you get this:
4 / 15 == 0.27
// When rounded, it becomes 0 and, as such the correct answer
// Now how about a negative number; -12
-12 / 15 == -0.8
// Again, when rounded we get the correct number
actually this is more correct if you want the nearest 15 degree angle :
do this:
newangle% = INT(((angle%+7.5)/15)*15)
INT ALWAYS rounds DOWN by default this should properly give you the nearest angle in any case that is positive or negative have fun!!
and add the part where you use degree to rad and rad to degree if needed INSIDE the parens (like right next to angle% if that angle is not given in degrees then use some sort of rad2deg multiplier inside there
this is more like how you would do this in basic, with some modification It will work in c code or such, well good luck!!

How to separate two colliding circles?

I'm looking to separate two colliding circles, to push them back equally by the minimum amount so that they're perfectly separated.
I have this so far:
var totalRadius : Number = _circle1.radius + _circle2.radius;
var x : Number = _circle1.position.x - _circle2.position.x;
var y : Number = _circle1.position.y - _circle2.position.y;
var distanceSquared : Number = (x * x) + (y * y);
if (distanceSquared < totalRadius * totalRadius)
{
var distance : Number = Math.sqrt(distanceSquared);
var separation : Number = totalRadius - distance;
// No idea what to do now!
}
That much I've figured out so far. So I know a collision has occurred and I know that each circle is separation amount into each other (so I guess divide by 2 to separate them equally).
The problem is that separation doesn't have any implied directionality and I don't know what to do. I can't just do circle1.position -= separation / 2; circle2.position += separation / 2 because that'll move both the X and Y axis equally.
How do I add directionality to separation?
Thanks!
Use unit vector to move both circles by separation / 2.
unitVector = (circle1.Position - circle2.Position) / distance
circle1.Position += unitVector * seperation / 2
circle2.Position -= unitVector * seperation / 2
Edit:
just change seperation / 2 and/or +- part. It will allow you to move it by any distance you want.

Categories