using System;
using System.Collections.Generic;
namespace Otter {
///
/// Class used for a game object. The bread and butter of your game. Entities are added to Scenes which are controlled by the Game.
///
public class Entity {
#region Public Fields
///
/// The X position of the Entity.
///
public float X;
///
/// The Y position of the Entity.
///
public float Y;
///
/// How long the Entity has been active.
///
public float Timer;
///
/// Determines if the Entity will render.
///
public bool Visible = true;
///
/// Determines if the Entity will collide with other entities. The entity can still check for
/// collisions, but will not register as a collision with other entities.
///
public bool Collidable = true;
///
/// Deteremines if the Entity's update functions will run automatically from the Scene.
///
public bool AutoUpdate = true;
///
/// Determines if the Entity's render functions will run automatically from the Scene.
///
public bool AutoRender = true;
///
/// The tween manager that controls all tweens on this entity.
///
public Tweener Tweener = new Tweener();
///
/// An action that fires when the entity is added to a Scene.
///
public Action OnAdded = delegate { };
///
/// An action that fires when the entity is updated.
///
public Action OnUpdate = delegate { };
///
/// An action that fires in the entity's UpdateFirst().
///
public Action OnUpdateFirst = delegate { };
///
/// An action that is fired in the entity's UpdateLast().
///
public Action OnUpdateLast = delegate { };
///
/// An action that fires when the entity is removed from a Scene.
///
public Action OnRemoved = delegate { };
///
/// An action that fires when the entity is rendered.
///
public Action OnRender = delegate { };
///
/// The name of this entity. Default's to the Type name.
///
public string Name;
///
/// The order in which to render this entity. Higher numbers draw later.
///
public int Layer;
///
/// The order in which to update this entity. Higher numbers update later.
///
public int Order;
///
/// The pause group this entity is a part of.
///
public int Group;
///
/// How long the entity should live in the scene before removing itself. If this is set the
/// entity will be automatically removed when the Timer exceeds this value.
///
public float LifeSpan;
///
/// Is true if the Entity has been updated by the Scene at least once.
///
public bool UpdatedOnce { get; private set; }
#endregion Public Fields
#region Public Indexers
public Component this[int id] {
get {
if (componentsById.ContainsKey(id)) return componentsById[id];
return null;
}
}
#endregion
#region Internal Fields
internal bool MarkedForRemoval = false;
internal bool MarkedForAdd = false;
internal int
oldLayer,
oldOrder;
#endregion Internal Fields
#region Private Fields
private List componentsToRemove = new List();
private List componentsToAdd = new List();
Dictionary componentsById = new Dictionary();
int nextComponentId;
#endregion Private Fields
#region Public Constructors
///
/// Create an entity.
///
/// The x position to place the entity.
/// The y position to place the entity.
/// The graphic to assign to the entity. Defaults to null.
/// The collider to assign to the entity. Defaults to null.
/// The name of the entity. Defaults to the type name.
public Entity(float x = 0, float y = 0, Graphic graphic = null, Collider collider = null, string name = "") {
X = x;
Y = y;
InstanceId = -1;
Graphics = new List();
Components = new List();
Colliders = new List();
Surfaces = new List();
if (graphic != null) {
Graphic = graphic;
}
if (collider != null) {
Collider = collider;
}
if (name == "") {
Name = this.GetType().Name;
}
else {
Name = name;
}
}
#endregion Public Constructors
#region Public Properties
///
/// The list of graphics to render.
///
public List Graphics { get; private set; }
///
/// The list of components to update and render.
///
public List Components { get; private set; }
///
/// The list of colliders to use for collision checks.
///
public List Colliders { get; private set; }
///
/// The list of surfaces the entity should draw to.
///
public List Surfaces { get; private set; }
///
/// The Scene that controls and updates this entity.
///
public Scene Scene { get; internal set; }
///
/// The int id of the Entity for the Scene its currently in.
///
public int InstanceId { get; internal set; }
///
/// Returns true if the entity is currently in a Scene, or is queued to be added to a Scene next update.
///
public bool IsInScene { get { return Scene != null; } }
///
/// The default Surface that the entity should render to.
///
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 currently overlapped entity. This only works when using an Overlap collision check, and there is a result.
///
public Entity Overlapped { get; private set; }
///
/// Set to a collider by using the SetHitbox method. Shortcut reference.
///
public BoxCollider Hitbox { get; private set; }
///
/// Returns the first available collider, or set the Collider.
///
public Collider Collider {
get {
if (Colliders.Count == 0) return null;
return Colliders[0];
}
set { SetCollider(value); }
}
///
/// A reference to the Input object in the Game that controls the Scene.
///
public Input Input {
get { return Scene.Game.Input; }
}
///
/// A reference to the Game that controls the Scene.
///
public Game Game {
get { return Scene.Game; }
}
///
/// If the entity is currently paused by the scene.
///
public bool IsPaused {
get {
if (Scene != null) {
return Scene.IsGroupPaused(Group);
}
return false;
}
}
///
/// The x position in screen space of the entity.
///
public float ScreenX {
get {
if (Scene != null) {
return X - Scene.CameraX;
}
return X;
}
}
///
/// The y position in screen space of the entity.
///
public float ScreenY {
get {
if (Scene != null) {
return Y - Scene.CameraY;
}
return Y;
}
}
///
/// Returns the first available graphic, or set the graphic.
///
public Graphic Graphic {
get {
if (Graphics.Count == 0) return null;
return Graphics[0];
}
set { SetGraphic(value); }
}
///
/// The position of the Entity represented as a Vector2
///
public Vector2 Position {
get {
return new Vector2(X, Y);
}
set {
X = value.X;
Y = value.Y;
}
}
#endregion Public Properties
#region Public Methods
///
/// Sets the graphic to a new graphic, removing all previous graphics.
///
///
public void SetGraphic(Graphic g) {
Graphics.Clear();
Graphics.Add(g);
}
///
/// Set the X and Y position to a value.
///
/// The value of the X and Y position.
public void SetPosition(float xy) {
X = Y = xy;
}
///
/// Add to the X and Y positions of the Entity.
///
/// The amount to add to the x position.
/// The amount to add to the y position.
public void AddPosition(float x, float y) {
X += x;
Y += y;
}
///
/// Add to the X and Y position of the Entity.
///
/// The axis to add from.
/// The amount to muliply the axis values by before adding.
public void AddPosition(Axis axis, float multiplier = 1) {
AddPosition(axis.X * multiplier, axis.Y * multiplier);
}
///
/// Add to the X and Y position of the Entity.
///
/// The Vector2 to add to the position.
public void AddPosition(Vector2 vector) {
AddPosition(vector.X, vector.Y);
}
///
/// Set the position of the Entity.
///
/// The new x position.
/// The new y position.
public void SetPosition(float x, float y) {
X = x;
Y = y;
}
///
/// Set the position of the Entity to another Entity's position.
///
/// The Entity to match positions with.
public void SetPosition(Entity e, float offsetX = 0, float offsetY = 0) {
SetPosition(e.X + offsetX, e.Y + offsetY);
}
///
/// Set the position of the Entity.
///
/// The vector of the new position.
public void SetPosition(Vector2 v) {
SetPosition(v.X, v.Y);
}
///
/// Adds a Graphic to the Entity.
///
/// The Graphic to add.
/// The added Graphic.
public T AddGraphic(T g) where T : Graphic {
Graphics.Add(g);
return g;
}
///
/// Adds a Graphic to the Entity.
///
///
/// The Graphic to add.
/// The X position to place the Graphic relative to the Entity.
/// The Y position to place the Graphic relative to the Entity.
/// The added Graphic.
public T AddGraphic(T g, float x, float y) where T : Graphic {
Graphics.Add(g);
g.X = x;
g.Y = y;
return g;
}
///
/// Adds the graphics to the Entity.
///
/// The Graphics to add.
public List AddGraphics(params Graphic[] graphics) {
var r = new List();
foreach (var g in graphics) {
r.Add(AddGraphic(g));
}
return r;
}
///
/// Adds a graphic to the Entity 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 Entity and sets its Scroll value to 0.
///
///
/// The Graphic to add.
/// The X position to place the Graphic relative to the Entity.
/// The Y position to place the Graphic relative to the Entity.
/// The added Graphic.
public T AddGraphicGUI(T g, float x, float y) where T : Graphic {
g.Scroll = 0;
return AddGraphic(g, x, y);
}
///
/// Adds Graphics to the Entity 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 a Graphic from the Entity.
///
/// The Graphic to remove.
public T RemoveGraphic(T g) where T : Graphic {
Graphics.Remove(g);
return g;
}
///
/// Removes Graphics from the Entity.
///
/// The Graphics to remove.
public void RemoveGraphics(params Graphic[] g) {
g.Each(gr => RemoveGraphic(gr));
}
///
/// Remove all the graphics from the entity.
///
public void ClearGraphics() {
Graphics.Clear();
}
///
/// Adds a component to the entity.
///
///
public T AddComponent(T c) where T : Component {
if (c.Entity != null) return c;
c.Entity = this;
componentsToAdd.Add(c);
return c;
}
///
/// Creates and adds a Component to the entity.
///
/// The type of Component to create.
/// The arguments for the Component's constructor.
/// The newly created Component.
public T AddComponent(params object[] constructorArgs) where T : Component {
var c = (T)Activator.CreateInstance(typeof(T), constructorArgs);
return AddComponent(c);
}
///
/// Add multiple components to the entity.
///
/// The components to add.
/// A list of the added components.
public List AddComponents(params Component[] c) {
var r = new List();
foreach (var com in c) {
r.Add(AddComponent(com));
}
return r;
}
///
/// Removes a component from the Entity.
///
///
public T RemoveComponent(T c) where T : Component {
if (componentsToAdd.Contains(c)) {
componentsToAdd.Remove(c);
return c;
}
componentsToRemove.Add(c);
return c;
}
///
/// Removes the first component of type T from the Entity.
///
/// The type of component to remove.
/// The removed component.
public T RemoveComponent() where T : Component {
return RemoveComponent(GetComponent());
}
///
/// Remove all components from the Entity.
///
public void ClearComponents() {
foreach (var c in Components) {
RemoveComponent(c);
}
}
///
/// Add a surface that the entity should render to.
///
///
public void AddSurface(Surface target) {
Surfaces.Add(target);
}
///
/// Remove a surface from the list of surfaces that the entity should render to.
///
///
public void RemoveSurface(Surface target) {
Surfaces.Remove(target);
}
///
/// Remove all Surfaces from the list of Surfaces that the Entity should render to.
///
public void ClearSurfaces() {
Surfaces.Clear();
}
///
/// Shortcut to set the Collider of the entity to a BoxCollider. Using this will set the "Hitbox" field to this Collider.
///
/// The width of the box collider.
/// The height of the box collider.
/// The tags assigned to the box collider.
/// The created box collider.
public BoxCollider SetHitbox(int width, int height, params int[] tags) {
var hitbox = new BoxCollider(width, height, tags);
SetCollider(hitbox);
Hitbox = hitbox;
return hitbox;
}
///
/// Shortcut to set the Collider of the entity to a BoxCollider. Using this will set the "Hitbox" field to this Collider.
///
/// The width of the box collider.
/// The height of the box collider.
/// The first tag to add.
/// The rest of the tags to add.
///
public BoxCollider SetHitbox(int width, int height, Enum tag, params Enum[] tags) {
var hitbox = new BoxCollider(width, height, tag, tags);
SetCollider(hitbox);
Hitbox = hitbox;
return hitbox;
}
///
/// Get the first instance of an Entity of type T in this Entity's Scene.
///
/// The entity type to search for.
/// The first entity of that type in the scene.
public T GetEntity() where T : Entity {
return Scene.GetEntity();
}
///
/// Get a list of entities of type T from this Entity's Scene.
///
/// The type of entity to collect.
/// A list of entities of type T.
public List GetEntities() where T : Entity {
return Scene.GetEntities();
}
///
/// Get the first Component of type T.
///
/// The type of Component to look for.
/// The Component.
public T GetComponent() where T : Component {
foreach (var c in Components) {
if (c is T) return (T)c;
}
foreach (var c in componentsToAdd) {
if (c is T) return (T)c;
}
return null;
}
///
/// Get the first Component of Type type.
///
/// The Type of Component to look for.
/// The first Component of that Type.
public Component GetComponent(Type type) {
foreach (var c in Components) {
if (c.GetType() == type) return c;
}
return null;
}
///
/// Get all Components of type T.
///
/// The type of Component to look for.
/// A list of Components of type T.
public List GetComponents() where T : Component {
var list = new List();
foreach (var c in Components) {
if (c is T) list.Add((T)c);
}
foreach (var c in componentsToAdd) {
if (c is T) list.Add((T)c);
}
return list;
}
///
/// Get the first graphic of type T.
///
/// The type of graphic to look for.
/// The graphic.
public T GetGraphic() where T : Graphic {
foreach (var g in Graphics) {
if (g is T) return (T)g;
}
return null;
}
///
/// Get the first collider of type T.
///
/// The type of collider to look for.
/// The collider.
public T GetCollider() where T : Collider {
foreach (var c in Colliders) {
if (c is T) return (T)c;
}
return null;
}
///
/// Add a collider to the entity.
///
///
public T AddCollider(T c) where T : Collider {
Colliders.Add(c);
c.Entity = this;
c.Added();
if (Scene != null) {
Scene.AddColliderInternal(c);
}
return c;
}
///
/// Remove the collider from the entity.
///
///
public T RemoveCollider(T c) where T : Collider {
if (Colliders.Contains(c)) {
Colliders.Remove(c);
c.Removed();
c.Entity = null;
if (Scene != null) {
Scene.RemoveColliderInternal(c);
}
}
return c;
}
///
/// Remove all colliders from the entity.
///
public void ClearColliders() {
var colliders = new List(Colliders);
foreach (var c in colliders) {
RemoveCollider(c);
}
Hitbox = null;
}
///
/// Remove all current colliders and set collider to a new one.
///
///
public T SetCollider(T c) where T : Collider {
ClearColliders();
return AddCollider(c);
}
///
/// Adds colliders to the entity.
///
///
public List AddColliders(params Collider[] colliders) {
var r = new List();
foreach (var c in colliders) {
r.Add(AddCollider(c));
}
return r;
}
///
/// Removes colliders from the entity.
///
///
public void RemoveColliders(params Collider[] colliders) {
foreach (var c in colliders) {
RemoveCollider(c);
}
}
///
/// Checks for a collision using the first available Collider.
///
/// The X position to check for a collision at.
/// The Y position to check for a collision at.
/// The int tags to check for.
///
public Collider Collide(float x, float y, params int[] tags) {
return Collider.Collide(x, y, tags);
}
///
/// Checks for a collision using the first available Collider.
///
/// The X position to check for a collision at.
/// The Y position to check for a collision at.
/// The enum tags to check for.
///
public Collider Collide(float x, float y, params Enum[] tags) {
return Collider.Collide(x, y, tags);
}
///
/// Checks for a collision with the first available Collider.
///
/// The X position to check for a collision at.
/// The Y position to check for a collision at.
/// The int tags to check for.
/// A list of all colliders touched.
public List CollideList(float x, float y, params int[] tags) {
return Collider.CollideList(x, y, tags);
}
///
/// Checks for a collision with the first available Collider.
///
/// The X position to check for a collision at.
/// The Y position to check for a collision at.
/// The enum tags to check for.
/// A list of all colliders touched.
public List CollideList(float x, float y, params Enum[] tags) {
return Collider.CollideList(x, y, tags);
}
///
/// Checks for a collision with the first available Collider.
///
/// The X position to check for a collision at.
/// The Y position to check for a collision at.
/// The int tags to check for.
/// A list of all entities touched.
public List CollideEntities(float x, float y, params int[] tags) {
return Collider.CollideEntities(x, y, tags);
}
///
/// Checks for a collision with the first available Collider.
///
/// The X position to check for a collision at.
/// The Y position to check for a collision at.
/// The enum tags to check for.
/// A list of all Entities touched.
public List CollideEntities(float x, float y, params Enum[] tags) {
return Collider.CollideEntities(x, y, tags);
}
public List CollideEntities(float x, float y, List entities) where T : Entity {
return Collider.CollideEntities(x, y, entities);
}
public List CollideEntities(float x, float y, params int[] tags) where T : Entity {
return Collider.CollideEntities(x, y, tags);
}
public List CollideEntities(float x, float y, params Enum[] tags) where T : Entity {
return Collider.CollideEntities(x, y, tags);
}
///
/// Checks for an overlap using the first available collider.
///
/// The X position to check for a collision at.
/// The Y position to check for a collision at.
/// The int tags to check for.
/// True if there is a collision.
public bool Overlap(float x, float y, params int[] tags) {
Overlapped = null;
if (Collider == null) return false; // If no collider, cant possibly overlap.
var result = Collider.Overlap(x, y, tags);
if (result) {
Overlapped = Collider.Collide(x, y, tags).Entity;
}
return result;
}
///
/// Checks for an overlap using the first available collider.
///
/// The X position to check for a collision at.
/// The Y position to check for a collision at.
/// The enum tags to check for.
/// True if there is a collision.
public bool Overlap(float x, float y, params Enum[] tags) {
return Overlap(x, y, Util.EnumToIntArray(tags));
}
///
/// Checks for an overlap using the first available collider.
///
/// The X position to check for a collision at.
/// The Y position to check for a collision at.
/// The Entity to check for a collision with.
/// True if there is a collision.
public bool Overlap(float x, float y, Entity e) {
Overlapped = null;
var result = Collider.Overlap(x, y, e);
if (result) {
Overlapped = Collider.Collide(x, y, e).Entity;
}
return result;
}
///
/// Called when the entity is added to a scene. The reference to the Scene is available here.
///
public virtual void Added() {
}
///
/// Called when the entity is removed from a scene. The reference to Scene is now null.
///
public virtual void Removed() {
}
///
/// Called when the Scene begins.
///
public virtual void SceneBegin() {
}
///
/// Called when the Scene ends.
///
public virtual void SceneEnd() {
}
///
/// Called when the Scene is paused.
///
public virtual void ScenePause() {
}
///
/// Called when the Scene is resumed.
///
public virtual void SceneResume() {
}
///
/// Called when the entity is paused by the Scene.
///
public virtual void Paused() {
}
///
/// Called when the entity is resumed by the Scene.
///
public virtual void Resumed() {
}
///
/// 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);
}
///
/// Called first during the update. Happens before Update.
///
public virtual void UpdateFirst() {
}
///
/// Called last during the update. Happens after Update.
///
public virtual void UpdateLast() {
}
///
/// Called during the update of the game.
///
public virtual void Update() {
}
///
/// Called when the entity is rendering to the screen.
///
public virtual void Render() {
}
///
/// Called before an entity is rendered. Things rendered here will appear below the Entity's Graphics.
///
public virtual void Prerender() {
}
///
/// Remove this entity from the Scene it's in.
///
public void RemoveSelf() {
if (Scene != null && !MarkedForRemoval) {
Scene.Remove(this);
}
}
#endregion Public Methods
#region Internal Methods
internal void UpdateComponentLists() {
while (componentsToRemove.Count > 0) {
var removing = new List(componentsToRemove);
componentsToRemove.Clear();
foreach (var c in removing) {
Components.Remove(c);
componentsById.Remove(c.InstanceId);
c.InstanceId = -1;
}
foreach (var c in removing) {
c.Removed();
c.Entity = null;
}
}
while (componentsToAdd.Count > 0) {
var adding = new List(componentsToAdd);
componentsToAdd.Clear();
foreach (var c in adding) {
Components.Add(c);
var id = GetNextComponentId();
componentsById.Add(id, c);
c.InstanceId = id;
}
foreach (var c in adding) {
c.Added();
}
}
}
internal void UpdateFirstInternal() {
Game.UpdateCount++;
UpdatedOnce = true;
UpdateComponentLists();
foreach (var c in Components) {
c.UpdateFirst();
}
if (OnUpdateFirst != null) {
OnUpdateFirst();
}
UpdateFirst();
}
internal void UpdateLastInternal() {
UpdateComponentLists();
foreach (var c in Components) {
c.UpdateLast();
c.Timer += Game.DeltaTime;
}
UpdateLast();
if (OnUpdateLast != null) {
OnUpdateLast();
}
foreach (var g in Graphics) {
g.Update();
}
Timer += Game.DeltaTime;
if (LifeSpan > 0) {
if (Timer >= LifeSpan) {
RemoveSelf();
}
}
}
internal void UpdateInternal() {
UpdateComponentLists();
foreach (var c in Components) {
c.Update();
}
if (OnUpdate != null) {
OnUpdate();
}
Tweener.Update(Game.DeltaTime);
Update();
}
internal void RenderInternal() {
if (!UpdatedOnce) return; //prevent rendering before update
if (!Visible) return;
if (Scene == null) return;
if (Surface == null) {
RenderEntity();
}
else {
foreach (var surface in Surfaces) {
Surface temp = Draw.Target;
Draw.SetTarget(surface);
RenderEntity();
Draw.SetTarget(temp);
}
}
}
#endregion Internal Methods
#region Private Methods
int GetNextComponentId() {
var id = nextComponentId;
nextComponentId++;
return id;
}
private void RenderEntity() {
Prerender();
foreach (var c in Components) {
if (!c.RenderAfterEntity) {
if (c.Visible) c.Render();
}
}
foreach (var g in Graphics) {
if (g.Relative) {
g.Render(X, Y);
}
else {
g.Render(0, 0);
}
}
Render();
if (OnRender != null) {
OnRender();
}
foreach (var c in Components) {
if (c.RenderAfterEntity) {
if (c.Visible) c.Render();
}
}
}
#endregion Private Methods
}
}