2D Ball Collision and resolution - c#

I have created a simple method that detects collision between the two balls by calculating the distance. I was wondering, once the collision is detected, how could I update the balls positions to not allow the balls to enter each other(intersect)
private void BallCollisionBlueRed()
{
double fDist;
CentreAX = redBall.Left + ball.Width / 2;
CentreAY = redBall.Top + ball.Height / 2;
CentreBX = blueBall.Left + ball.Width / 2;
CentreBY = blueBall.Top + ball.Height / 2;
vDx = CentreBX - CentreAX;
vDy = CentreBY - CentreAY;
fDist = Math.Sqrt((vDx * vDx) + (vDy * vDy));
if (fDist < radA + radB)
{
// Help!
}
}
vDx and vDy are only used to hold the value for the calculations. I am controlling both the balls with arrow keys(players), I don't want them to bounce off each other, but just not allow them to intersect.

You need to picture the interaction in your head. When the distance is exactly zero the objects will bounce and start moving away from each other.
It's been too long since college to calculate the new trajectory BUT the main thing will be that, if radA + radB - fDist is, say -4, you'll need to set the new distance to radA + radB + 4.
That will accomodate for any low fps you have (until they lag so bad that they go THROUGH each other before you can detect the collision :-p

For some good advice on this type of physics in I would read this blog post:
http://www.wildbunny.co.uk/blog/2011/04/06/physics-engines-for-dummies/

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);

Camera is not centering between 2 gameobjects correctly

I have a button that makes the MainCamera switch between being in the middle of several objects.
For example, there's the [StartPoint , CheckPoint , CheckPoint , EndPoint]
I want the camera to switch continuously across the checkpoints like the comma between the checkpoints. ^^
This code is the actual switching the position placing for the MainCamera (where the error might be)
public void SwitchCameraBetween(GameObject nextPoint, GameObject afterNextPoint)
{
Vector3 centerPoint = (afterNextPoint.transform.position - nextPoint.transform.position) / 2;
centerPoint.z = -10;
float cameraSize = (afterNextPoint.transform.position - nextPoint.transform.position).magnitude - nextPoint.transform.localScale.x;
mainCamera.transform.position = centerPoint;
mainCamera.orthographicSize = cameraSize / 3;
}
The camera switches perfect for the first 2 checkpoints (either StartPoint & EndPoint or StartPoint & CheckPoint) after the camera stays in same position or off by a couple values. I checked by using Debug.Log(); to see if the camera has the correct gameobjects between and it does so why does it not work?
The center point should be the average of all points, so you need to add your points, not subtract them, and then divide the result by the number of points included for the average.
center = sum(points) / count(points), so center = (a + b) / 2
Alternatively, you could use Vector3.Lerp(a, b, 0.5f) if you find that more readable, although technically this would be slower, since it's both a method call and more operations, unless the compiler is doing fancy things behind the scenes...

MathHelper.WrapAngle() rotation issue

