C# Collision Programming - Momentum - c#

I'm programming a 3D XNA game and I'm struggling to understand how to implement a momentum-like collision between my two players. Basically the concept is for either player to hit the other and attempt to knock the opponent off the level (Like the Smash Bros games) to score a point.
//Declared Variables
PrimitiveObject player1body;
PrimitiveObject player2body;
Vector3 player1vel = Vector3.Zero;
Vector3 player2vel = Vector3.Zero;
//Created in LoadContent()
PrimitiveSphere player1 = new PrimitiveSphere(tgd, 70, 32);
this.player1vel = new Vector3(0, 135, -120);
this.player1body = new PrimitiveObject(player1);
this.player1body.Translation = player1vel;
this.player1body.Colour = Color.Blue;
PrimitiveSphere player2 = new PrimitiveSphere(tgd, 70, 32);
this.player2vel = new Vector3(0, 135, 120);
this.player2body = new PrimitiveObject(player2);
this.player2body.Translation = player2vel;
this.player2body.Colour = Color.Red;
//Update method code for collision
this.player1vel.X = 0;
this.player1vel.Y = 0;
this.player1vel.Z = 0;
this.player2vel.X = 0;
this.player2vel.Y = 0;
this.player2vel.Z = 0;
if (player1body.BoundingSphere.Intersects(player2body.BoundingSphere))
{
this.player2vel.Z += 10;
}
As you can see all I'm doing is checking for Player1's bounding sphere and when it intersects with Player 2's then player 2 will be pushed back on the Z axis, now obviously this just wouldn't work and isn't very intuitive and is where my problem lies as I'm buggered trying to work out how to come up with a solution, what I want happening is when either player collides with the other they basically swap vectors giving it that bounce effect I'm looking for and not just affecting one axis but both the X & Z.
Thanks for taking the time to read and I'll be grateful for any solutions that anyone can think of.
Notes:
PrimitiveSphere if you're wondering is using (graphics device, diameter of the sphere, tesselation).

