Reduce code duplication by extracting a common class to deal with spatial partitioning of actors, and use some (cached) delegates to reduce duplication further without affecting performance too much. Speed up updates and removal of actors by caching their location so we only need to update or remove them from bins they are actually in (typically very few), compared to having to check every bin for removals which is much more work in comparison. Speed up checking for actors inside a region by checking if items are located entirely within the bin they are located in. If so, we don't need to add them to the hash-set for de-duplication purposes which is fairly expensive.
140 lines
3.8 KiB
C#
140 lines
3.8 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
|
|
* This file is part of OpenRA, which is free software. It is made
|
|
* available to you under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation. For more information,
|
|
* see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using OpenRA.Graphics;
|
|
using OpenRA.Primitives;
|
|
|
|
namespace OpenRA.Traits
|
|
{
|
|
public class ScreenMapInfo : ITraitInfo
|
|
{
|
|
[Desc("Size of partition bins (world pixels)")]
|
|
public readonly int BinSize = 250;
|
|
|
|
public object Create(ActorInitializer init) { return new ScreenMap(init.World, this); }
|
|
}
|
|
|
|
public class ScreenMap : IWorldLoaded
|
|
{
|
|
static readonly IEnumerable<FrozenActor> NoFrozenActors = new FrozenActor[0];
|
|
readonly Func<FrozenActor, bool> frozenActorIsValid = fa => fa.IsValid;
|
|
readonly Func<Actor, bool> actorIsInWorld = a => a.IsInWorld;
|
|
readonly Cache<Player, SpatiallyPartitioned<FrozenActor>> partitionedFrozenActors;
|
|
readonly SpatiallyPartitioned<Actor> partitionedActors;
|
|
WorldRenderer worldRenderer;
|
|
|
|
public ScreenMap(World world, ScreenMapInfo info)
|
|
{
|
|
var ts = Game.ModData.Manifest.TileSize;
|
|
var width = world.Map.MapSize.X * ts.Width;
|
|
var height = world.Map.MapSize.Y * ts.Height;
|
|
partitionedFrozenActors = new Cache<Player, SpatiallyPartitioned<FrozenActor>>(
|
|
_ => new SpatiallyPartitioned<FrozenActor>(width, height, info.BinSize));
|
|
partitionedActors = new SpatiallyPartitioned<Actor>(width, height, info.BinSize);
|
|
}
|
|
|
|
public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
|
|
|
|
Rectangle FrozenActorBounds(FrozenActor fa)
|
|
{
|
|
var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition);
|
|
var bounds = fa.Bounds;
|
|
bounds.Offset(pos.X, pos.Y);
|
|
return bounds;
|
|
}
|
|
|
|
Rectangle ActorBounds(Actor a)
|
|
{
|
|
var pos = worldRenderer.ScreenPxPosition(a.CenterPosition);
|
|
var bounds = a.Bounds;
|
|
bounds.Offset(pos.X, pos.Y);
|
|
return bounds;
|
|
}
|
|
|
|
public void Add(Player viewer, FrozenActor fa)
|
|
{
|
|
partitionedFrozenActors[viewer].Add(fa, FrozenActorBounds(fa));
|
|
}
|
|
|
|
public void Remove(Player viewer, FrozenActor fa)
|
|
{
|
|
partitionedFrozenActors[viewer].Remove(fa);
|
|
}
|
|
|
|
public void Add(Actor a)
|
|
{
|
|
partitionedActors.Add(a, ActorBounds(a));
|
|
}
|
|
|
|
public void Update(Actor a)
|
|
{
|
|
partitionedActors.Update(a, ActorBounds(a));
|
|
}
|
|
|
|
public void Remove(Actor a)
|
|
{
|
|
partitionedActors.Remove(a);
|
|
}
|
|
|
|
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, int2 worldPx)
|
|
{
|
|
if (viewer == null)
|
|
return NoFrozenActors;
|
|
return partitionedFrozenActors[viewer].At(worldPx).Where(frozenActorIsValid);
|
|
}
|
|
|
|
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, MouseInput mi)
|
|
{
|
|
return FrozenActorsAt(viewer, worldRenderer.Viewport.ViewToWorldPx(mi.Location));
|
|
}
|
|
|
|
public IEnumerable<Actor> ActorsAt(int2 worldPx)
|
|
{
|
|
return partitionedActors.At(worldPx).Where(actorIsInWorld);
|
|
}
|
|
|
|
public IEnumerable<Actor> ActorsAt(MouseInput mi)
|
|
{
|
|
return ActorsAt(worldRenderer.Viewport.ViewToWorldPx(mi.Location));
|
|
}
|
|
|
|
static Rectangle RectWithCorners(int2 a, int2 b)
|
|
{
|
|
return Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
|
|
}
|
|
|
|
public IEnumerable<Actor> ActorsInBox(int2 a, int2 b)
|
|
{
|
|
return ActorsInBox(RectWithCorners(a, b));
|
|
}
|
|
|
|
public IEnumerable<Actor> ActorsInBox(Rectangle r)
|
|
{
|
|
return partitionedActors.InBox(r).Where(actorIsInWorld);
|
|
}
|
|
|
|
public IEnumerable<FrozenActor> FrozenActorsInBox(Player p, int2 a, int2 b)
|
|
{
|
|
return FrozenActorsInBox(p, RectWithCorners(a, b));
|
|
}
|
|
|
|
public IEnumerable<FrozenActor> FrozenActorsInBox(Player p, Rectangle r)
|
|
{
|
|
if (p == null)
|
|
return NoFrozenActors;
|
|
return partitionedFrozenActors[p].InBox(r).Where(frozenActorIsValid);
|
|
}
|
|
}
|
|
}
|