
In most games, you'll need to know where everything is.
There are typically two ways to find things: by type ("where are all radar detectors") and by area ("what things are within 20 meters of me"). In a typical game engine, this job is done by a spatial index. Sometimes, that spatial index is shared with graphics, typically in a scene graph setting. Other times, that spatial index is shared with physics, typically in a collision detection setting. Yet other times, there's a totally separate entity index, which is separate from graphics and physics. I prefer the totally separate index.
From the point of view of users, the interface for the index is something like:
public class SpatialIndex {
public IIndexed AddEntity(object o, Vector3 position);
public void CallForThingsInRange(Vector3 center, float radius, EntityCallback cb);
public void CallForThingsOfType(Type type, EntityCallback cb);
}
public interface IIndexed {
void SetPosition(Vector3 pos);
void Remove();
}
public delegate void EntityCallback(object o);
For simple games, you can implement this with a simple List<Indexed> where Indexed contains the object value and the position. When you get lots of entities (say, more than 50), you'll have to start keeping separate indices by type queried (say, typeof(Player)) and by location (say as a Quadtree or Octree).
A sketch of a really simple implementation is below. Each entity can register itself in the index by saying:myIndex = SpatialIndex.Global.AddEntity(this, myPos);
The entity will then keep track of its registration using myIndex, and update itself in the index when it moves by calling
myIndex.SetPosition(myPos);
Finally, when the entity dies, it will remove itself by calling:
myIndex.Remove();
| public class SpatialIndex |
| { |
| public static SpatialIndex Global = new SpatialIndex(); |
| public IIndexed AddEntity(object o, Vector3 position) |
| { |
| Indexed i = new Indexed(this, o, position); |
| items.Add(i); |
| return i; |
| } |
| public void CallForThingsInRange(Vector3 center, float radius, EntityCallback cb) |
| { |
| float r2 = radius*radius; |
| foreach (Indexed i in items) |
| if ((i.pos - center).LengthSq() <= r2) |
| cb(i.o); |
| } |
| public void CallForThingsOfType(Type type, EntityCallback cb) |
| { |
| foreach (Indexed i in items) |
| if (type.IsAssignableFrom(i.o)) |
| cb(i.o); |
| } |
| class Indexed : IIndexed |
| { |
| internal Indexed(SpatialIndex owner, object o, Vector3 pos) |
| { |
| this.owner = owner; |
| this.o = o; |
| this.pos = pos; |
| } |
| SpatialIndex owner; |
| object o; |
| Vector3 pos; |
| public void SetPosition(Vector3 pos) |
| { |
| this.pos = pos; |
| } |
| public void Remove() |
| { |
| owner.items.Remove(this); |
| } |
| } |
| List<Indexed> items = new List<Indexed>(); |
| } |
| public interface IIndexed { |
| void SetPosition(Vector3 pos); |
| void Remove(); |
| } |
| public delegate void EntityCallback(object o); |