Reuse HashSets for actors entered/exited in CellTrigger & ProximityTrigger.

Actors involved in the trigger are determined as actors move around near the trigger - and can be expensive in some maps. This allows us to avoid allocating new sets and the CPU hit required to set it up each time.
This commit is contained in:
RoosterDragon
2017-12-22 12:57:57 +00:00
committed by Paul Chote
parent 11b44963da
commit 986025ca76

View File

@@ -48,8 +48,8 @@ namespace OpenRA.Mods.Common.Traits
readonly Action<Actor> onActorEntered; readonly Action<Actor> onActorEntered;
readonly Action<Actor> onActorExited; readonly Action<Actor> onActorExited;
readonly HashSet<Actor> oldActors = new HashSet<Actor>();
IEnumerable<Actor> currentActors = Enumerable.Empty<Actor>(); readonly HashSet<Actor> currentActors = new HashSet<Actor>();
public CellTrigger(CPos[] footprint, Action<Actor> onActorEntered, Action<Actor> onActorExited) public CellTrigger(CPos[] footprint, Action<Actor> onActorEntered, Action<Actor> onActorExited)
{ {
@@ -67,18 +67,21 @@ namespace OpenRA.Mods.Common.Traits
if (!Dirty) if (!Dirty)
return; return;
var oldActors = currentActors; // PERF: Reuse collection to avoid allocations.
currentActors = Footprint.SelectMany(actorMap.GetActorsAt).ToList(); oldActors.Clear();
oldActors.UnionWith(currentActors);
var entered = currentActors.Except(oldActors); currentActors.Clear();
var exited = oldActors.Except(currentActors); currentActors.UnionWith(Footprint.SelectMany(actorMap.GetActorsAt));
if (onActorEntered != null) if (onActorEntered != null)
foreach (var a in entered) foreach (var a in currentActors)
if (!oldActors.Contains(a))
onActorEntered(a); onActorEntered(a);
if (onActorExited != null) if (onActorExited != null)
foreach (var a in exited) foreach (var a in oldActors)
if (!currentActors.Contains(a))
onActorExited(a); onActorExited(a);
Dirty = false; Dirty = false;
@@ -94,13 +97,13 @@ namespace OpenRA.Mods.Common.Traits
readonly Action<Actor> onActorEntered; readonly Action<Actor> onActorEntered;
readonly Action<Actor> onActorExited; readonly Action<Actor> onActorExited;
readonly HashSet<Actor> oldActors = new HashSet<Actor>();
readonly HashSet<Actor> currentActors = new HashSet<Actor>();
WPos position; WPos position;
WDist range; WDist range;
WDist vRange; WDist vRange;
IEnumerable<Actor> currentActors = Enumerable.Empty<Actor>();
public ProximityTrigger(WPos pos, WDist range, WDist vRange, Action<Actor> onActorEntered, Action<Actor> onActorExited) public ProximityTrigger(WPos pos, WDist range, WDist vRange, Action<Actor> onActorEntered, Action<Actor> onActorExited)
{ {
this.onActorEntered = onActorEntered; this.onActorEntered = onActorEntered;
@@ -128,22 +131,25 @@ namespace OpenRA.Mods.Common.Traits
if (!Dirty) if (!Dirty)
return; return;
var oldActors = currentActors; // PERF: Reuse collection to avoid allocations.
var delta = new WVec(range, range, WDist.Zero); oldActors.Clear();
currentActors = am.ActorsInBox(position - delta, position + delta) oldActors.UnionWith(currentActors);
.Where(a => (a.CenterPosition - position).HorizontalLengthSquared < range.LengthSquared
&& (vRange.Length == 0 || (a.World.Map.DistanceAboveTerrain(a.CenterPosition).LengthSquared <= vRange.LengthSquared)))
.ToList();
var entered = currentActors.Except(oldActors); var delta = new WVec(range, range, WDist.Zero);
var exited = oldActors.Except(currentActors); currentActors.Clear();
currentActors.UnionWith(
am.ActorsInBox(position - delta, position + delta)
.Where(a => (a.CenterPosition - position).HorizontalLengthSquared < range.LengthSquared
&& (vRange.Length == 0 || (a.World.Map.DistanceAboveTerrain(a.CenterPosition).LengthSquared <= vRange.LengthSquared))));
if (onActorEntered != null) if (onActorEntered != null)
foreach (var a in entered) foreach (var a in currentActors)
if (!oldActors.Contains(a))
onActorEntered(a); onActorEntered(a);
if (onActorExited != null) if (onActorExited != null)
foreach (var a in exited) foreach (var a in oldActors)
if (!currentActors.Contains(a))
onActorExited(a); onActorExited(a);
Dirty = false; Dirty = false;