Added a bunch of checks to make sure a normalized zero vector (= NaN, NaN) is not used in any position/velocity/movement calculations. There were at least three places where it was causing problems according to error reports: when a character that can't enter a sub spawns at the center of a hull and when using an underwater scooter or throwing something while the cursor is at the position of the character, but there were tons of other places as well where it may have potentially caused physics errors.

This commit is contained in:
Joonas Rikkonen
2018-07-31 12:28:04 +03:00
parent d881a7060b
commit d81ee1a27e
13 changed files with 56 additions and 29 deletions
@@ -280,7 +280,9 @@ namespace Barotrauma
return; return;
} }
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(SimPosition - selectedAiTarget.SimPosition) * 5); Vector2 escapeDir = Vector2.Normalize(SimPosition - selectedAiTarget.SimPosition);
if (!MathUtils.IsValid(escapeDir)) escapeDir = Vector2.UnitY;
SteeringManager.SteeringManual(deltaTime, escapeDir * 5);
SteeringManager.SteeringWander(1.0f); SteeringManager.SteeringWander(1.0f);
SteeringManager.SteeringAvoid(deltaTime, 2f); SteeringManager.SteeringAvoid(deltaTime, 2f);
} }
@@ -490,8 +492,10 @@ namespace Barotrauma
if (dist < ConvertUnits.ToSimUnits(500.0f)) if (dist < ConvertUnits.ToSimUnits(500.0f))
{ {
Vector2 attackDir = Vector2.Normalize(Character.SimPosition - attackPosition);
if (!MathUtils.IsValid(attackDir)) attackDir = Vector2.UnitY;
steeringManager.SteeringSeek(attackPosition, -0.8f); steeringManager.SteeringSeek(attackPosition, -0.8f);
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(Character.SimPosition - attackPosition) * (1.0f - (dist / 500.0f))); steeringManager.SteeringManual(deltaTime, attackDir * (1.0f - (dist / 500.0f)));
} }
steeringManager.SteeringAvoid(deltaTime, 1.0f); steeringManager.SteeringAvoid(deltaTime, 1.0f);
@@ -63,6 +63,7 @@ namespace Barotrauma
character.SetInput(InputType.Aim, false, true); character.SetInput(InputType.Aim, false, true);
Vector2 enemyDiff = Vector2.Normalize(enemy.Position - character.Position); Vector2 enemyDiff = Vector2.Normalize(enemy.Position - character.Position);
if (!MathUtils.IsValid(enemyDiff)) enemyDiff = Rand.Vector(1.0f);
float weaponAngle = ((weapon.body.Dir == 1.0f) ? weapon.body.Rotation : weapon.body.Rotation - MathHelper.Pi); float weaponAngle = ((weapon.body.Dir == 1.0f) ? weapon.body.Rotation : weapon.body.Rotation - MathHelper.Pi);
Vector2 weaponDir = new Vector2((float)Math.Cos(weaponAngle), (float)Math.Sin(weaponAngle)); Vector2 weaponDir = new Vector2((float)Math.Cos(weaponAngle), (float)Math.Sin(weaponAngle));
@@ -80,9 +80,9 @@ namespace Barotrauma
} }
float steeringSpeed = steering.Length(); float steeringSpeed = steering.Length();
if (steeringSpeed>speed) if (steeringSpeed > speed)
{ {
steering = Vector2.Normalize(steering) * Math.Abs(speed); steering = Vector2.Normalize(steering) * Math.Abs(speed);
} }
host.Steering = steering; host.Steering = steering;
@@ -97,12 +97,12 @@ namespace Barotrauma
targetVel = Vector2.Normalize(targetVel) * speed; targetVel = Vector2.Normalize(targetVel) * speed;
Vector2 newSteering = targetVel - host.Steering; Vector2 newSteering = targetVel - host.Steering;
if (newSteering==Vector2.Zero) return Vector2.Zero; if (newSteering == Vector2.Zero) return Vector2.Zero;
float steeringSpeed = (newSteering + host.Steering).Length(); float steeringSpeed = (newSteering + host.Steering).Length();
if (steeringSpeed > Math.Abs(speed)) if (steeringSpeed > Math.Abs(speed))
{ {
newSteering = Vector2.Normalize(newSteering)*Math.Abs(speed); newSteering = Vector2.Normalize(newSteering) * Math.Abs(speed);
} }
return newSteering; return newSteering;
@@ -151,8 +151,7 @@ namespace Barotrauma
} }
else else
{ {
Structure closestStructure = closestBody.UserData as Structure; if (closestBody.UserData is Structure closestStructure)
if (closestStructure != null)
{ {
Vector2 obstaclePosition = Submarine.LastPickedPosition; Vector2 obstaclePosition = Submarine.LastPickedPosition;
if (closestStructure.IsHorizontal) if (closestStructure.IsHorizontal)
@@ -166,15 +165,17 @@ namespace Barotrauma
avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - obstaclePosition); avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - obstaclePosition);
} }
else if (closestBody.UserData is Item) else if (closestBody.UserData is Item item)
{ {
Item item = (Item)closestBody.UserData;
avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - item.SimPosition); avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - item.SimPosition);
} }
else else
{ {
avoidSteering = Vector2.Normalize(host.SimPosition - Submarine.LastPickedPosition); avoidSteering = Vector2.Normalize(host.SimPosition - Submarine.LastPickedPosition);
} }
//failed to normalize (the obstacle to avoid is at the same position as the character?)
// -> move to a random direction
if (!MathUtils.IsValid(avoidSteering)) avoidSteering = Rand.Vector(1.0f);
} }
} }
@@ -766,10 +766,13 @@ namespace Barotrauma
//this is preferable to the cost of using continuous collision detection for the character collider //this is preferable to the cost of using continuous collision detection for the character collider
if (newHull != null) if (newHull != null)
{ {
Vector2 hullDiff = WorldPosition - newHull.WorldPosition;
Vector2 moveDir = hullDiff.LengthSquared() < 0.001f ? Vector2.UnitY : Vector2.Normalize(hullDiff);
//find a position 32 units away from the hull //find a position 32 units away from the hull
Vector2? intersection = MathUtils.GetLineRectangleIntersection( Vector2? intersection = MathUtils.GetLineRectangleIntersection(
newHull.WorldPosition, newHull.WorldPosition,
newHull.WorldPosition + Vector2.Normalize(WorldPosition - newHull.WorldPosition) * Math.Max(newHull.Rect.Width, newHull.Rect.Height), newHull.WorldPosition + moveDir * Math.Max(newHull.Rect.Width, newHull.Rect.Height),
new Rectangle(newHull.WorldRect.X - 32, newHull.WorldRect.Y + 32, newHull.WorldRect.Width + 64, newHull.Rect.Height + 64)); new Rectangle(newHull.WorldRect.X - 32, newHull.WorldRect.Y + 32, newHull.WorldRect.Width + 64, newHull.Rect.Height + 64));
if (intersection != null) if (intersection != null)
@@ -1494,7 +1497,7 @@ namespace Barotrauma
Vector2 force = Vector2.Zero; Vector2 force = Vector2.Zero;
foreach (Gap gap in Gap.GapList) foreach (Gap gap in Gap.GapList)
{ {
if (gap.Open <= 0.0f || gap.FlowTargetHull != currentHull || gap.LerpedFlowForce == Vector2.Zero) continue; if (gap.Open <= 0.0f || gap.FlowTargetHull != currentHull || gap.LerpedFlowForce.LengthSquared() < 0.01f) continue;
Vector2 gapPos = gap.SimPosition; Vector2 gapPos = gap.SimPosition;
float dist = Vector2.Distance(limbPos, gapPos); float dist = Vector2.Distance(limbPos, gapPos);
@@ -74,6 +74,8 @@ namespace Barotrauma.Items.Components
} }
Vector2 dir = Vector2.Normalize(character.CursorPosition - character.Position); Vector2 dir = Vector2.Normalize(character.CursorPosition - character.Position);
//move upwards if the cursor is at the position of the character
if (!MathUtils.IsValid(dir)) dir = Vector2.UnitY;
Vector2 propulsion = dir * force; Vector2 propulsion = dir * force;
@@ -94,8 +94,9 @@ namespace Barotrauma.Items.Components
if (throwPos < -0.0) if (throwPos < -0.0)
{ {
Vector2 throwVector = picker.CursorWorldPosition - picker.WorldPosition; Vector2 throwVector = Vector2.Normalize(picker.CursorWorldPosition - picker.WorldPosition);
throwVector = Vector2.Normalize(throwVector); //throw upwards if cursor is at the position of the character
if (!MathUtils.IsValid(throwVector)) throwVector = Vector2.UnitY;
GameServer.Log(picker.LogName + " threw " + item.Name, ServerLog.MessageType.ItemInteraction); GameServer.Log(picker.LogName + " threw " + item.Name, ServerLog.MessageType.ItemInteraction);
@@ -116,7 +116,7 @@ namespace Barotrauma.Items.Components
else else
{ {
diff.Y = 0.0f; diff.Y = 0.0f;
if (diff != Vector2.Zero && diff.Length() > 10.0f) if (diff != Vector2.Zero && diff.LengthSquared() > 10.0f * 10.0f)
{ {
character.AnimController.TargetMovement = Vector2.Normalize(diff); character.AnimController.TargetMovement = Vector2.Normalize(diff);
character.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left; character.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
@@ -211,8 +211,9 @@ namespace Barotrauma.Items.Components
{ {
Vector2 diff = item.Submarine.WorldPosition - (Vector2)intersection; Vector2 diff = item.Submarine.WorldPosition - (Vector2)intersection;
//far enough -> ignore float dist = diff.Length();
if (diff.Length() > avoidRadius) continue; //far enough or too close to normalize the diff -> ignore
if (dist > avoidRadius || dist < 0.00001f) continue;
float dot = item.Submarine.Velocity == Vector2.Zero ? float dot = item.Submarine.Velocity == Vector2.Zero ?
0.0f : Vector2.Dot(item.Submarine.Velocity, -Vector2.Normalize(diff)); 0.0f : Vector2.Dot(item.Submarine.Velocity, -Vector2.Normalize(diff));
@@ -241,16 +242,15 @@ namespace Barotrauma.Items.Components
float otherSize = Math.Max(sub.Borders.Width, sub.Borders.Height); float otherSize = Math.Max(sub.Borders.Width, sub.Borders.Height);
Vector2 diff = item.Submarine.WorldPosition - sub.WorldPosition; Vector2 diff = item.Submarine.WorldPosition - sub.WorldPosition;
float dist = diff == Vector2.Zero ? 0.0f : diff.Length(); float dist = diff == Vector2.Zero ? 0.0f : diff.Length();
//far enough -> ignore //far enough -> ignore
if (dist > thisSize + otherSize) continue; if (dist > thisSize + otherSize) continue;
diff = Vector2.Normalize(diff); Vector2 dir = dist <= 0.0001f ? Vector2.UnitY : diff / dist;
float dot = item.Submarine.Velocity == Vector2.Zero ? float dot = item.Submarine.Velocity == Vector2.Zero ?
0.0f : Vector2.Dot(Vector2.Normalize(item.Submarine.Velocity), -Vector2.Normalize(diff)); 0.0f : Vector2.Dot(Vector2.Normalize(item.Submarine.Velocity), -dir);
//heading away -> ignore //heading away -> ignore
if (dot < 0.0f) continue; if (dot < 0.0f) continue;
@@ -180,6 +180,7 @@ namespace Barotrauma
if (limb.WorldPosition != worldPosition && force > 0.0f) if (limb.WorldPosition != worldPosition && force > 0.0f)
{ {
Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition); Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
if (!MathUtils.IsValid(limbDiff)) limbDiff = Rand.Vector(1.0f);
Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius; Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
limb.body.ApplyLinearImpulse(limbDiff * distFactor * force, impulsePoint); limb.body.ApplyLinearImpulse(limbDiff * distFactor * force, impulsePoint);
} }
@@ -919,7 +919,7 @@ namespace Barotrauma
ConvertUnits.ToSimUnits(endPos), ConvertUnits.ToSimUnits(endPos),
null, Physics.CollisionLevel) != null) null, Physics.CollisionLevel) != null)
{ {
position = ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.Normalize(startPos - endPos)*offsetFromWall; position = ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.Normalize(startPos - endPos) * offsetFromWall;
break; break;
} }
@@ -214,7 +214,7 @@ namespace Barotrauma
closestSub = Character.Controlled.Submarine; closestSub = Character.Controlled.Submarine;
} }
bool displace = moveAmount.Length() > 100.0f; bool displace = moveAmount.LengthSquared() > 100.0f * 100.0f;
foreach (Submarine sub in subsToMove) foreach (Submarine sub in subsToMove)
{ {
sub.PhysicsBody.SetTransform(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f); sub.PhysicsBody.SetTransform(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f);
@@ -259,7 +259,7 @@ namespace Barotrauma
Vector2 totalForce = CalculateBuoyancy(); Vector2 totalForce = CalculateBuoyancy();
if (Body.LinearVelocity.LengthSquared() > 0.000001f) if (Body.LinearVelocity.LengthSquared() > 0.0001f)
{ {
float dragCoefficient = 0.01f; float dragCoefficient = 0.01f;
@@ -285,6 +285,7 @@ namespace Barotrauma
worldBorders.Location += MathUtils.ToPoint(ConvertUnits.ToDisplayUnits(Body.SimPosition)); worldBorders.Location += MathUtils.ToPoint(ConvertUnits.ToDisplayUnits(Body.SimPosition));
Vector2 translateDir = Vector2.Normalize(subTranslation); Vector2 translateDir = Vector2.Normalize(subTranslation);
if (!MathUtils.IsValid(translateDir)) translateDir = Vector2.UnitY;
foreach (Character c in Character.CharacterList) foreach (Character c in Character.CharacterList)
{ {
@@ -384,7 +385,9 @@ namespace Barotrauma
if (f2.UserData is VoronoiCell cell) if (f2.UserData is VoronoiCell cell)
{ {
HandleLevelCollision(contact, Vector2.Normalize(ConvertUnits.ToDisplayUnits(Body.SimPosition) - cell.Center)); Vector2 collisionNormal = Vector2.Normalize(ConvertUnits.ToDisplayUnits(Body.SimPosition) - cell.Center);
if (!MathUtils.IsValid(collisionNormal)) collisionNormal = Rand.Vector(1.0f);
HandleLevelCollision(contact, collisionNormal);
return true; return true;
} }
@@ -445,7 +448,9 @@ namespace Barotrauma
{ {
if (limb.Mass > 100.0f) if (limb.Mass > 100.0f)
{ {
Vector2 normal = Vector2.Normalize(Body.SimPosition - limb.SimPosition); Vector2 normal = Vector2.DistanceSquared(Body.SimPosition, limb.SimPosition) < 0.0001f ?
Vector2.UnitY :
Vector2.Normalize(Body.SimPosition - limb.SimPosition);
float impact = Math.Min(Vector2.Dot(Velocity - limb.LinearVelocity, -normal), 50.0f) / 5.0f * Math.Min(limb.Mass / 200.0f, 1); float impact = Math.Min(Vector2.Dot(Velocity - limb.LinearVelocity, -normal), 50.0f) / 5.0f * Math.Min(limb.Mass / 200.0f, 1);
@@ -634,7 +639,10 @@ namespace Barotrauma
float contactDot = Vector2.Dot(otherSub.PhysicsBody.LinearVelocity, -avgContactNormal); float contactDot = Vector2.Dot(otherSub.PhysicsBody.LinearVelocity, -avgContactNormal);
if (contactDot > 0.0f) if (contactDot > 0.0f)
{ {
otherSub.PhysicsBody.LinearVelocity -= Vector2.Normalize(otherSub.PhysicsBody.LinearVelocity) * contactDot; if (otherSub.PhysicsBody.LinearVelocity.LengthSquared() > 0.0001f)
{
otherSub.PhysicsBody.LinearVelocity -= Vector2.Normalize(otherSub.PhysicsBody.LinearVelocity) * contactDot;
}
impact = Vector2.Dot(otherSub.Velocity, normal); impact = Vector2.Dot(otherSub.Velocity, normal);
otherSub.SubBody.ApplyImpact(impact, normal, contact); otherSub.SubBody.ApplyImpact(impact, normal, contact);
@@ -89,7 +89,9 @@ namespace Barotrauma
foreach (Submarine sub in subs) foreach (Submarine sub in subs)
{ {
if (sub.Position == targetPos) continue; if (sub.Position == targetPos) continue;
sub.ApplyForce((Vector2.Normalize(targetPos - sub.Position) * targetSpeed - sub.Velocity) * 500.0f); Vector2 dir = Vector2.Normalize(targetPos - sub.Position);
if (!MathUtils.IsValid(dir)) continue;
sub.ApplyForce((dir * targetSpeed - sub.Velocity) * 500.0f);
} }
timer += CoroutineManager.UnscaledDeltaTime; timer += CoroutineManager.UnscaledDeltaTime;
@@ -362,8 +362,12 @@ namespace Barotrauma.Networking
while (Math.Abs(position.Y - respawnShuttle.WorldPosition.Y) > 100.0f) while (Math.Abs(position.Y - respawnShuttle.WorldPosition.Y) > 100.0f)
{ {
Vector2 displayVel = Vector2.Normalize(position - respawnShuttle.WorldPosition) * speed; Vector2 diff = position - respawnShuttle.WorldPosition;
respawnShuttle.SubBody.Body.LinearVelocity = ConvertUnits.ToSimUnits(displayVel); if (diff.LengthSquared() > 0.01f)
{
Vector2 displayVel = Vector2.Normalize(diff) * speed;
respawnShuttle.SubBody.Body.LinearVelocity = ConvertUnits.ToSimUnits(displayVel);
}
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
if (respawnShuttle.SubBody == null) yield return CoroutineStatus.Success; if (respawnShuttle.SubBody == null) yield return CoroutineStatus.Success;