Basically, in a collision like the one you are trying to do, you want an elastic collision, in which both the kinetic energy and momentum are conserved. All momentum (P) is is mass * velocity, and Kinetic Energy (K) is 1/2*mass*velocity^2. In your situtation, I'm assuming everything has the same mass. If so, K = 1/2 * v^2. So, Kf = Ki and Pf = Pi. Using the kinetic energy, the velocity's magnitudes of the players are swapped. And as long as the collisions are head on (which based on your code I assume you're fine with), the players will swap directions.
So you could do something as simple as this:
if (player1body.BoundingSphere.Intersects(player2body.BoundingSphere))
{
Vector3 p1v = this.player1vel;
this.player1vel = this.player2vel;
this.player2vel = p1v;
}
This should create a fairly realistic collision. Now the reason I included that info about P and K is so that if you want non-head on collisions or different masses, you should be able to incorporate that. If the masses aren't the same, the velocity's won't simply change magnitudes and directions. There will be a lot more math involved. I hope this helps.

Related

Why does SignedAngle work properly with transform.position but not with transform.localPosition?

I'm working on a VR motorcycle game and trying to get the steering to work by the player grabbing and rotating the handles. The steering works because of the function CalculateRotation() (in the code block below) which gets the angle between the hands and rotates the handlebars/the bike based on that.
When transform.position is used for the controller positions it works smoothly (the handlebars smoothly rotate to the controller positions), but when transform.localPosition is used it freaks out (the handlebars rotate away from the controller positions as far as they can - 50degrees because the maxRotation variable clamps the rotation to 50degrees) so you can't actually grab onto the handlebars.
I would just use position but when the bike starts moving it can't calculate the angle properly and the handlebars start acting like they do with localPosition.
I either need to figure out why localPosition doesn't work or figure out how to make position work when the bike is moving. But I would like to have some insight on why localPosition isn't working since if I can figure out why it's an issue I can likely solve it.
Some more info:
I tried using transform.TransformDirection/TransformPoint/TransformVector & the inverses but they didn't seem to help. Also the handlebars and the controllers are children of different gameobjects so I think that may be a part of why the localPosition doesn't work but I'm not sure. Any suggestions would be appreciated.
Here's a link of it working with position then not working with localPosition
private Quaternion CalculateRotation()
{
//hand xz + source y (to stay consistent)
//use these positions to figure out do the math for rotation
//USE POSITION NOT LOCALPOSITION FOR IT TO WORK
/*leftControllerPos = leftController.transform.InverseTransformDirection(leftController.transform.position);
rightControllerPos = rightController.transform.InverseTransformDirection(rightController.transform.position);
leftSubPos = leftSub.transform.InverseTransformDirection(leftSub.transform.position);
rightSubPos = rightSub.transform.InverseTransformDirection(rightSub.transform.position);*/
leftControllerPos = leftController.transform.position;
rightControllerPos = rightController.transform.position;
leftSubPos = leftSub.transform.position;
rightSubPos = rightSub.transform.position;
//check which hands are detected to get targetDir
Vector3 firstPos;
Vector3 secondPos;
if (detectedHands == DetectedHands.BothHands)
{
Debug.Log("BothHands");
firstPos = leftControllerPos;
secondPos = rightControllerPos;
Debug.Log("firstPos = " + firstPos + "\nsecondPos = " + secondPos);
}
else if (detectedHands == DetectedHands.LeftHandOnly)
{
Debug.Log("LeftHandOnly");
firstPos = leftControllerPos;
secondPos = rightSubPos;
}
else if (detectedHands == DetectedHands.RightHandOnly)
{
Debug.Log("RightHandOnly");
firstPos = leftSubPos;
secondPos = rightControllerPos;
Debug.Log("firstPos = " + firstPos + "\nsecondPos = " + secondPos);
}
else
{
Debug.Log("None");
firstPos = leftControllerPos;
secondPos = rightControllerPos;
}
//do math with correct targetDir
Vector3 targetDir = secondPos - firstPos;
Vector3 right = transform.right;
float angle = Vector3.SignedAngle(targetDir, right, Vector3.up);
angle = Mathf.Clamp(angle, -maxRotation, +maxRotation);
AdjustedAngle = -angle;
//visualizeDirection.transform.localRotation = Quaternion.Euler(new Vector3(0, -angle, 0));
//Quaternion bikeRotation = FindObjectOfType<BikeController>().transform.localRotation;
Quaternion firstRot = Quaternion.Euler(new Vector3(-24, 0, 0));
Quaternion secondRot = Quaternion.Euler(new Vector3(0, AdjustedAngle, 0));
Quaternion adjustedRotation = firstRot * secondRot; //bikeRotation *
Debug.Log("adjustedRotation = " + adjustedRotation);
return adjustedRotation;
}
I think instead of InverseTransformDirection you would rather want to use InverseTransformPoint!
From the API
You should use Transform.InverseTransformPoint if the vector represents a position in space rather than a direction.
You are currently treating some world space positions as if they were directions - this is only valid for the world origin 0,0,0 ;)
Further it makes no sense to convert each individual object position into it's own local space ... the result would always be 0,0,0!
If you want to calculate in local space of an object it would always need to be the same objects local space!
So since you later compare to this object's transform.right probably
leftControllerPos = transform.InverseTransformPoint(leftController.transform.position);
rightControllerPos = transform.InverseTransformPoint(rightController.transform.position);
leftSubPos = transform.InverseTransformPoint(leftSub.transform.position);
rightSubPos = transform.InverseTransformPoint(rightSub.transform.position);
Then later when you come to the SignedAngle you would also have to use accordingly Vector3.right instead of the world space vector transform.right in order to use local space.
Facit: If world space positions works then why bother around with local space positions?

Unity 3D Make Objects Follow Player