My game involves ships moving and rotating around a target (i.e., an enemy ship). Rotation depends on whether the user wants to rotate by port/starboard, or just by the closest side to the enemy.
The problem:
The angles are being wrapped with MathHelper.WrapAngle(). Keeping the angles between PI and -PI works great, until the rotating ship gets to the point where -3.141 becomes 3.141 (and vice versa). For example, the ship is rotating to port correctly, then when it hits this line it flips over starboard, then back to port again, then starboard again and so on!
I would be very grateful if the community could point out:
What I can do to make the ship rotate logic work correctly when going over the PI/-PI wrap 'barrier'
Point out any inefficiencies in my code (I'm sure there are many, and I'm sure there are many other ways to do this more efficiently)
Link to any relevant articles or tutorials that can help me overcome this issue (this is my first game)
Additional Information:
Ship.ShipMoveState.NoMoveRotate is essentially a flag that tells the ship to rotate (starting at 100th of max speed up until we hit max speed) either port or starboard, whichever is closest. The ship rotates to these sides as that is where the weapons are located. ShipMoveState.AwaitFurtherOrders is tells the ship to rotate depending on the difference in angle between closest side (port/starboard) and angle to enemy.
ShipCompartment primeCompartment = TargetShip.CenterCompartment;
if (FireState == ShipFireState.FireAtTarget)
primeCompartment = TargetCompartment;
// If ship is to the left of target, below will work
Vector2 distanceToDestination = primeCompartment.Position - CenterCompartment.Position;
float angleToEnemy = (float)Math.Atan2(distanceToDestination.Y, distanceToDestination.X);
angleToEnemy = MathHelper.WrapAngle(angleToEnemy);
CenterCompartment.Rotation = MathHelper.WrapAngle(CenterCompartment.Rotation);
float portBatteryAngle = MathHelper.WrapAngle(CenterCompartment.Rotation - Helpers.RightAngle);
float starboardBatteryAngle = MathHelper.WrapAngle(CenterCompartment.Rotation + Helpers.RightAngle);
float allowance = 0.005f;
bool portIsClosest = false;
switch (primaryFacing)
{
case PreferredFacing.None:
// If port battery not facing enemy
if (angleToEnemy > (MathHelper.WrapAngle(portBatteryAngle + allowance))
|| angleToEnemy < (MathHelper.WrapAngle(portBatteryAngle - allowance)))
{
// And starboard battery not facing either
if (angleToEnemy > (MathHelper.WrapAngle(starboardBatteryAngle + allowance))
|| angleToEnemy < (MathHelper.WrapAngle(starboardBatteryAngle - allowance)))
MoveState = Ship.ShipMoveState.NoMoveRotate;
else
MoveState = ShipMoveState.AwaitFurtherOrders;
}
else
{
portIsClosest = true;
MoveState = ShipMoveState.AwaitFurtherOrders;
}
if (MoveState == ShipMoveState.AwaitFurtherOrders)
{
float diff = 0f;
if (portIsClosest)
diff = angleToEnemy - portBatteryAngle;
else
diff = angleToEnemy - starboardBatteryAngle;
RotateShip(diff);
}
else if (MoveState == Ship.ShipMoveState.NoMoveRotate)
{
// Turn to port (if target is between 6 and 9 o'clock)
if (angleToEnemy < portBatteryAngle)
RotateShip(-MaxRotation / 100);
// Turn to starboard (if target is between 3 and 6 o'clock)
else if (angleToEnemy > starboardBatteryAngle)
RotateShip(MaxRotation / 100);
else
{
if (angleToEnemy > portBatteryAngle && angleToEnemy < starboardBatteryAngle)
{
// Turn to starboard (if target is between 9 and 12 o'clock)
if (angleToEnemy < CenterCompartment.Rotation)
RotateShip(MaxRotation / 100);
// Turn to port (if target is between 12 and 3 o'clock)
else
RotateShip(-MaxRotation / 100);
}
}
}
break;
}
Please let me know if you require any further information. Thank you very much for your assistance.
Instead of, for instance,
a < wrap(b-c)
use
0 < wrap(b-c-a)
or
0 > wrap(a-b+c)
This makes it a little less readable, but is the correct way to compare (supposedly small) angle differences.

Implement bouncing balls collision detection

I have a bouncing ball application and I have to extend it to prevent overlapping of the balls.
When ball overlaps another, they should move away as in real life.
I have to extend the given MoveBall method:
private void MoveBall()
{
prevX = x;
prevY = y;
x += xVelocity;
y += yVelocity;
// Is there too closed ball?
foreach (Ball ball in parentForm.balls)
{
distance = Math.Sqrt(Math.Pow((double)(ball.prevX - prevX), 2) +
Math.Pow((double)(ball.prevY- prevY), 2));
overlap = ((radius + ball.radius) - distance);// +ball.radius;
if (ball.id != this.id &&
ball.id != lastID &&
overlap > 0)
{
lastID = this.id;
if (xVelocity > 0) // roading right
{
xVelocity = -xVelocity;
x -= xVelocity - ball.xVelocity;
}
else if (xVelocity <= 0) // roading left
{
xVelocity = -xVelocity;
x += xVelocity + ball.xVelocity;
}
if (yVelocity > 0)
{ // going up
yVelocity = -yVelocity;
y -= yVelocity - ball.yVelocity;
}
else if (yVelocity <= 0) // down
{
yVelocity = -yVelocity;
y += yVelocity + ball.yVelocity;
}
}
}
// ***********************************************
// ***************** END MY CODE *****************
if (x > parentForm.Width - 10 - (radius) || x < 0)
{
if (x < 0) x = 0;
if (x > parentForm.Width - 10) x = parentForm.Width - 10 - radius;
xVelocity = -xVelocity;
}
if (y > parentForm.Height - 40 - (radius) || y < 0)
{
if (y < 0) y = 0;
if (y > parentForm.Height - 40) y = parentForm.Height - 40 - (radius);
yVelocity = -yVelocity;
}
}
x,y, xVelocity, yVelocity, radius, prevX, prevY declared as int.
overlap, distance as double.
When 2 overlap, they are getting stuck. Why?
Unfortunately, I can't upload all source code because there are lot of modules.
I'm using Visual C# Express 2010.
As no Question is asked explicitly, I will assume the question "Why are the balls sticking together?"
You have only shown one loop in source code, that's not enough ;-) To check all possible collisions, you need to check n*(n-1)/2 possible collisions. That is normally done with two loops. You have to put in careful measures to avoid handling the same collision twice.
The reason that your balls get stuck is that you handle the same collision multiple times. For example two balls colliding exactly horizontal: The left one has velocity 5 and x-position of 100. The other one shall have a position of 110 and velocity of -6. When the collision happens:
x is set to 105.
Collision detected: x is set to 104 and velocity to -5.
The other Ball handles the same collision:
He moves according to his velocity to position 104.
Collision handling: His velocity becomes 6 and position becomes 105.
The balls were at 100 and 110 resp. and have been moved to 104 and 105. While the velocities are now pointing away from each other, the collision handling in the following step will invert them again. So the positions are close together and the velocities are changing sign every frame. The balls seem "stuck to each other".
I hope the answer helps you to understand your problem. For a better implementation of an elastic collision (that handles each collision exactly once) look here: Ball to Ball Collision - Detection and Handling
Having stumbled upon similar issues when I made my first attempts at collision detection algorithms, I'll try to describe what I think is the problem here.
Maybe the balls move fast enough so that, before collision is even detected by your code, they are already partially "inside" each other. When collision detection comes and notices that, it does what it's supposed to do: change the planned trajectories of the objects according to the details of the collision that just happened. The problem is that, because these objects got sort-of "merged" before collision detection caught them, they can't get unstuck because collision detection is fired again, trapping them with each other.
If this is the source of the problem, then maybe the above code would work with a small enough velocity vector. Of course, that's not a real solution, but if it does work for very small velocities, it probably confirms my hypothesis and you have some idea regarding how to proceed.

