using System;
using System.Collections.Generic;
using System.Linq;
namespace Otter {
///
/// Class used to manage Entities. The active Game should update the active Scene, which then updates
/// all of the contained Entities.
///
public class Scene {
#region Private Fields
List entitiesToAdd = new List();
List entitiesToRemove = new List();
List entitiesToRemoveNextFrame = new List();
List entitiesToChangeLayer = new List();
List entitiesToChangeOrder = new List();
List groupsToPause = new List();
List groupsToUnpause = new List();
List pausedGroups = new List();
List graphics = new List();
SortedDictionary> orders = new SortedDictionary>();
SortedDictionary> layers = new SortedDictionary>();
internal Dictionary> Colliders = new Dictionary>();
List entities = new List();
Dictionary entitiesById = new Dictionary();
int nextEntityId = 0;
int entityCount = 0;
#endregion
#region Public Fields
///
/// The Glide instance for this Scene to control all tweens.
///
public Tweener Tweener = new Tweener();
///
/// The current time since this Scene has started.
///
public float Timer;
///
/// An action that triggers during Update().
///
public Action OnUpdate = delegate { };
///
/// An action that triggers during UpdateFirst().
///
public Action OnUpdateFirst = delegate { };
///
/// An action that triggers during UpdateLast().
///
public Action OnUpdateLast = delegate { };
///
/// An action that triggers during Render(), after all entities have been rendered.
///
public Action OnRender = delegate { };
///
/// An action that triggers during Begin().
///
public Action OnBegin = delegate { };
///
/// An action that triggers during End().
///
public Action OnEnd = delegate { };
///
/// An action that triggers when an entity is Added.
///
public Action OnAdd = delegate { };
///
/// An action that triggers when an entity is removed.
///
public Action OnRemove = delegate { };
///
/// An action that triggers when the Scene is paused because a Scene is stacked on top of it.
///
public Action OnPause = delegate { };
///
/// An action that triggers when the Scene is resumed because the active Scene on top of it was popped.
///
public Action OnResume = delegate { };
///
/// An action that triggers after the Scene has updated the camera positions for the Game's Surfaces.
///
public Action OnCameraUpdate = delegate { };
///
/// The angle of the camera.
///
public float CameraAngle;
///
/// The zoom of the camera.
///
public float CameraZoom = 1f;
///
/// The width of the scene.
///
public int Width;
///
/// The height of the scene.
///
public int Height;
///
/// Determines if the scene will control the game surface's camera.
///
public bool ApplyCamera = true;
///
/// A reference back to the current scene being run by the game.
///
public static Scene Instance;
///
/// Determines if scenes below this scene on the stack are allowed to render.
///
public bool DrawScenesBelow = true;
///
/// The bounds that the camera should be clamped inside.
///
public Rectangle CameraBounds;
///
/// Determines if the camera will be clamped inside the CameraBounds rectangle.
///
public bool UseCameraBounds = false;
///
/// The Entity that the Scene's camera will follow.
///
public Entity CameraFocus;
///
/// Determines if the scene will render its graphics or not.
///
public bool Visible = true;
#endregion
#region Public Properties
///
/// A reference to the Game that owns this Scene.
///
public Game Game { get; internal set; }
///
/// The default surface to render the scene's graphics to. If null then render
/// to the default game surface.
///
public Surface Surface {
get {
if (Surfaces == null) return null;
if (Surfaces.Count == 0) return null;
return Surfaces[Surfaces.Count - 1];
}
set {
Surfaces.Clear();
Surfaces.Add(value);
}
}
///
/// The list of surfaces the Scene should render to.
///
public List Surfaces { get; private set; }
///
/// Half of the scene's width.
///
public float HalfWidth {
private set { }
get { return Width / 2; }
}
///
/// Half of the scene's height.
///
public float HalfHeight {
private set { }
get { return Height / 2; }
}
public Vector2 Center {
get {
return new Vector2(HalfWidth, HalfHeight);
}
}
///
/// A reference to the Input from the Game controlling this scene.
///
public Input Input {
get { return Game.Input; }
}
///
/// The current number of entities in the scene.
///
public int EntityCount {
get { return entityCount; }
}
///
/// The current mouse X position in relation to the scene space.
///
public float MouseX {
get { return Input.MouseX + CameraX; }
}
///
/// The current mouse Y position in relation to the scene space.
///
public float MouseY {
get { return Input.MouseY + CameraY; }
}
///
/// The current raw mouse X position in relation to the scene space.
///
public float MouseRawX {
get { return Input.MouseRawX + CameraX; }
}
///
/// The current raw mouse Y position in relation to the scene space.
///
public float MouseRawY {
get { return Input.MouseRawY + CameraY; }
}
///
/// The X position of the camera in the scene.
///
public float CameraX {
get {
return cameraX;
}
set {
cameraX = value;
if (UseCameraBounds) {
cameraX = Util.Clamp(cameraX, CameraBounds.Left, CameraBounds.Right - CameraWidth);
}
}
}
///
/// The X position of the center of the camera.
///
public float CameraCenterX {
get { return cameraX + Game.HalfWidth; }
}
///
/// The Y position of the center of the camera.
///
public float CameraCenterY {
get { return cameraY + Game.HalfHeight; }
}
///
/// The Y position of the camera in the scene.
///
public float CameraY {
get {
return cameraY;
}
set {
cameraY = value;
if (UseCameraBounds) {
cameraY = Util.Clamp(cameraY, CameraBounds.Top, CameraBounds.Bottom - CameraHeight);
}
}
}
///
/// The width in pixels that the camera is showing with the current zoom.
///
public float CameraWidth {
get {
return Game.Width / CameraZoom;
}
}
///
/// The height in pixels that the camera is showing with the current zoom.
///
public float CameraHeight {
get {
return Game.Height / CameraZoom;
}
}
///
/// The bounds of the Scene as a Rectangle.
///
public Rectangle Bounds {
get {
return new Rectangle(0, 0, Width, Height);
}
}
///
/// A reference to the debugger object from the game that owns this scene.
///
public Debugger Debugger {
get { return Game.Debugger; }
}
#endregion
#region Constructors
///
/// Create a Scene with a specified width and height. If the width and height are not defined they
/// will be inferred by the Game class that uses the Scene.
///
/// The width of the scene.
/// The height of the scene.
public Scene(int width = 0, int height = 0) {
Width = width;
Height = height;
Surfaces = new List();
CameraBounds = new Rectangle(0, 0, (int)width, (int)height);
}
#endregion
#region Private Methods
void RenderScene() {
if (Visible) {
foreach (var g in graphics) {
g.Render();
}
OnRender();
}
}
#endregion
#region Public Indexers
public Entity this[int id] {
get {
if (entitiesById.ContainsKey(id)) return entitiesById[id];
return null;
}
}
#endregion
#region Public Methods
///
/// A handy shortcut for casting the Scene as a specific scene type.
/// For some reason I just like this better than doing (Scene as Type).Whatever();
///
/// The type of scene.
/// The scene as that type.
public T As() where T : Scene {
return (T)this;
}
///
/// Centers the camera of the scene.
///
/// The x coordinate to be the center of the scene.
/// The y coordinate to be the center of the scene.
public void CenterCamera(float x, float y) {
CameraX = x - Game.HalfWidth;
CameraY = y - Game.HalfHeight;
}
///
/// Add an entity to the scene.
///
/// Adds a new entity
/// The added Entity.
public T Add(T e) where T : Entity {
if (e == null) throw new ArgumentNullException("Entity cannot be null.");
if (e.Scene != null) return e;
entitiesToAdd.Add(e);
e.Scene = this;
e.MarkedForRemoval = false;
e.MarkedForAdd = true;
return e;
}
///
/// Create and add a new Entity to the Scene.
///
/// The Type of entity to add.
/// The constructor arguments for creating the Entity.
/// The created Entity.
public T Add(params object[] constructorArgs) where T : Entity {
return Add((T)Activator.CreateInstance(typeof(T), constructorArgs));
}
///
/// Add a list of Entities to the scene.
///
/// The type of Entity.
/// The list of Entities.
/// The list of Entities.
public List Add(List entities) where T : Entity {
foreach (var e in entities) {
Add(e);
}
return entities;
}
///
/// Adds an Entity only if no other Entities of that type exist in the Scene already.
///
/// The type of Entity.
/// The Entity to add.
/// The added Entity, or the Entity of type T that exists in the Scene already.
public T AddUnique(T e) where T : Entity {
if (GetEntity() == null) {
if (entitiesToAdd.Count(en => en is T) == 0) {
return Add(e);
}
else {
return (T)entitiesToAdd.Find(en => en is T);
}
}
return GetEntity();
}
///
/// Creates an adds an Entity to the Scene if there is no Entity of that type in the Scene already.
///
/// The type of Entity to create and Add.
/// The constructor arguments for creating the Entity.
/// The added Entity, or the Entity of type T that exists in the Scene already.
public T AddUnique(params object[] constructorArgs) where T : Entity {
return AddUnique((T)Activator.CreateInstance(typeof(T), constructorArgs));
}
///
/// Adds a list of Entities to the Scene if there is no Entity of that type added already.
///
/// The type of Entity
/// The list of Entities to AddUnique.
/// A list of the Entities that were successfully added.
public List AddUnique(List entities) where T : Entity {
var added = new List();
entities.ForEach(e => {
if (AddUnique(e) != null) {
added.Add(e);
}
});
return added;
}
///
/// Add multiple entities to the scene.
///
/// The entities to add.
/// A list of the entities.
public List AddMultiple(params Entity[] entities) {
var r = new List();
foreach (var e in entities) {
r.Add(Add(e));
}
return r;
}
///
/// Set the only graphic of the scene.
///
/// The graphic.
/// The graphic.
public T SetGraphic(T g) where T : Graphic {
graphics.Clear();
graphics.Add(g);
return g;
}
///
/// Adds a Graphic to the scene.
///
/// The Graphic.
/// The Graphic.
public T AddGraphic(T g) where T : Graphic {
graphics.Add(g);
return g;
}
///
/// Adds a Graphic to the Scene.
///
///
/// The Graphic to add.
/// The X position to place the Graphic.
/// The Y position to add the Graphic.
/// The added Graphic.
public T AddGraphic(T g, float x, float y) where T : Graphic {
graphics.Add(g);
g.SetPosition(x, y);
return g;
}
///
/// Add multiple graphics to the scene.
///
/// The graphics.
/// A list of the graphics added.
public List AddGraphics(params Graphic[] graphics) {
var r = new List();
foreach (var g in graphics) {
r.Add(AddGraphic(g));
}
return r;
}
///
/// Removes a Graphic from the scene.
///
/// The type (inferred from the parameter.)
/// The Graphic to remove.
/// The Graphic.
public T RemoveGraphic(T g) where T : Graphic {
graphics.Remove(g);
return g;
}
///
/// Removes all Graphics from the scene.
///
public void ClearGraphics() {
graphics.Clear();
}
///
/// Adds a Graphic to the Scene and sets its Scroll value to 0.
///
///
/// The Graphic to add.
/// The added Graphic.
public T AddGraphicGUI(T g) where T : Graphic {
g.Scroll = 0;
return AddGraphic(g);
}
///
/// Adds a Graphic to the Scene and sets its Scroll value to 0.
///
///
/// The Graphic to add.
/// The X position to place the Graphic.
/// The Y position to add the Graphic.
/// The added Graphic.
public T AddGraphicGUI(T g, float x, float y) where T : Graphic {
g.Scroll = 0;
g.SetPosition(x, y);
return AddGraphic(g);
}
///
/// Adds graphics to the Scene and sets their Scroll values to 0.
///
///
/// The graphics to add.
/// The added graphics.
public List AddGraphicsGUI(params Graphic[] graphics) {
var r = new List();
foreach (var g in graphics) {
r.Add(AddGraphicGUI(g));
}
return r;
}
///
/// Removes an entity from the scene.
///
/// The type (inferred from the parameter.)
/// The entity to remove.
/// The entity.
public T Remove(T e) where T : Entity {
if (e == null) throw new ArgumentNullException("Entity to remove cannot be null.");
if (e.MarkedForRemoval) return e;
if (e.Scene == null) return e;
if (!entitiesToAdd.Contains(e)) {
entitiesToRemove.Add(e); // Only add to entities to remove if it has been added already.
}
e.MarkedForRemoval = true;
e.MarkedForAdd = false;
return e;
}
///
/// Removes the first Entity of type T from the Scene.
///
/// The type of Entity to remove.
/// The removed Entity.
public T Remove() where T : Entity {
return Remove(GetEntity());
}
public void Remove(List entities) where T : Entity {
foreach (var e in entities) Remove(e);
}
///
/// Remove all entities from the scene.
///
public void RemoveAll() {
foreach (var e in entities) {
Remove(e);
}
}
public List RemoveMultiple(params Entity[] entities) {
var r = new List();
foreach (var e in entities) {
r.Add(Remove(e));
}
return r;
}
public T RemoveNextFrame(T e) where T : Entity {
if (e == null) throw new ArgumentNullException("Entity to remove cannot be null.");
if (e.MarkedForRemoval) return e;
if (e.Scene == null) return e;
if (!entitiesToAdd.Contains(e)) {
entitiesToRemoveNextFrame.Add(e);
}
//e.MarkedForRemoval = true;
e.MarkedForAdd = false;
return e;
}
///
/// Add a surface to the list of surfaces that the scene should render to.
/// This only applies to the Scene's graphics, NOT the entities in the scene.
///
///
public void AddSurface(Surface target) {
if (Surfaces == null) Surfaces = new List();
Surfaces.Add(target);
}
///
/// Remove a surface from the list of targets that the scene should render to.
///
///
public void RemoveSurface(Surface target) {
if (Surfaces == null) Surfaces = new List();
Surfaces.Remove(target);
}
///
/// Remove all surface targets and revert back to the default game surface.
///
public void ClearSurfaces() {
if (Surfaces == null) Surfaces = new List();
Surfaces.Clear();
}
///
/// Sends an Entity to the back of its layer. Probably don't use this and change the Entity's layer in the same update.
///
/// The Entity to modify.
public void SendToBack(Entity e) {
if (!layers.ContainsKey(e.Layer)) return;
if (!layers[e.Layer].Contains(e)) return;
layers[e.Layer].Remove(e);
layers[e.Layer].Insert(0, e);
}
///
/// Sends an Entity further back in its layer. Probably don't use this and change the Entity's layer in the same update.
///
/// The Entity to modify.
public void SendBackward(Entity e) {
if (!layers.ContainsKey(e.Layer)) return;
if (!layers[e.Layer].Contains(e)) return;
var oldIndex = layers[e.Layer].IndexOf(e);
if (oldIndex == 0) return;
layers[e.Layer].Remove(e);
layers[e.Layer].InsertOrAdd(oldIndex - 1, e);
}
///
/// Brings an Entity further forward in its layer. Probably don't use this and change the Entity's layer in the same update.
///
/// The Entity to modify.
public void BringForward(Entity e) {
if (!layers.ContainsKey(e.Layer)) return;
if (!layers[e.Layer].Contains(e)) return;
var oldIndex = layers[e.Layer].IndexOf(e);
if (oldIndex == layers[e.Layer].Count - 1) return;
layers[e.Layer].Remove(e);
layers[e.Layer].InsertOrAdd(oldIndex + 1, e);
}
///
/// Brings an Entity to the front of its layer. Probably don't use this and change the Entity's layer in the same update.
///
/// The Entity to modify.
public void BringToFront(Entity e) {
if (!layers.ContainsKey(e.Layer)) return;
if (!layers[e.Layer].Contains(e)) return;
layers[e.Layer].Remove(e);
layers[e.Layer].Add(e);
}
///
/// Called when the scene begins after being switched to, or added to the stack.
///
public virtual void Begin() {
}
///
/// Called when the scene ends after being switched away from, or removed from the stack.
///
public virtual void End() {
}
///
/// Called when the scene is paused because a new scene is stacked on it.
///
public virtual void Pause() {
}
///
/// Called when the scene resumes after a scene is added above it.
///
public virtual void Resume() {
}
///
/// The first update of the scene.
///
public virtual void UpdateFirst() {
}
///
/// The last update of the scene.
///
public virtual void UpdateLast() {
}
///
/// The main update loop of the scene.
///
public virtual void Update() {
}
///
/// Renders the scene. Graphics added to the scene render first.
/// Graphics drawn in Render() will render on top of all entities.
///
public virtual void Render() {
}
///
/// Update the internal lists stored by the scene. The engine will usually take care of this!
///
public void UpdateLists() {
while (entitiesToAdd.Count > 0) {
var adding = new List(entitiesToAdd);
entitiesToAdd.Clear();
foreach (var e in adding) {
if (e.MarkedForRemoval) continue;
if (!orders.ContainsKey(e.Order)) {
orders.Add(e.Order, new List());
}
orders[e.Order].Add(e);
if (!layers.ContainsKey(e.Layer)) {
layers.Add(e.Layer, new List());
}
layers[e.Layer].Add(e);
entities.Add(e);
var id = GetNextEntityId();
entitiesById.Add(id, e);
e.InstanceId = id;
e.MarkedForAdd = false;
foreach (var c in e.Colliders) {
AddColliderInternal(c);
}
entityCount++;
}
foreach (var e in adding) {
// Invoke these methods after *all* entities in the queue are actually in the scene.
e.UpdateComponentLists(); // trying this twice? this might break everything.
e.Added();
e.UpdateComponentLists(); // Add components after e.Added, so that Entity.Scene is not null for components.
e.OnAdded(); // Moved OnAdded after UpdateComponentLists so components can hook into OnAdded
}
}
foreach (var e in entitiesToChangeOrder) {
orders[e.oldOrder].Remove(e);
if (orders[e.oldOrder].Count == 0) {
orders.Remove(e.oldOrder);
}
if (!orders.ContainsKey(e.Order)) {
orders.Add(e.Order, new List());
}
orders[e.Order].Add(e);
}
entitiesToChangeOrder.Clear();
foreach (var e in entitiesToChangeLayer) {
layers[e.oldLayer].Remove(e);
if (layers[e.oldLayer].Count == 0) {
layers.Remove(e.oldLayer);
}
if (!layers.ContainsKey(e.Layer)) {
layers.Add(e.Layer, new List());
}
layers[e.Layer].Add(e);
}
entitiesToChangeLayer.Clear();
while (entitiesToRemove.Count > 0) {
var removing = new List(entitiesToRemove);
entitiesToRemove.Clear();
foreach (var e in removing) {
orders[e.Order].Remove(e);
if (orders[e.Order].Count == 0) {
orders.Remove(e.Order);
}
layers[e.Layer].Remove(e);
if (layers[e.Layer].Count == 0) {
layers.Remove(e.Layer);
}
entities.Remove(e);
entitiesById.Remove(e.InstanceId);
e.InstanceId = -1;
foreach (var c in e.Colliders) {
RemoveColliderInternal(c);
}
entityCount--;
}
foreach (var e in removing) {
e.Removed();
e.OnRemoved();
e.Scene = null;
}
}
foreach (var e in entitiesToRemoveNextFrame) {
Remove(e);
}
entitiesToRemoveNextFrame.Clear();
foreach (var group in groupsToPause) {
if (!pausedGroups.Contains(group)) {
foreach (var order in orders) {
foreach (var e in order.Value) {
if (e.Group == group) {
e.Paused();
}
}
}
pausedGroups.Add(group);
}
}
groupsToPause.Clear();
foreach (var group in groupsToUnpause) {
if (IsGroupPaused(group)) {
foreach (var order in orders) {
foreach (var e in order.Value) {
if (e.Group == group) {
e.Resumed();
}
}
}
pausedGroups.Remove(group);
}
}
groupsToUnpause.Clear();
}
///
/// Tweens a set of numeric properties on an object.
///
/// The object to tween.
/// The values to tween to, in an anonymous type ( new { prop1 = 100, prop2 = 0} ).
/// Duration of the tween in seconds.
/// Delay before the tween starts, in seconds.
/// The tween created, for setting properties on.
public Tween Tween(object target, object values, float duration, float delay = 0) {
return Tweener.Tween(target, values, duration, delay);
}
///
/// Pause a group of entities.
///
/// The group to pause.
public void PauseGroup(int group) {
if (groupsToUnpause.Contains(group)) {
groupsToUnpause.Remove(group);
}
else {
groupsToPause.Add(group);
}
}
///
/// Resume a paused group of entities.
///
/// The group to resume.
public void ResumeGroup(int group) {
if (!IsGroupPaused(group)) return;
if (groupsToPause.Contains(group)) {
groupsToPause.Remove(group);
}
else {
groupsToUnpause.Add(group);
}
}
///
/// Pause or resume a group of entities. If paused, resume. If running, pause.
///
/// The group to toggle.
public void PauseGroupToggle(int group) {
if (IsGroupPaused(group)) {
ResumeGroup(group);
}
else {
PauseGroup(group);
}
}
///
/// If a group of entities is currently paused. Note that pausing wont happen until the next update
/// aftering calling pause.
///
/// The group to check.
/// True if the group is paused.
public bool IsGroupPaused(int group) {
if (groupsToPause.Contains(group)) {
return true;
}
return pausedGroups.Contains(group);
}
///
/// Returns a list of all the entities in the given group.
///
/// The group to get.
/// All of the entities in the group.
public List GetEntitiesInGroup(int group) {
return entities.Where(e => e.Group == group).ToList();
}
///
/// Returns a list of all the Entities in the given layer.
///
/// The layer to get.
/// All of the entities in the group.
public List GetEntitiesInLayer(int layer) {
if (layers.ContainsKey(layer)) {
return layers[layer];
}
return new List();
}
///
/// Get a list of entities of type T from the Scene.
///
/// The type of entity to collect.
/// A list of entities of type T.
public List GetEntities() where T : Entity {
if (typeof(T) == typeof(Entity)) {
return entities.Cast().ToList();
}
var list = new List();
foreach (var e in entities) {
if (e is T) {
list.Add(e as T);
}
}
return list;
}
public List GetEntitiesAll() {
return entities.ToList();
}
///
/// Get a list of Entities of a type from the Scene.
///
/// The type of Entity to list.
/// A list of Entities of type t.
public List GetEntities(Type t) {
return entities.Where(e => e.GetType() == t).ToList();
}
///
/// Get the first instance of an Entity of type T.
///
/// The entity type to search for.
/// The first entity of that type in the scene.
public T GetEntity() where T : Entity {
foreach (var e in entities) {
if (e is T) {
return (e as T);
}
}
return null;
}
///
/// Get a list of Entities that have a Collider that matches a specified tag.
///
/// The tag to search for.
/// Entities that have a Collider with that tag.
public List GetEntities(int colliderTag) {
var list = new List();
GetColliders(colliderTag).ForEach(c => list.Add(c.Entity));
return list;
}
///
/// Get a list of Entities that have a Collider that matches a specified tag.
///
/// The tag to search for.
/// Entities that have a Collider with that tag.
public List GetEntities(Enum colliderTag) {
return GetEntities(Convert.ToInt32(colliderTag));
}
public List GetEntitiesWith()
where T1 : Component {
return entities
.Where(e => e.GetComponent() != null)
.ToList();
}
public List GetEntitiesWith()
where T1 : Component
where T2 : Component{
return entities
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.ToList();
}
public List GetEntitiesWith()
where T1 : Component
where T2 : Component
where T3 : Component{
return entities
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.ToList();
}
public List GetEntitiesWith()
where T1 : Component
where T2 : Component
where T3 : Component
where T4 : Component {
return entities
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.ToList();
}
public List GetEntitiesWith()
where T1 : Component
where T2 : Component
where T3 : Component
where T4 : Component
where T5 : Component {
return entities
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.ToList();
}
public List GetEntitiesWith()
where T1 : Component
where T2 : Component
where T3 : Component
where T4 : Component
where T5 : Component
where T6 : Component {
return entities
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.Where(e => e.GetComponent() != null)
.ToList();
}
///
/// Get a list of Colliders that match a specified tag.
///
/// The tag to search for.
/// Colliders that have the specified tag.
public List GetColliders(int tag) {
return Colliders[tag].ToList();
}
///
/// Get a list of Colliders that match a specified tag.
///
/// The tag to search for.
/// Colliders that have the specified tag.
public List GetColliders(Enum tag) {
return GetColliders(Convert.ToInt32(tag));
}
///
/// Get the top most Entity in the rendering order from a set of Entities.
///
/// The set of Entities to evaluate.
/// The top most Entity in the set.
public Entity GetTopEntity(params Entity[] entities) {
if (entities.Length == 0) return null;
if (entities.Length == 1) return entities[0];
var validEntities = entities.Where(e => e.Scene == this);
if (validEntities.Count() == 0) return null;
var min = validEntities.Min(e => e.Layer);
var minIndex = validEntities.Where(e => e.Layer == min).Max(e => layers[min].IndexOf(e));
return layers[min][minIndex];
}
///
/// Get the bottom most Entity in the rendering order from a set of Entities.
///
/// The set of Entities to evaluate.
/// The bottom most Entity in the set.
public Entity GetBottomEntity(params Entity[] entities) {
if (entities.Length == 0) return null;
if (entities.Length == 1) return entities[0];
var validEntities = entities.Where(e => e.Scene == this);
if (validEntities.Count() == 0) return null;
var max = validEntities.Max(e => e.Layer);
var maxIndex = validEntities.Where(e => e.Layer == max).Min(e => layers[max].IndexOf(e));
return layers[max][maxIndex];
}
///
/// Count how many entities of type T are in this Scene.
///
/// The type of entity to count.
/// The number of entities of type T.
public int GetCount() where T : Entity {
var count = 0;
foreach (var e in entities) {
if (e is T) {
count++;
}
}
return count;
}
#endregion
#region Internal
internal float
cameraX,
cameraY;
internal int GetNextEntityId() {
var id = nextEntityId;
nextEntityId++;
return id;
}
internal void AddColliderInternal(Collider c) {
foreach (var tag in c.Tags) {
if (Colliders.ContainsKey(tag)) {
if (!Colliders[tag].Contains(c)) { // Quick fix to prevent double adding.
Colliders[tag].Add(c);
}
}
else {
Colliders[tag] = new List();
Colliders[tag].Add(c);
}
}
}
internal void RemoveColliderInternal(Collider c) {
foreach (var tag in c.Tags) {
if (Colliders.ContainsKey(tag)) {
Colliders[tag].Remove(c);
if (Colliders[tag].Count == 0) {
Colliders.Remove(tag);
}
}
}
}
internal void BeginInternal() {
Instance = this;
foreach (var e in entitiesToAdd) {
e.SceneBegin();
}
Game.OnSceneBegin();
OnBegin();
if (Width == 0 || Height == 0) {
Width = Game.Width;
Height = Game.Height;
}
Begin();
}
internal void EndInternal() {
foreach (var order in orders) {
foreach (var e in order.Value) {
e.SceneEnd();
}
}
Game.OnSceneEnd();
OnEnd();
End();
UpdateLists(); // Testing this
}
internal void PauseInternal() {
foreach (var order in orders) {
foreach (var e in order.Value) {
e.ScenePause();
}
}
OnPause();
Pause();
}
internal void ResumeInternal() {
Instance = this;
foreach (var order in orders) {
foreach (var e in order.Value) {
e.SceneResume();
}
}
OnResume();
Resume();
}
internal void UpdateFirstInternal() {
OnUpdateFirst();
UpdateFirst();
foreach (var order in orders) {
foreach (var e in order.Value) {
if (e.AutoUpdate) {
if (!IsGroupPaused(e.Group)) {
e.UpdateFirstInternal();
}
}
}
}
}
internal void UpdateLastInternal() {
OnUpdateLast();
UpdateLast();
foreach (var order in orders) {
foreach (var e in order.Value) {
if (e.AutoUpdate) {
if (!IsGroupPaused(e.Group)) {
e.UpdateLastInternal();
}
}
if (e.Order != order.Key) {
entitiesToChangeOrder.Add(e);
e.oldOrder = order.Key;
}
}
}
foreach (var layer in layers.Reverse()) {
foreach (var e in layer.Value) {
if (e.Layer != layer.Key) {
entitiesToChangeLayer.Add(e);
e.oldLayer = layer.Key;
}
}
}
foreach (Graphic g in graphics) {
g.Update();
}
if (UseCameraBounds) {
CameraX = Util.Clamp(CameraX, CameraBounds.Left, CameraBounds.Right - Game.Width);
CameraY = Util.Clamp(CameraY, CameraBounds.Top, CameraBounds.Bottom - Game.Height);
}
foreach (var g in graphics) {
g.Update();
}
UpdateCamera();
Timer += Game.DeltaTime;
}
internal void UpdateCamera() {
if (CameraFocus != null) {
CameraX = CameraFocus.X - Game.HalfWidth;
CameraY = CameraFocus.Y - Game.HalfHeight;
}
var cx = CameraX;
var cy = CameraY;
if (Debugger.Instance != null) {
if (Debugger.IsOpen) {
cx += Debugger.DebugCameraX;
cy += Debugger.DebugCameraY;
}
}
if (ApplyCamera) {
Game.Surfaces.FindAll(s => s.UseSceneCamera).ForEach(s => s.SetView(Util.Round(cx), Util.Round(cy), CameraAngle, CameraZoom));
Game.Surface.SetView(Util.Round(cx), Util.Round(cy), CameraAngle, CameraZoom);
}
OnCameraUpdate();
}
internal void UpdateInternal() {
OnUpdate();
Tweener.Update(Game.DeltaTime);
Update();
foreach (var order in orders) {
foreach (var e in order.Value) {
if (e.AutoUpdate) {
if (!IsGroupPaused(e.Group)) {
e.UpdateInternal();
}
}
}
}
}
internal void RenderInternal() {
//Render scene graphics behind everything (Scenery!)
if (Surface == null) {
RenderScene();
}
else {
Surface temp = Draw.Target;
foreach (var surface in Surfaces) {
Draw.SetTarget(surface);
RenderScene();
}
Draw.SetTarget(temp);
}
foreach (var layer in layers.Reverse()) {
foreach (var e in layer.Value) {
if (e.AutoRender && e.Visible) {
e.RenderInternal();
}
}
}
if (Surface == null) {
Render();
}
else {
Surface temp = Draw.Target;
foreach (var surface in Surfaces) {
Draw.SetTarget(surface);
Render();
}
Draw.SetTarget(temp);
}
}
#endregion
}
}