diff --git a/OpenRA.Game/Primitives/SpatiallyPartitioned.cs b/OpenRA.Game/Primitives/SpatiallyPartitioned.cs index debe06edc4..ae0f337421 100644 --- a/OpenRA.Game/Primitives/SpatiallyPartitioned.cs +++ b/OpenRA.Game/Primitives/SpatiallyPartitioned.cs @@ -62,6 +62,11 @@ namespace OpenRA.Primitives return true; } + public bool Contains(T item) + { + return itemBounds.ContainsKey(item); + } + Dictionary BinAt(int row, int col) { return itemBoundsBins[row * cols + col]; diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index 0c36c29003..186484521f 100644 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -210,7 +210,7 @@ namespace OpenRA.Traits public void Add(FrozenActor fa) { frozenActorsById.Add(fa.ID, fa); - world.ScreenMap.Add(owner, fa); + world.ScreenMap.AddOrUpdate(owner, fa); partitionedFrozenActorIds.Add(fa.ID, FootprintBounds(fa)); } diff --git a/OpenRA.Game/Traits/World/ScreenMap.cs b/OpenRA.Game/Traits/World/ScreenMap.cs index b7a019ef4a..49994627db 100644 --- a/OpenRA.Game/Traits/World/ScreenMap.cs +++ b/OpenRA.Game/Traits/World/ScreenMap.cs @@ -35,6 +35,13 @@ namespace OpenRA.Traits readonly Cache> partitionedFrozenActors; readonly SpatiallyPartitioned partitionedActors; readonly SpatiallyPartitioned partitionedEffects; + + // Updates are done in one pass to ensure all bound changes have been applied + readonly HashSet addOrUpdateActors = new HashSet(); + readonly HashSet removeActors = new HashSet(); + readonly Cache> addOrUpdateFrozenActors; + readonly Cache> removeFrozenActors; + WorldRenderer worldRenderer; public ScreenMap(World world, ScreenMapInfo info) @@ -44,6 +51,10 @@ namespace OpenRA.Traits var height = world.Map.MapSize.Y * size.Height; partitionedFrozenActors = new Cache>( _ => new SpatiallyPartitioned(width, height, info.BinSize)); + + addOrUpdateFrozenActors = new Cache>(_ => new HashSet()); + removeFrozenActors = new Cache>(_ => new HashSet()); + partitionedActors = new SpatiallyPartitioned(width, height, info.BinSize); partitionedEffects = new SpatiallyPartitioned(width, height, info.BinSize); } @@ -66,33 +77,30 @@ namespace OpenRA.Traits return bounds; } - public void Add(Player viewer, FrozenActor fa) + public void AddOrUpdate(Player viewer, FrozenActor fa) { - partitionedFrozenActors[viewer].Add(fa, FrozenActorBounds(fa)); + if (removeFrozenActors[viewer].Contains(fa)) + removeFrozenActors[viewer].Remove(fa); + + addOrUpdateFrozenActors[viewer].Add(fa); } public void Remove(Player viewer, FrozenActor fa) { - partitionedFrozenActors[viewer].Remove(fa); + removeFrozenActors[viewer].Add(fa); } - public void Add(Actor a) + public void AddOrUpdate(Actor a) { - var bounds = ActorBounds(a); - if (!bounds.Size.IsEmpty) - partitionedActors.Add(a, ActorBounds(a)); - } + if (removeActors.Contains(a)) + removeActors.Remove(a); - public void Update(Actor a) - { - var bounds = ActorBounds(a); - if (!bounds.Size.IsEmpty) - partitionedActors.Update(a, ActorBounds(a)); + addOrUpdateActors.Add(a); } public void Remove(Actor a) { - partitionedActors.Remove(a); + removeActors.Add(a); } public void Add(IEffect effect, WPos position, Size size) @@ -192,6 +200,56 @@ namespace OpenRA.Traits return partitionedFrozenActors[p].InBox(r).Where(frozenActorIsValid); } + public void Tick() + { + foreach (var a in addOrUpdateActors) + { + var bounds = ActorBounds(a); + if (!bounds.Size.IsEmpty) + { + if (partitionedActors.Contains(a)) + partitionedActors.Update(a, bounds); + else + partitionedActors.Add(a, bounds); + } + else + partitionedActors.Remove(a); + } + + foreach (var a in removeActors) + partitionedActors.Remove(a); + + addOrUpdateActors.Clear(); + removeActors.Clear(); + + foreach (var kv in addOrUpdateFrozenActors) + { + foreach (var fa in kv.Value) + { + var bounds = FrozenActorBounds(fa); + if (!bounds.Size.IsEmpty) + { + if (partitionedFrozenActors[kv.Key].Contains(fa)) + partitionedFrozenActors[kv.Key].Update(fa, bounds); + else + partitionedFrozenActors[kv.Key].Add(fa, bounds); + } + else + partitionedFrozenActors[kv.Key].Remove(fa); + } + + kv.Value.Clear(); + } + + foreach (var kv in removeFrozenActors) + { + foreach (var fa in kv.Value) + partitionedFrozenActors[kv.Key].Remove(fa); + + kv.Value.Clear(); + } + } + public IEnumerable ItemBounds(Player viewer) { var bounds = partitionedActors.ItemBounds diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 112c266a3a..1d1688245f 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -191,7 +191,7 @@ namespace OpenRA { ActorMap.AddInfluence(self, ios); ActorMap.AddPosition(self, ios); - ScreenMap.Add(self); + ScreenMap.AddOrUpdate(self); } public void UpdateMaps(Actor self, IOccupySpace ios) @@ -199,7 +199,7 @@ namespace OpenRA if (!self.IsInWorld) return; - ScreenMap.Update(self); + ScreenMap.AddOrUpdate(self); ActorMap.UpdatePosition(self, ios); } @@ -353,6 +353,7 @@ namespace OpenRA ActorsWithTrait().DoTimed(x => x.Trait.Tick(x.Actor), "Trait"); effects.DoTimed(e => e.Tick(this), "Effect"); + ScreenMap.Tick(); } while (frameEndActions.Count != 0) diff --git a/OpenRA.Mods.Common/Traits/Husk.cs b/OpenRA.Mods.Common/Traits/Husk.cs index 3965a44b18..411187f03e 100644 --- a/OpenRA.Mods.Common/Traits/Husk.cs +++ b/OpenRA.Mods.Common/Traits/Husk.cs @@ -105,7 +105,7 @@ namespace OpenRA.Mods.Common.Traits public void SetVisualPosition(Actor self, WPos pos) { CenterPosition = pos; - self.World.ScreenMap.Update(self); + self.World.ScreenMap.AddOrUpdate(self); } public void SetPosition(Actor self, WPos pos)