Split Actor.Bounds into RenderBounds and SelectableBounds
Additionally, internally renamed VisualBounds to SelectionOverlayBounds to avoid confusion with RenderBounds. This step was necessary to prevent actors with selectable area smaller than their graphics to be removed too early from ScreenMap even though part of the graphics should still be visible. RA cruisers were a prime example, but to a lesser extent several other actors were affected as well. This separation also serves as preparation to determine the final RenderBounds from multiple source bounds later, to fix the remaining ScreenMap issues (building 'bibs', aircraft shadows).
This commit is contained in:
@@ -47,8 +47,9 @@ namespace OpenRA
|
|||||||
|
|
||||||
public int Generation;
|
public int Generation;
|
||||||
|
|
||||||
public Rectangle Bounds { get; private set; }
|
public Rectangle RenderBounds { get; private set; }
|
||||||
public Rectangle VisualBounds { get; private set; }
|
public Rectangle SelectableBounds { get; private set; }
|
||||||
|
public Rectangle SelectionOverlayBounds { get; private set; }
|
||||||
public IEffectiveOwner EffectiveOwner { get; private set; }
|
public IEffectiveOwner EffectiveOwner { get; private set; }
|
||||||
public IOccupySpace OccupiesSpace { get; private set; }
|
public IOccupySpace OccupiesSpace { get; private set; }
|
||||||
public ITargetable[] Targetables { get; private set; }
|
public ITargetable[] Targetables { get; private set; }
|
||||||
@@ -110,8 +111,14 @@ namespace OpenRA
|
|||||||
// PERF: Cache all these traits as soon as the actor is created. This is a fairly cheap one-off cost per
|
// PERF: Cache all these traits as soon as the actor is created. This is a fairly cheap one-off cost per
|
||||||
// actor that allows us to provide some fast implementations of commonly used methods that are relied on by
|
// actor that allows us to provide some fast implementations of commonly used methods that are relied on by
|
||||||
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
|
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
|
||||||
Bounds = DetermineBounds();
|
|
||||||
VisualBounds = DetermineVisualBounds();
|
// RenderBounds are used for ScreenMap binning
|
||||||
|
// SelectableBounds define the selectable area of the actor
|
||||||
|
// SelectionOverlayBounds are used to draw the selection box and determine offsets for other selection overlays
|
||||||
|
RenderBounds = DetermineRenderBounds();
|
||||||
|
SelectableBounds = DetermineSelectableBounds();
|
||||||
|
SelectionOverlayBounds = DetermineSelectionOverlayBounds();
|
||||||
|
|
||||||
EffectiveOwner = TraitOrDefault<IEffectiveOwner>();
|
EffectiveOwner = TraitOrDefault<IEffectiveOwner>();
|
||||||
facing = TraitOrDefault<IFacing>();
|
facing = TraitOrDefault<IFacing>();
|
||||||
health = TraitOrDefault<IHealth>();
|
health = TraitOrDefault<IHealth>();
|
||||||
@@ -129,24 +136,34 @@ namespace OpenRA
|
|||||||
.Select(pair => new SyncHash(pair.First, pair.Second(pair.First)));
|
.Select(pair => new SyncHash(pair.First, pair.Second(pair.First)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle DetermineBounds()
|
Rectangle DetermineRenderBounds()
|
||||||
|
{
|
||||||
|
var size = TraitsImplementing<IAutoSelectionSize>().Select(x => x.SelectionSize(this)).FirstOrDefault();
|
||||||
|
var offset = -size / 2;
|
||||||
|
|
||||||
|
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle DetermineSelectableBounds()
|
||||||
{
|
{
|
||||||
var si = Info.TraitInfoOrDefault<SelectableInfo>();
|
var si = Info.TraitInfoOrDefault<SelectableInfo>();
|
||||||
var size = (si != null && si.Bounds != null) ? new int2(si.Bounds[0], si.Bounds[1]) :
|
if (si == null || si.Bounds == null)
|
||||||
TraitsImplementing<IAutoSelectionSize>().Select(x => x.SelectionSize(this)).FirstOrDefault();
|
return RenderBounds;
|
||||||
|
|
||||||
|
var size = new int2(si.Bounds[0], si.Bounds[1]);
|
||||||
|
|
||||||
var offset = -size / 2;
|
var offset = -size / 2;
|
||||||
if (si != null && si.Bounds != null && si.Bounds.Length > 2)
|
if (si.Bounds.Length > 2)
|
||||||
offset += new int2(si.Bounds[2], si.Bounds[3]);
|
offset += new int2(si.Bounds[2], si.Bounds[3]);
|
||||||
|
|
||||||
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
|
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle DetermineVisualBounds()
|
Rectangle DetermineSelectionOverlayBounds()
|
||||||
{
|
{
|
||||||
var sd = Info.TraitInfoOrDefault<ISelectionDecorationsInfo>();
|
var sd = Info.TraitInfoOrDefault<ISelectionDecorationsInfo>();
|
||||||
if (sd == null || sd.SelectionBoxBounds == null)
|
if (sd == null || sd.SelectionBoxBounds == null)
|
||||||
return Bounds;
|
return SelectableBounds;
|
||||||
|
|
||||||
var size = new int2(sd.SelectionBoxBounds[0], sd.SelectionBoxBounds[1]);
|
var size = new int2(sd.SelectionBoxBounds[0], sd.SelectionBoxBounds[1]);
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ namespace OpenRA.Graphics
|
|||||||
var health = actor.TraitOrDefault<IHealth>();
|
var health = actor.TraitOrDefault<IHealth>();
|
||||||
|
|
||||||
var screenPos = wr.Screen3DPxPosition(pos);
|
var screenPos = wr.Screen3DPxPosition(pos);
|
||||||
var bounds = actor.VisualBounds;
|
var bounds = actor.SelectionOverlayBounds;
|
||||||
bounds.Offset((int)screenPos.X, (int)screenPos.Y);
|
bounds.Offset((int)screenPos.X, (int)screenPos.Y);
|
||||||
|
|
||||||
var start = new float3(bounds.Left + 1, bounds.Top, screenPos.Z);
|
var start = new float3(bounds.Left + 1, bounds.Top, screenPos.Z);
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public static Actor WithHighestSelectionPriority(this IEnumerable<Actor> actors, int2 selectionPixel)
|
public static Actor WithHighestSelectionPriority(this IEnumerable<Actor> actors, int2 selectionPixel)
|
||||||
{
|
{
|
||||||
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.Bounds, selectionPixel));
|
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.SelectableBounds, selectionPixel));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FrozenActor WithHighestSelectionPriority(this IEnumerable<FrozenActor> actors, int2 selectionPixel)
|
public static FrozenActor WithHighestSelectionPriority(this IEnumerable<FrozenActor> actors, int2 selectionPixel)
|
||||||
{
|
{
|
||||||
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.Bounds, selectionPixel));
|
return actors.MaxByOrDefault(a => CalculateActorSelectionPriority(a.Info, a.SelectableBounds, selectionPixel));
|
||||||
}
|
}
|
||||||
|
|
||||||
static long CalculateActorSelectionPriority(ActorInfo info, Rectangle bounds, int2 selectionPixel)
|
static long CalculateActorSelectionPriority(ActorInfo info, Rectangle bounds, int2 selectionPixel)
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ namespace OpenRA.Traits
|
|||||||
{
|
{
|
||||||
public readonly PPos[] Footprint;
|
public readonly PPos[] Footprint;
|
||||||
public readonly WPos CenterPosition;
|
public readonly WPos CenterPosition;
|
||||||
public readonly Rectangle Bounds;
|
public readonly Rectangle RenderBounds;
|
||||||
|
public readonly Rectangle SelectableBounds;
|
||||||
public readonly HashSet<string> TargetTypes;
|
public readonly HashSet<string> TargetTypes;
|
||||||
readonly Actor actor;
|
readonly Actor actor;
|
||||||
readonly Shroud shroud;
|
readonly Shroud shroud;
|
||||||
@@ -77,7 +78,8 @@ namespace OpenRA.Traits
|
|||||||
footprint.Select(p => shroud.Contains(p).ToString()).JoinWith("|")));
|
footprint.Select(p => shroud.Contains(p).ToString()).JoinWith("|")));
|
||||||
|
|
||||||
CenterPosition = self.CenterPosition;
|
CenterPosition = self.CenterPosition;
|
||||||
Bounds = self.Bounds;
|
RenderBounds = self.RenderBounds;
|
||||||
|
SelectableBounds = self.SelectableBounds;
|
||||||
TargetTypes = self.GetEnabledTargetTypes().ToHashSet();
|
TargetTypes = self.GetEnabledTargetTypes().ToHashSet();
|
||||||
|
|
||||||
tooltips = self.TraitsImplementing<ITooltip>().ToArray();
|
tooltips = self.TraitsImplementing<ITooltip>().ToArray();
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace OpenRA.Traits
|
|||||||
Rectangle FrozenActorBounds(FrozenActor fa)
|
Rectangle FrozenActorBounds(FrozenActor fa)
|
||||||
{
|
{
|
||||||
var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition);
|
var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition);
|
||||||
var bounds = fa.Bounds;
|
var bounds = fa.RenderBounds;
|
||||||
bounds.Offset(pos.X, pos.Y);
|
bounds.Offset(pos.X, pos.Y);
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ namespace OpenRA.Traits
|
|||||||
Rectangle ActorBounds(Actor a)
|
Rectangle ActorBounds(Actor a)
|
||||||
{
|
{
|
||||||
var pos = worldRenderer.ScreenPxPosition(a.CenterPosition);
|
var pos = worldRenderer.ScreenPxPosition(a.CenterPosition);
|
||||||
var bounds = a.Bounds;
|
var bounds = a.RenderBounds;
|
||||||
bounds.Offset(pos.X, pos.Y);
|
bounds.Offset(pos.X, pos.Y);
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ namespace OpenRA
|
|||||||
ActorMap.AddInfluence(self, ios);
|
ActorMap.AddInfluence(self, ios);
|
||||||
ActorMap.AddPosition(self, ios);
|
ActorMap.AddPosition(self, ios);
|
||||||
|
|
||||||
if (!self.Bounds.Size.IsEmpty)
|
if (!self.RenderBounds.Size.IsEmpty)
|
||||||
ScreenMap.Add(self);
|
ScreenMap.Add(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ namespace OpenRA
|
|||||||
if (!self.IsInWorld)
|
if (!self.IsInWorld)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!self.Bounds.Size.IsEmpty)
|
if (!self.RenderBounds.Size.IsEmpty)
|
||||||
ScreenMap.Update(self);
|
ScreenMap.Update(self);
|
||||||
|
|
||||||
ActorMap.UpdatePosition(self, ios);
|
ActorMap.UpdatePosition(self, ios);
|
||||||
@@ -212,7 +212,7 @@ namespace OpenRA
|
|||||||
ActorMap.RemoveInfluence(self, ios);
|
ActorMap.RemoveInfluence(self, ios);
|
||||||
ActorMap.RemovePosition(self, ios);
|
ActorMap.RemovePosition(self, ios);
|
||||||
|
|
||||||
if (!self.Bounds.Size.IsEmpty)
|
if (!self.RenderBounds.Size.IsEmpty)
|
||||||
ScreenMap.Remove(self);
|
ScreenMap.Remove(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Graphics
|
|||||||
readonly Color color;
|
readonly Color color;
|
||||||
|
|
||||||
public SelectionBoxRenderable(Actor actor, Color color)
|
public SelectionBoxRenderable(Actor actor, Color color)
|
||||||
: this(actor.CenterPosition, actor.VisualBounds, color) { }
|
: this(actor.CenterPosition, actor.SelectionOverlayBounds, color) { }
|
||||||
|
|
||||||
public SelectionBoxRenderable(WPos pos, Rectangle visualBounds, Color color)
|
public SelectionBoxRenderable(WPos pos, Rectangle visualBounds, Color color)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
self.World.ActorMap.AddInfluence(self, this);
|
self.World.ActorMap.AddInfluence(self, this);
|
||||||
self.World.ActorMap.AddPosition(self, this);
|
self.World.ActorMap.AddPosition(self, this);
|
||||||
|
|
||||||
if (!self.Bounds.Size.IsEmpty)
|
if (!self.RenderBounds.Size.IsEmpty)
|
||||||
self.World.ScreenMap.Add(self);
|
self.World.ScreenMap.Add(self);
|
||||||
|
|
||||||
influence.AddInfluence(self, Info.Tiles(self.Location));
|
influence.AddInfluence(self, Info.Tiles(self.Location));
|
||||||
@@ -325,7 +325,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
self.World.ActorMap.RemoveInfluence(self, this);
|
self.World.ActorMap.RemoveInfluence(self, this);
|
||||||
self.World.ActorMap.RemovePosition(self, this);
|
self.World.ActorMap.RemovePosition(self, this);
|
||||||
|
|
||||||
if (!self.Bounds.Size.IsEmpty)
|
if (!self.RenderBounds.Size.IsEmpty)
|
||||||
self.World.ScreenMap.Remove(self);
|
self.World.ScreenMap.Remove(self);
|
||||||
|
|
||||||
influence.RemoveInfluence(self, Info.Tiles(self.Location));
|
influence.RemoveInfluence(self, Info.Tiles(self.Location));
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
self.World.ActorMap.AddInfluence(self, this);
|
self.World.ActorMap.AddInfluence(self, this);
|
||||||
self.World.ActorMap.AddPosition(self, this);
|
self.World.ActorMap.AddPosition(self, this);
|
||||||
|
|
||||||
if (!self.Bounds.Size.IsEmpty)
|
if (!self.RenderBounds.Size.IsEmpty)
|
||||||
self.World.ScreenMap.Add(self);
|
self.World.ScreenMap.Add(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
self.World.ActorMap.RemoveInfluence(self, this);
|
self.World.ActorMap.RemoveInfluence(self, this);
|
||||||
self.World.ActorMap.RemovePosition(self, this);
|
self.World.ActorMap.RemovePosition(self, this);
|
||||||
|
|
||||||
if (!self.Bounds.Size.IsEmpty)
|
if (!self.RenderBounds.Size.IsEmpty)
|
||||||
self.World.ScreenMap.Remove(self);
|
self.World.ScreenMap.Remove(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
|
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr)
|
||||||
{
|
{
|
||||||
var pos = wr.ScreenPxPosition(self.CenterPosition);
|
var pos = wr.ScreenPxPosition(self.CenterPosition);
|
||||||
var bounds = self.Bounds;
|
var bounds = self.SelectableBounds;
|
||||||
bounds.Offset(pos.X, pos.Y);
|
bounds.Offset(pos.X, pos.Y);
|
||||||
var spaceBuffer = (int)(10 / wr.Viewport.Zoom);
|
var spaceBuffer = (int)(10 / wr.Viewport.Zoom);
|
||||||
var effectPos = wr.ProjectedPosition(new int2(pos.X, bounds.Y - spaceBuffer));
|
var effectPos = wr.ProjectedPosition(new int2(pos.X, bounds.Y - spaceBuffer));
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
{
|
{
|
||||||
[PaletteReference] public readonly string Palette = "chrome";
|
[PaletteReference] public readonly string Palette = "chrome";
|
||||||
|
|
||||||
[Desc("Visual bounds for selection box. If null, it uses AutoSelectionSize.",
|
[Desc("Bounds for visual selection box. If null, it uses AutoSelectionSize.",
|
||||||
"The first two values define the bounds' size, the optional third and fourth",
|
"The first two values define the bounds' size, the optional third and fourth",
|
||||||
"values specify the position relative to the actors' center. Defaults to selectable bounds.")]
|
"values specify the position relative to the actors' center. Defaults to selectable bounds.")]
|
||||||
public readonly int[] VisualBounds = null;
|
public readonly int[] VisualBounds = null;
|
||||||
@@ -128,7 +128,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
if (pipSources.Length == 0)
|
if (pipSources.Length == 0)
|
||||||
return Enumerable.Empty<IRenderable>();
|
return Enumerable.Empty<IRenderable>();
|
||||||
|
|
||||||
var b = self.VisualBounds;
|
var b = self.SelectionOverlayBounds;
|
||||||
var pos = wr.ScreenPxPosition(self.CenterPosition);
|
var pos = wr.ScreenPxPosition(self.CenterPosition);
|
||||||
var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom));
|
var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom));
|
||||||
var pal = wr.Palette(Info.Palette);
|
var pal = wr.Palette(Info.Palette);
|
||||||
@@ -143,7 +143,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
var pipSize = pipImages.Image.Size.XY.ToInt2();
|
var pipSize = pipImages.Image.Size.XY.ToInt2();
|
||||||
var pipxyBase = basePosition + new int2(1 - pipSize.X / 2, -(3 + pipSize.Y / 2));
|
var pipxyBase = basePosition + new int2(1 - pipSize.X / 2, -(3 + pipSize.Y / 2));
|
||||||
var pipxyOffset = new int2(0, 0);
|
var pipxyOffset = new int2(0, 0);
|
||||||
var width = self.VisualBounds.Width;
|
var width = self.SelectionOverlayBounds.Width;
|
||||||
|
|
||||||
foreach (var pips in pipSources)
|
foreach (var pips in pipSources)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
if (!ShouldRender(self) || self.World.FogObscures(self))
|
if (!ShouldRender(self) || self.World.FogObscures(self))
|
||||||
return Enumerable.Empty<IRenderable>();
|
return Enumerable.Empty<IRenderable>();
|
||||||
|
|
||||||
var bounds = self.VisualBounds;
|
var bounds = self.SelectionOverlayBounds;
|
||||||
var halfSize = (0.5f * Anim.Image.Size.XY).ToInt2();
|
var halfSize = (0.5f * Anim.Image.Size.XY).ToInt2();
|
||||||
|
|
||||||
var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;
|
var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
|
|
||||||
pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group);
|
pipImages.PlayFetchIndex(Info.GroupSequence, () => (int)group);
|
||||||
|
|
||||||
var bounds = self.VisualBounds;
|
var bounds = self.SelectionOverlayBounds;
|
||||||
var boundsOffset = 0.5f * new float2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom);
|
var boundsOffset = 0.5f * new float2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom);
|
||||||
if (Info.ReferencePoint.HasFlag(ReferencePoints.Top))
|
if (Info.ReferencePoint.HasFlag(ReferencePoints.Top))
|
||||||
boundsOffset -= new float2(0, 0.5f * bounds.Height);
|
boundsOffset -= new float2(0, 0.5f * bounds.Height);
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
if (group == null)
|
if (group == null)
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
var bounds = self.VisualBounds;
|
var bounds = self.SelectionOverlayBounds;
|
||||||
var number = group.Value.ToString();
|
var number = group.Value.ToString();
|
||||||
var halfSize = font.Measure(number) / 2;
|
var halfSize = font.Measure(number) / 2;
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace OpenRA.Mods.Common.Traits.Render
|
|||||||
if (!ShouldRender(self) || self.World.FogObscures(self))
|
if (!ShouldRender(self) || self.World.FogObscures(self))
|
||||||
return Enumerable.Empty<IRenderable>();
|
return Enumerable.Empty<IRenderable>();
|
||||||
|
|
||||||
var bounds = self.VisualBounds;
|
var bounds = self.SelectionOverlayBounds;
|
||||||
var halfSize = font.Measure(Info.Text) / 2;
|
var halfSize = font.Measure(Info.Text) / 2;
|
||||||
|
|
||||||
var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;
|
var boundsOffset = new int2(bounds.Left + bounds.Right, bounds.Top + bounds.Bottom) / 2;
|
||||||
|
|||||||
Reference in New Issue
Block a user