From 33b205b1c79e40c7eb4b8745e4afebb0a6941c03 Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Sun, 7 Apr 2019 21:44:15 +0300 Subject: [PATCH] (3300d3915) When searching for a new room to move to, AIObjectiveIdle only attempts to find a path to one potential room per frame. Fixes lag spikes caused by idling NPCs (was particularly noticeable if the character was in a room with no way out). Closes #1397 --- .../AI/Objectives/AIObjectiveIdle.cs | 109 +++++++++--------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs index 886394bd4..34d03462d 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs @@ -21,6 +21,8 @@ namespace Barotrauma private Hull currentTarget; private float newTargetTimer; + private bool searchingNewHull; + private float standStillTimer; private float walkDuration; @@ -66,7 +68,38 @@ namespace Barotrauma } if (newTargetTimer <= 0.0f) { - currentTarget = FindRandomHull(); + if (!searchingNewHull) + { + //find all available hulls first + FindTargetHulls(); + searchingNewHull = true; + return; + } + else if (targetHulls.Count > 0) + { + //choose a random available hull + var randomHull = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced); + + bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull); + if (isCurrentHullOK) + { + // Check that there is no unsafe or forbidden hulls on the way to the target + // Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull. + var path = PathSteering.PathFinder.FindPath(character.SimPosition, randomHull.SimPosition); + if (path.Unreachable || + path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull))) + { + //can't go to this room, remove it from the list and try another room next frame + int index = targetHulls.IndexOf(randomHull); + targetHulls.RemoveAt(index); + hullWeights.RemoveAt(index); + PathSteering.Reset(); + return; + } + } + currentTarget = randomHull; + searchingNewHull = false; + } if (currentTarget != null) { @@ -162,70 +195,36 @@ namespace Barotrauma private readonly List targetHulls = new List(20); private readonly List hullWeights = new List(20); - private Hull FindRandomHull() + private void FindTargetHulls() { var idCard = character.Inventory.FindItemByIdentifier("idcard"); - Hull targetHull = null; bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull); - //random chance of navigating back to the room where the character spawned - if (Rand.Int(5) == 1 && idCard != null) - { - foreach (WayPoint wp in WayPoint.WayPointList) - { - if (wp.SpawnType != SpawnType.Human || wp.CurrentHull == null) { continue; } - foreach (string tag in wp.IdCardTags) + targetHulls.Clear(); + hullWeights.Clear(); + foreach (var hull in Hull.hullList) + { + if (HumanAIController.UnsafeHulls.Contains(hull)) { continue; } + if (hull.Submarine == null) { continue; } + if (hull.Submarine.TeamID != character.TeamID) { continue; } + // If the character is inside, only take connected hulls into account. + if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(hull, true)) { continue; } + if (IsForbidden(hull)) { continue; } + // Ignore hulls that are too low to stand inside + if (character.AnimController is HumanoidAnimController animController) + { + if (hull.CeilingHeight < ConvertUnits.ToDisplayUnits(animController.HeadPosition.Value)) { - if (idCard.HasTag(tag)) - { - targetHull = wp.CurrentHull; - } + continue; } } - } - if (targetHull == null) - { - targetHulls.Clear(); - hullWeights.Clear(); - foreach (var hull in Hull.hullList) + if (!targetHulls.Contains(hull)) { - if (HumanAIController.UnsafeHulls.Contains(hull)) { continue; } - if (hull.Submarine == null) { continue; } - if (hull.Submarine.TeamID != character.TeamID) { continue; } - // If the character is inside, only take connected hulls into account. - if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(hull, true)) { continue; } - if (IsForbidden(hull)) { continue; } - // Ignore hulls that are too low to stand inside - if (character.AnimController is HumanoidAnimController animController) - { - if (hull.CeilingHeight < ConvertUnits.ToDisplayUnits(animController.HeadPosition.Value)) - { - continue; - } - } - if (isCurrentHullOK) - { - // Check that there is no unsafe or forbidden hulls on the way to the target - // Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull. - - //TODO: optimize this (only attempt to find a couple of paths per frame?). Now idle characters completely kill the fps if they can't find a path (e.g. if they're in a room with no way out or if the sub is missing waypoints) - var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition); - if (path.Unreachable) { continue; } - if (path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull))) { continue; } - } - - // If we want to do a steering check, we should do it here, before setting the path - //if (path.Cost > 1000.0f) { continue; } - - if (!targetHulls.Contains(hull)) - { - targetHulls.Add(hull); - hullWeights.Add(hull.Volume); - } + targetHulls.Add(hull); + hullWeights.Add(hull.Volume); } - return ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced); } - return targetHull; + } private bool IsForbidden(Hull hull)