(24c49c72a) Added swarm behavior to crawlers. Any group of monsters spawned by a single MonsterEvent will now act as a swarm if they have SwarmBehavior defined in their xml

This commit is contained in:
Joonas Rikkonen
2019-04-18 12:02:35 +03:00
parent fe163b34b2
commit aed2c71806
4 changed files with 109 additions and 11 deletions

View File

@@ -45,6 +45,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\PathFinder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\SteeringManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\SteeringPath.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\SwarmBehavior.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\AnimController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\FishAnimController.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\HumanoidAnimController.cs" />

View File

@@ -122,6 +122,7 @@ namespace Barotrauma
private readonly float memoryFadeTime = 0.5f;
public LatchOntoAI LatchOntoAI { get; private set; }
public SwarmBehavior SwarmBehavior { get; private set; }
public bool AttackHumans
{
@@ -215,6 +216,10 @@ namespace Barotrauma
case "latchonto":
LatchOntoAI = new LatchOntoAI(subElement, this);
break;
case "swarm":
case "swarmbehavior":
SwarmBehavior = new SwarmBehavior(subElement, this);
break;
case "targetpriority":
targetingPriorities.Add(subElement.GetAttributeString("tag", "").ToLowerInvariant(), new TargetingPriority(subElement));
break;
@@ -364,12 +369,8 @@ namespace Barotrauma
default:
throw new NotImplementedException();
}
// Just some debug code that makes the characters to follow the mouse cursor
//run = true;
//Vector2 mousePos = ConvertUnits.ToSimUnits(Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition));
//steeringManager.SteeringSeek(mousePos, Character.AnimController.GetCurrentSpeed(run));
SwarmBehavior?.Update(deltaTime);
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
}
@@ -790,6 +791,7 @@ namespace Barotrauma
{
UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance);
}
return false;
}
private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)

View File

@@ -0,0 +1,95 @@
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma
{
class SwarmBehavior
{
private float minDistFromClosest;
private float maxDistFromCenter;
private float cohesion;
private List<AICharacter> members = new List<AICharacter>();
private AIController ai;
public SwarmBehavior(XElement element, AIController ai)
{
this.ai = ai;
minDistFromClosest = ConvertUnits.ToSimUnits(element.GetAttributeFloat("mindistfromclosest", 10.0f));
maxDistFromCenter = ConvertUnits.ToSimUnits(element.GetAttributeFloat("maxdistfromcenter", 1000.0f));
cohesion = element.GetAttributeFloat("cohesion", 0.1f);
}
public static void CreateSwarm(IEnumerable<AICharacter> swarm)
{
foreach (AICharacter character in swarm)
{
if (character.AIController is EnemyAIController enemyAI && enemyAI.SwarmBehavior != null)
{
enemyAI.SwarmBehavior.members = swarm.ToList();
}
}
}
public void Update(float deltaTime)
{
members.RemoveAll(m => m.IsDead || m.Removed);
if (members.Count < 2) { return; }
//calculate the "center of mass" of the swarm and the distance to the closest character in the swarm
float closestDistSqr = float.MaxValue;
Vector2 center = Vector2.Zero;
AICharacter closest = null;
foreach (AICharacter member in members)
{
center += member.SimPosition;
if (member == ai.Character) { continue; }
float distSqr = Vector2.DistanceSquared(member.SimPosition, ai.Character.SimPosition);
if (distSqr < closestDistSqr)
{
closestDistSqr = distSqr;
closest = member;
}
}
center /= members.Count;
if (closest == null) { return; }
//steer away from the closest if too close
float closestDist = (float)Math.Sqrt(closestDistSqr);
if (closestDist < minDistFromClosest)
{
Vector2 diff = closest.SimPosition - ai.SimPosition;
if (diff.LengthSquared() < 0.0001f)
{
diff = Vector2.UnitX;
}
ai.SteeringManager.SteeringManual(deltaTime, -diff);
}
//steer closer to the center of mass if too far
else if (Vector2.DistanceSquared(center, ai.SimPosition) > maxDistFromCenter * maxDistFromCenter)
{
float distFromCenter = Vector2.Distance(center, ai.SimPosition);
ai.SteeringManager.SteeringSeek(center, distFromCenter - maxDistFromCenter);
}
//keep the characters moving in roughly the same direction
if (cohesion > 0.0f)
{
Vector2 avgVel = Vector2.Zero;
foreach (AICharacter member in members)
{
avgVel += member.AnimController.TargetMovement;
}
avgVel /= members.Count;
ai.SteeringManager.SteeringManual(deltaTime, avgVel * cohesion);
}
}
}
}

View File

@@ -227,17 +227,17 @@ namespace Barotrauma
monsters = new List<Character>();
float offsetAmount = spawnPosType == Level.PositionType.MainPath ? 1000 : 100;
for (int i = 0; i < amount; i++)
{
{
CoroutineManager.InvokeAfter(() =>
{
bool isClient = false;
#if CLIENT
isClient = GameMain.Client != null;
#endif
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
monsters.Add(Character.Create(characterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true));
if (monsters.Count == amount)
{
spawnReady = true;
//this will do nothing if the monsters have no swarm behavior defined,
//otherwise it'll make the spawned characters act as a swarm
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
}
}, Rand.Range(0f, amount / 2, Rand.RandSync.Server));
}