I am trying to make objects follow player with offset without delay on Z-axis but delayed on X-axis. When player entered a object(coin) trigger, i am adding this object to a list and i am updating each one’s position using this code. Each coin has previous and next coin that current coin follows.
{
foreach (Coin coin in coins)
{
Vector3 desiredPos = new Vector3(
coin.previous.transform.position.x,
coin.previous.transform.position.y,
coin.previous.transform.position.z + .15f);
Vector3 smoothedPos = Vector3.Lerp(
coin.transform.position,
desiredPos,
smoothness);
coin.transform.position = smoothedPos;
}
}
I want to get this result
https://youtube.com/shorts/PHAg8Pqf0mA?feature=share
This is what i got
https://youtube.com/shorts/fzukzL_0r74?feature=share
But as you can see coins struggling some times. It is not updating their positions properly. I dont want to make it child because i need to move them on x-axis with delay.
I’ll be appreciated if you can help me.
You are using Vector3.Lerp which lerps all x y and z. If you want to lerp only on x axis, you should use Mathf.Lerp on the x instead:
var x = Mathf.Lerp(x, desiredPos.x, smoothness);

Terrain refresh lag unity c# - How effectively refresh the terrain?

In the game, players are able to chop down trees. I then instantiate a falling tree in it's spot.
I remove the tree from the terrain list and refresh the terrain like so:
var treeInstancesToRemove = new List<TreeInstance>(terrain.treeInstances);
treeInstancesToRemove.RemoveAt(closestTreeIndex);
terrain.treeInstances = treeInstancesToRemove.ToArray();
// I refresh the terrain so the collider gets removed...
float[,] heights = terrain.GetHeights(0, 0, 0, 0);
terrain.SetHeights(0, 0, heights);
The terrain is VERY LARGE... This means that whenever a tree is chopped the game freezes for a few seconds and then resumes (as it refreshes). Is there a faster or more effective way I could take a look at? Having a freeze after every tree you chop down is not quite ideal?
THANKS A LOT IN ADVANCE!
The best thing I can suggest is having the world split into chunks that you can update separately. Either that, or have the collider update in a separate thread from the main one.
float hmWidth = grav.currentTerrain.terrainData.heightmapWidth;
float hmHeight = grav.currentTerrain.terrainData.heightmapHeight;
// get the normalized position of this game object relative to the terrain
Vector3 tempCoord = (transform.position - grav.currentTerrain.gameObject.transform.position);
Vector3 coord;
coord.x = tempCoord.x / grav.currentTerrain.terrainData.size.x;
coord.y = tempCoord.y / grav.currentTerrain.terrainData.size.y;
coord.z = tempCoord.z / grav.currentTerrain.terrainData.size.z;
// get the position of the terrain heightmap where this game object is
int posXInTerrain = (int)(coord.x * hmWidth);
int posYInTerrain = (int)(coord.z * hmHeight);
// we set an offset so that all the raising terrain is under this game object
//int offset = size / 2;
// get the heights of the terrain under this game object
float[,] heights = grav.currentTerrain.terrainData.GetHeights(posXInTerrain, posYInTerrain, 1, 1);
grav.currentTerrain.terrainData.SetHeights(posXInTerrain, posYInTerrain, heights); //THIS CHANGES TERRAIN FOR GOOD

Slight offset in time and distance