Obtaining perfect sprite rotation in XNA

I am encountering an issue when updating a bullet's position when I rotate the ship that shoots it. Currently the code almost works except that at most angles the bullet is not fired from where i want it to. Sometimes it fires slightly upward than the centre where it should be, and sometimes downwards. I'm pretty sure it has something to do with the trigonometry but I'm having a hard time finding the problem.
This code is in the constructor; it sets the bullet's initial position and rotation based on the ship's position (sp.position) and its rotation (Controls.rotation):
this.backgroundRect = new RotatedRectangle(Rectangle.Empty, Controls.rotation);
//get centre of ship
this.position = new Vector2(sp.getPosition().X + sp.getTexture().Width/2,
(sp.getPosition().Y + sp.getTexture().Height / 2));
//get blast pos
this.position = new Vector2((float)(this.position.X + (sp.getTexture().Width / 2 *
Math.Cos(this.backgroundRect.Rotation))), (float)(this.position.Y +
(sp.getTexture().Width / 2 * Math.Sin(this.backgroundRect.Rotation))));
//get blast pos + texture height / 2
this.position = new Vector2((float)(this.position.X + (this.texture.Height / 2 *
Math.Cos(this.backgroundRect.Rotation))), (float)(this.position.Y +
(this.texture.Height / 2 * Math.Sin(this.backgroundRect.Rotation))));
this.backgroundRect = new RotatedRectangle(new Rectangle((int)this.position.X,
(int)this.position.Y, this.texture.Width, this.texture.Height), Controls.rotation);
speed = defSpeed;
Then in the update method I use the following code; I'm pretty sure this is working fine though:
this.position.X = this.position.X + defSpeed *
(float)Math.Cos(this.getRectangle().Rotation);
this.position.Y = this.position.Y + defSpeed *
(float)Math.Sin(this.getRectangle().Rotation);
this.getRectangle().CollisionRectangle.X = (int)(position.X + defSpeed *
(float)Math.Cos(this.getRectangle().Rotation));
this.getRectangle().CollisionRectangle.Y = (int)(position.Y + defSpeed *
(float)Math.Sin(this.getRectangle().Rotation));
Also I should mention that my ship had not been working correctly when I rotated it since the origin (center of rotation) was (0,0). To remedy this I changed the origin to the center of the ship (width/2,height/2) and also added those values to the drawing rectangle so that the sprite would draw correctly. I imagine this could be the problem and suppose there's a better way of going around this. The game is in 2D by the way.
The problem is that the sprites are not drawn on the positions they are supposed to be. When calculating the center position, you assume that the sprite is not rotated. This causes some trouble. Furthermore, rotating an arbitrary vector is not as easy as you did. You can only rotate a vector by multiplying its components with sin/cos when it is on the x-axis.
Here is how you can rotate arbitrary vectors:
Vector2 vecRotate(Vector2 vec, double phi)
{
double length = vec.Length(); //Save length of vector
vec.Normalize();
double alpha = Math.Acos(vec.X); //Angle of vector against x-axis
if(vec.Y < 0)
alpha = 2 * Math.PI - alpha
alpha += phi;
vec.X = Math.Cos(alpha) * length;
vec.Y = Math.Sin(alpha) * length;
return vec;
}
Make sure, the angle is in radians.
With that you can calculate the correct position of the ship:
Vector2 ShipCenter = sp.Position() + vecRotate(new Vector2(sp.getTexture().Width/2, sp.getTexture().Height/2), Controls.Rotation);
You can use the same function to determine the position of the bullet's center. I don't exactly know, where you want to place the bullet. I will assume, it is at the center of the right edge:
Vector2 offset = new Vector2(sp.getTexture.Width/2, 0);
this.position = ShipCenter + vecRotate(offset, Controls.Rotation);
If you then need the position of the upper left corner of the bullet, you can go further:
this.position -= vecRotate(new Vector2(this.texture.Width/2, this.texture.Height/2), Controls.Rotation)
However, as I stated, it is probably easier to handle the ship's and bullets' positions as central positions. Doing so, you need far less trigonometry.

Categories