From d81ee1a27e53f2ec631d92755fae047dee9fe8d3 Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Tue, 31 Jul 2018 12:28:04 +0300 Subject: [PATCH] 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. --- .../Source/Characters/AI/EnemyAIController.cs | 8 ++++++-- .../AI/Objectives/AIObjectiveCombat.cs | 1 + .../Source/Characters/AI/SteeringManager.cs | 17 +++++++++-------- .../Source/Characters/Animation/Ragdoll.cs | 7 +++++-- .../Items/Components/Holdable/Propulsion.cs | 2 ++ .../Items/Components/Holdable/Throwable.cs | 5 +++-- .../Items/Components/Machines/Controller.cs | 2 +- .../Items/Components/Machines/Steering.cs | 10 +++++----- .../BarotraumaShared/Source/Map/Explosion.cs | 1 + .../Source/Map/Levels/Level.cs | 2 +- .../Source/Map/SubmarineBody.cs | 18 +++++++++++++----- .../Source/Map/TransitionCinematic.cs | 4 +++- .../Source/Networking/RespawnManager.cs | 8 ++++++-- 13 files changed, 56 insertions(+), 29 deletions(-) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs index 72b956137..ba7a0f353 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs @@ -280,7 +280,9 @@ namespace Barotrauma 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.SteeringAvoid(deltaTime, 2f); } @@ -490,8 +492,10 @@ namespace Barotrauma 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.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); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs index 84c807617..423a9ec82 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -63,6 +63,7 @@ namespace Barotrauma character.SetInput(InputType.Aim, false, true); 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); Vector2 weaponDir = new Vector2((float)Math.Cos(weaponAngle), (float)Math.Sin(weaponAngle)); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/SteeringManager.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/SteeringManager.cs index 910c8d8c2..e3d4aac22 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/SteeringManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/SteeringManager.cs @@ -80,9 +80,9 @@ namespace Barotrauma } 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; @@ -97,12 +97,12 @@ namespace Barotrauma targetVel = Vector2.Normalize(targetVel) * speed; 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(); if (steeringSpeed > Math.Abs(speed)) { - newSteering = Vector2.Normalize(newSteering)*Math.Abs(speed); + newSteering = Vector2.Normalize(newSteering) * Math.Abs(speed); } return newSteering; @@ -151,8 +151,7 @@ namespace Barotrauma } else { - Structure closestStructure = closestBody.UserData as Structure; - if (closestStructure != null) + if (closestBody.UserData is Structure closestStructure) { Vector2 obstaclePosition = Submarine.LastPickedPosition; if (closestStructure.IsHorizontal) @@ -166,15 +165,17 @@ namespace Barotrauma 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); } else { 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); } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs index d2f06b11a..fb5ed6df7 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/Ragdoll.cs @@ -766,10 +766,13 @@ namespace Barotrauma //this is preferable to the cost of using continuous collision detection for the character collider 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 Vector2? intersection = MathUtils.GetLineRectangleIntersection( 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)); if (intersection != null) @@ -1494,7 +1497,7 @@ namespace Barotrauma Vector2 force = Vector2.Zero; 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; float dist = Vector2.Distance(limbPos, gapPos); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Propulsion.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Propulsion.cs index 0d4079573..97a54afc4 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Propulsion.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Propulsion.cs @@ -74,6 +74,8 @@ namespace Barotrauma.Items.Components } 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; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs index 13c66dde4..4369bbf7a 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Holdable/Throwable.cs @@ -94,8 +94,9 @@ namespace Barotrauma.Items.Components if (throwPos < -0.0) { - Vector2 throwVector = picker.CursorWorldPosition - picker.WorldPosition; - throwVector = Vector2.Normalize(throwVector); + Vector2 throwVector = Vector2.Normalize(picker.CursorWorldPosition - picker.WorldPosition); + //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); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs index 9c7dd768c..d7256eaa9 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Controller.cs @@ -116,7 +116,7 @@ namespace Barotrauma.Items.Components else { 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.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs index 00326b53b..8f9b0e30b 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs @@ -211,8 +211,9 @@ namespace Barotrauma.Items.Components { Vector2 diff = item.Submarine.WorldPosition - (Vector2)intersection; - //far enough -> ignore - if (diff.Length() > avoidRadius) continue; + float dist = diff.Length(); + //far enough or too close to normalize the diff -> ignore + if (dist > avoidRadius || dist < 0.00001f) continue; float dot = item.Submarine.Velocity == Vector2.Zero ? 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); Vector2 diff = item.Submarine.WorldPosition - sub.WorldPosition; - float dist = diff == Vector2.Zero ? 0.0f : diff.Length(); //far enough -> ignore 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 ? - 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 if (dot < 0.0f) continue; diff --git a/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs b/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs index 121fe5417..3280a3f51 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Explosion.cs @@ -180,6 +180,7 @@ namespace Barotrauma if (limb.WorldPosition != worldPosition && force > 0.0f) { Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition); + if (!MathUtils.IsValid(limbDiff)) limbDiff = Rand.Vector(1.0f); Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius; limb.body.ApplyLinearImpulse(limbDiff * distFactor * force, impulsePoint); } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs index 7d7ce68e1..2304b34cd 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Levels/Level.cs @@ -919,7 +919,7 @@ namespace Barotrauma ConvertUnits.ToSimUnits(endPos), 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; } diff --git a/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs b/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs index 94663b760..22d147ad4 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs @@ -214,7 +214,7 @@ namespace Barotrauma closestSub = Character.Controlled.Submarine; } - bool displace = moveAmount.Length() > 100.0f; + bool displace = moveAmount.LengthSquared() > 100.0f * 100.0f; foreach (Submarine sub in subsToMove) { sub.PhysicsBody.SetTransform(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f); @@ -259,7 +259,7 @@ namespace Barotrauma Vector2 totalForce = CalculateBuoyancy(); - if (Body.LinearVelocity.LengthSquared() > 0.000001f) + if (Body.LinearVelocity.LengthSquared() > 0.0001f) { float dragCoefficient = 0.01f; @@ -285,6 +285,7 @@ namespace Barotrauma worldBorders.Location += MathUtils.ToPoint(ConvertUnits.ToDisplayUnits(Body.SimPosition)); Vector2 translateDir = Vector2.Normalize(subTranslation); + if (!MathUtils.IsValid(translateDir)) translateDir = Vector2.UnitY; foreach (Character c in Character.CharacterList) { @@ -384,7 +385,9 @@ namespace Barotrauma 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; } @@ -445,7 +448,9 @@ namespace Barotrauma { 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); @@ -634,7 +639,10 @@ namespace Barotrauma float contactDot = Vector2.Dot(otherSub.PhysicsBody.LinearVelocity, -avgContactNormal); 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); otherSub.SubBody.ApplyImpact(impact, normal, contact); diff --git a/Barotrauma/BarotraumaShared/Source/Map/TransitionCinematic.cs b/Barotrauma/BarotraumaShared/Source/Map/TransitionCinematic.cs index 60a9a8449..528862426 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/TransitionCinematic.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/TransitionCinematic.cs @@ -89,7 +89,9 @@ namespace Barotrauma foreach (Submarine sub in subs) { 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; diff --git a/Barotrauma/BarotraumaShared/Source/Networking/RespawnManager.cs b/Barotrauma/BarotraumaShared/Source/Networking/RespawnManager.cs index 3189a49e8..f5a526544 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/RespawnManager.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/RespawnManager.cs @@ -362,8 +362,12 @@ namespace Barotrauma.Networking while (Math.Abs(position.Y - respawnShuttle.WorldPosition.Y) > 100.0f) { - Vector2 displayVel = Vector2.Normalize(position - respawnShuttle.WorldPosition) * speed; - respawnShuttle.SubBody.Body.LinearVelocity = ConvertUnits.ToSimUnits(displayVel); + Vector2 diff = position - respawnShuttle.WorldPosition; + if (diff.LengthSquared() > 0.01f) + { + Vector2 displayVel = Vector2.Normalize(diff) * speed; + respawnShuttle.SubBody.Body.LinearVelocity = ConvertUnits.ToSimUnits(displayVel); + } yield return CoroutineStatus.Running; if (respawnShuttle.SubBody == null) yield return CoroutineStatus.Success;