I am building a teaching platform for teaching basic Physics.
From my experience in Flash development, I have met similar problems before.
The game time is not the same as real world time. In which case, for example, the distance covered by a projectile can be larger or smaller if the computer lags or for whatever reasons.
Here is a screenshot of the said platform. As shown in the screenshot, I am only developing a lesson for teaching the basic s = v * t relation.
Necessary background
The red line marks the position 69.06284 m, our target distance. The time 11.56087 s is a given value.
The user is supposed to input a speed, moving a projectile from 0 m to the right in order to reach the red line within the time given.
The green line marks the position of the projectile when time is up.
I can assure you that I input an accurate speed up to 5 decimal points so there is no human error in this case.
Ignore the yellow rectangle. It is just a UI element.
The screen is 800 pixels wide, and therefore 10 pixels represent 1 meter.
Way to solve the problem
I'm not sure what to do, quite frankly. But I heard from someone that variable time step represents real world time better. However, Farseer, being a physics simulation engine, should be used with fixed time step, isn't it?
Any advice will be greatly appreciated.
EDIT: here in the screenshot, the actual distance covered by the projectile is ~66.3 m, whereas the theoretical distance is 69.1 m. I also notice that if the target distance (currently 69.1 m) is smaller (red line moves a lot more to the left), the error is smaller.
How do I fire a projectile?
public override void ShootProjectile(Vector2 start, float angle, float speed) {
GameTemplate g = Game as GameTemplate;
Arrow a = new Arrow(Game, start, this);
a.Initialize();
a.Restitution = 0f;
a.Friction = 0f;
a.LinearVelocity = new Vector2(speed, 0);
Console.WriteLine("Speed: "+a.LinearVelocity.X);
_fireTime = g.SinceGameStarts;
//Console.WriteLine("Fire time: "+_fireTime);
_projectiles.Add(a);
}
In the Arrow class, I basically set up Farseer through a CreateBody method:
protected override void CreateBody() {
GameTemplate g = Game as GameTemplate;
Vector2 positionInMeters = _initPos / g.MeterInPixels;
float width = 30f / g.MeterInPixels;
float height = 40f / g.MeterInPixels;
_body = BodyFactory.CreateRectangle(g.GameWorld.World, width, height, 1f, positionInMeters);
_body.BodyType = BodyType.Dynamic;
_origin = new Vector2(15f, 40f);
}
How do I calculate the flight time of projectile?
May be you are curious about the _fireTime = g.SinceGameStart line. SinceGameStart is a getter of the variable _currTime in a GameTemplate. The value of _currTime is updated once every Update(gameTime) call. So if I want to know how long the projectile has been flying, I do this:
time = _currTime - projectile.FireTime

Physics Simulation Ensuring Circles Do Not Overlap - C# XNA 4.0

Okay, so I am trying to simulate the collision of balls on a 2-Dimensional plane. I can detect the collisions pretty easily using a simple comparison of positions and the sum of radii, however, sometimes the simulation gets ahead of itself and the circles overlap, which plays havoc with the rest of the simulation.
So I have figured that finding the normal vector between the two circles at the point of contact and adding onto the position vectors in that direction is what I need to do basically, and luckily I had a similar algorithm handling the velocity changes due to collisions so I adapted it thusly:
Vector2 normal = orgA.getCenterPosition() - orgB.getCenterPosition();
Vector2 tangent = new Vector2((normal.Y * -1), normal.X);
float diff = (orgA.getRadius() + orgB.getRadius()) - normal.Length();
normal.Normalize();
float PAn = Vector2.Dot(normal, orgA.position);
float PAt = Vector2.Dot(tangent, orgA.position);
PAn += diff;
float PBn = Vector2.Dot(normal, orgB.position);
float PBt = Vector2.Dot(tangent, orgB.position);
PBn -= diff;
Vector2 PA = (PAn * normal) + (PAt * tangent);
Vector2 PB = (PBn * normal) + (PBt * tangent);
orgA.position = PA;
orgB.position = PB;
The trouble is that when I run the simulation, and two balls meet, the whole thing goes crazy and they're suddenly going all over the shop.
Can anyone see the flaw in my algorithm? I've looked at it loads and I still can't find what's causing this.
Hey buddy i think what you need is a loop. Its going crazy because once the balls touch they are constantly being upgraded with a new logic....
im not amazing at this but try putting the collision in a loop... should look something like this:
if ( diff < (orb radius))
{
Vector2 PA = (PAn * normal) + (PAt * tangent);
Vector2 PB = (PBn * normal) + (PBt * tangent);
orgA.position = PA;
orgB.position = PB;
}
something like that... I really hope this helps a little :/
from my understanding is this is in your update method, so keep in mind update runs constantly every millisecond... so its fine when your getting the difference between the spheres and sizes but after they collide and you you want them to move in a certain way you are calculating the same equation over and over...
Better yet make a bool such as isCollided and make sure you switch that true/false according to that statement
hope it helps i have an example project of collision if you want i can send it to you, samerhachem#hotmail.com

Categories

Resources