working on an entity component system

This commit is contained in:
Dylan Wilson
2016-08-18 23:21:37 +10:00
parent d1a9f7652b
commit fe02308254
16 changed files with 275 additions and 156 deletions
@@ -36,9 +36,16 @@
<ApplicationIcon>Icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Compile Include="Entities\Components\EntityComponent.cs" />
<Compile Include="Entities\Components\SpriteAnimatorComponent.cs" />
<Compile Include="Entities\Components\SpriteComponent.cs" />
<Compile Include="Entities\Entity.cs" />
<Compile Include="Entities\EntityGameComponent.cs" />
<Compile Include="Entities\EntityComponentSystem.cs" />
<Compile Include="Entities\Systems\SpriteAnimatorComponentSystem.cs" />
<Compile Include="Entities\Systems\UpdatableComponentSystem.cs" />
<Compile Include="Entities\Systems\ComponentSystem.cs" />
<Compile Include="Entities\Systems\DrawableComponentSystem.cs" />
<Compile Include="Entities\Systems\SpriteBatchComponentSystem.cs" />
<Compile Include="Game1.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@@ -74,6 +81,7 @@
<Name>MonoGame.Extended</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
@@ -0,0 +1,11 @@
namespace Demo.Platformer.Entities.Components
{
public abstract class EntityComponent
{
protected EntityComponent()
{
}
public Entity Entity { get; internal set; }
}
}
@@ -0,0 +1,17 @@
using MonoGame.Extended.Animations.SpriteSheets;
namespace Demo.Platformer.Entities.Components
{
public class SpriteAnimatorComponent : EntityComponent
{
public SpriteAnimatorComponent(SpriteSheetAnimationFactory animationFactory, string playAnimation = null)
{
Animator = new SpriteSheetAnimator(animationFactory);
if (playAnimation != null)
Animator.Play(playAnimation);
}
public SpriteSheetAnimator Animator { get; }
}
}
@@ -1,23 +1,10 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Extended.SceneGraphs;
using MonoGame.Extended.TextureAtlases;
namespace Demo.Platformer.Entities.Components
{
public abstract class EntityComponent
{
protected EntityComponent()
{
}
public Entity Entity { get; internal set; }
public abstract void Update(float deltaTime);
public abstract void Draw(SpriteBatch spriteBatch);
}
public class SpriteComponent : EntityComponent, ISpriteBatchDrawable
public class SpriteComponent : EntityComponent
{
public SpriteComponent(TextureRegion2D textureRegion)
{
@@ -26,24 +13,19 @@ namespace Demo.Platformer.Entities.Components
Color = Color.White;
Origin = new Vector2(textureRegion.Size.Width, textureRegion.Size.Height)*0.5f;
Effect = SpriteEffects.None;
Alpha = 1.0f;
Depth = 0.0f;
}
public bool IsVisible { get; set; }
public TextureRegion2D TextureRegion { get; set; }
public Color Color { get; }
public Vector2 Origin { get; }
public SpriteEffects Effect { get; }
public Color Color { get; set; }
public Vector2 Origin { get; set; }
public SpriteEffects Effect { get; set; }
public Vector2 Position => Entity.Position;
public float Rotation => Entity.Rotation;
public Vector2 Scale => Entity.Scale;
public override void Update(float deltaTime)
{
}
public override void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(TextureRegion, Position, Color, Rotation, Origin, Scale, Effect, 0);
}
public float Alpha { get; set; }
public float Depth { get; set; }
}
}
@@ -1,4 +1,5 @@
using Demo.Platformer.Entities.Components;
using System;
using Demo.Platformer.Entities.Components;
using Microsoft.Xna.Framework;
using MonoGame.Extended;
@@ -6,24 +7,51 @@ namespace Demo.Platformer.Entities
{
public class Entity : IMovable, IRotatable, IScalable
{
private readonly IEntityManager _entityManager;
private readonly EntityComponentSystem _entityComponentSystem;
public Entity(IEntityManager entityManager, long id)
public Entity(EntityComponentSystem entityComponentSystem, long id, string name)
{
_entityManager = entityManager;
_entityComponentSystem = entityComponentSystem;
Id = id;
Name = name;
Position = Vector2.Zero;
Rotation = 0;
Scale = Vector2.One;
}
public long Id { get; }
public string Name { get; }
public Vector2 Position { get; set; }
public float Rotation { get; set; }
public Vector2 Scale { get; set; }
public override string ToString()
{
return Name;
}
public void AttachComponent(EntityComponent component)
{
if (component.Entity != null)
throw new InvalidOperationException("Component already attached to another entity");
component.Entity = this;
_entityManager.AttachComponent(component);
_entityComponentSystem.AttachComponent(component);
}
public void DetachComponent(EntityComponent component)
{
if (component.Entity != this)
throw new InvalidOperationException("Component not attached to entity");
component.Entity = null;
_entityComponentSystem.DetachComponent(component);
}
public void Destroy()
{
_entityComponentSystem.DestroyEntity(this);
}
}
}
@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Demo.Platformer.Entities.Components;
using Demo.Platformer.Entities.Systems;
using Microsoft.Xna.Framework;
namespace Demo.Platformer.Entities
{
public class EntityComponentSystem
{
public EntityComponentSystem()
{
_entities = new List<Entity>();
_components = new List<EntityComponent>();
_systems = new List<ComponentSystem>();
_nextEntityId = 1;
}
private readonly List<ComponentSystem> _systems;
private readonly List<Entity> _entities;
private readonly List<EntityComponent> _components;
private long _nextEntityId;
public void RegisterSystem(ComponentSystem system)
{
if (system.EntityComponentSystem != null)
throw new InvalidOperationException("Component system already registered");
system.EntityComponentSystem = this;
_systems.Add(system);
}
public Entity CreateEntity(string name)
{
var entity = new Entity(this, _nextEntityId, name);
_entities.Add(entity);
_nextEntityId++;
return entity;
}
public Entity FindEntity(string name)
{
return _entities.FirstOrDefault(e => e.Name == name);
}
public void DestroyEntity(Entity entity)
{
_entities.Remove(entity);
}
public void AttachComponent(EntityComponent component)
{
_components.Add(component);
}
public void DetachComponent(EntityComponent component)
{
_components.Remove(component);
}
internal IEnumerable<T> GetComponents<T>()
{
return _components.OfType<T>();
}
public void Update(GameTime gameTime)
{
foreach (var componentSystem in _systems.OfType<UpdatableComponentSystem>())
componentSystem.Update(gameTime);
}
public void Draw(GameTime gameTime)
{
foreach (var componentSystem in _systems.OfType<DrawableComponentSystem>())
componentSystem.Draw(gameTime);
}
}
}
@@ -1,82 +0,0 @@
using System.Collections.Generic;
using Demo.Platformer.Entities.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Extended;
namespace Demo.Platformer.Entities
{
public interface IEntityManager
{
void AttachComponent(EntityComponent component);
void DetachComponent(EntityComponent component);
}
public class EntityGameComponent : DrawableGameComponent, IEntityManager
{
public EntityGameComponent(Game game)
: base(game)
{
_entities = new List<Entity>();
_components = new List<EntityComponent>();
_nextEntityId = 1;
}
private SpriteBatch _spriteBatch;
private readonly List<Entity> _entities;
private readonly List<EntityComponent> _components;
private long _nextEntityId;
public override void Initialize()
{
base.Initialize();
_spriteBatch = new SpriteBatch(GraphicsDevice);
}
public Entity CreateEntity()
{
var entity = new Entity(this, _nextEntityId);
_entities.Add(entity);
_nextEntityId++;
return entity;
}
public void DestroyEntity(Entity entity)
{
_entities.Remove(entity);
}
public void AttachComponent(EntityComponent component)
{
_components.Add(component);
}
public void DetachComponent(EntityComponent component)
{
_components.Remove(component);
}
public override void Update(GameTime gameTime)
{
base.Update(gameTime);
var deltaTime = gameTime.GetElapsedSeconds();
foreach (var entityComponent in _components)
entityComponent.Update(deltaTime);
}
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
_spriteBatch.Begin();
foreach (var entityComponent in _components)
entityComponent.Draw(_spriteBatch);
_spriteBatch.End();
}
}
}
@@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace Demo.Platformer.Entities.Systems
{
public abstract class ComponentSystem
{
protected ComponentSystem()
{
}
internal EntityComponentSystem EntityComponentSystem { get; set; }
protected IEnumerable<T> GetComponents<T>()
{
return EntityComponentSystem.GetComponents<T>();
}
}
}
@@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Demo.Platformer.Entities.Systems
{
public abstract class DrawableComponentSystem : ComponentSystem
{
public abstract void Draw(GameTime gameTime);
}
}
@@ -0,0 +1,20 @@
using Demo.Platformer.Entities.Components;
using Microsoft.Xna.Framework;
namespace Demo.Platformer.Entities.Systems
{
public class SpriteAnimatorComponentSystem : UpdatableComponentSystem
{
public SpriteAnimatorComponentSystem()
{
}
public override void Update(GameTime gameTime)
{
var components = GetComponents<SpriteAnimatorComponent>();
foreach (var component in components)
component.Animator.Update(gameTime);
}
}
}
@@ -0,0 +1,36 @@
using Demo.Platformer.Entities.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Demo.Platformer.Entities.Systems
{
public class SpriteBatchComponentSystem : DrawableComponentSystem
{
public SpriteBatchComponentSystem(GraphicsDevice graphicsDevice)
{
_spriteBatch = new SpriteBatch(graphicsDevice);
}
private readonly SpriteBatch _spriteBatch;
public override void Draw(GameTime gameTime)
{
var spriteComponents = GetComponents<SpriteComponent>();
_spriteBatch.Begin();
foreach (var sprite in spriteComponents)
{
if (sprite.IsVisible)
{
var texture = sprite.TextureRegion.Texture;
var sourceRectangle = sprite.TextureRegion.Bounds;
_spriteBatch.Draw(texture, sprite.Position, sourceRectangle, sprite.Color * sprite.Alpha, sprite.Rotation, sprite.Origin,
sprite.Scale, sprite.Effect, sprite.Depth);
}
}
_spriteBatch.End();
}
}
}
@@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Demo.Platformer.Entities.Systems
{
public abstract class UpdatableComponentSystem : ComponentSystem
{
public abstract void Update(GameTime gameTime);
}
}
+25 -31
View File
@@ -1,12 +1,12 @@
using Demo.Platformer.Entities;
using Demo.Platformer.Entities.Components;
using Demo.Platformer.Entities.Systems;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGame.Extended;
using MonoGame.Extended.Animations.SpriteSheets;
using MonoGame.Extended.Maps.Tiled;
using MonoGame.Extended.Sprites;
using MonoGame.Extended.TextureAtlases;
using MonoGame.Extended.ViewportAdapters;
@@ -16,16 +16,9 @@ namespace Demo.Platformer
{
// ReSharper disable once NotAccessedField.Local
private readonly GraphicsDeviceManager _graphicsDeviceManager;
private SpriteBatch _spriteBatch;
private Camera2D _camera;
private TiledMap _tiledMap;
private Sprite _sprite;
private SpriteSheetAnimator _animator;
private EntityGameComponent _entityManager;
//private Sprite _sprite0;
//private Sprite _sprite1;
//private Texture2D _logo;
private EntityComponentSystem _entityComponentSystem;
public Game1()
{
@@ -37,36 +30,33 @@ namespace Demo.Platformer
protected override void Initialize()
{
Components.Add(_entityManager = new EntityGameComponent(this));
_entityComponentSystem = new EntityComponentSystem();
_entityComponentSystem.RegisterSystem(new SpriteBatchComponentSystem(GraphicsDevice));
_entityComponentSystem.RegisterSystem(new SpriteAnimatorComponentSystem());
base.Initialize();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
//_logo = Content.Load<Texture2D>("logo-square-128");
var viewportAdapter = new BoxingViewportAdapter(Window, GraphicsDevice, 800, 480);
_camera = new Camera2D(viewportAdapter);
// first load our resources
var texture = Content.Load<Texture2D>("tiny-characters");
var atlas = TextureAtlas.Create(texture, 32, 32, 15);
var animationFactory = new SpriteSheetAnimationFactory(atlas);
animationFactory.Add("idle", new SpriteSheetAnimationData(new[] { 0, 1, 2, 3 }, isReversed: true));
var entity = _entityManager.CreateEntity();
// let's build our dude entity
var entity = _entityComponentSystem.CreateEntity("dude");
entity.AttachComponent(new SpriteComponent(atlas[0]));
entity.AttachComponent(new SpriteAnimatorComponent(animationFactory, "idle"));
entity.Position = new Vector2(300, 300);
animationFactory.Add("idle", new SpriteSheetAnimationData(new[] {0, 1, 2, 3}, isReversed: true));
_animator = new SpriteSheetAnimator(animationFactory);
_animator.Play("idle");
_sprite = _animator.CreateSprite(position: new Vector2(116, 273));
//_sprite1 = _animator.CreateSprite(position: new Vector2(132, 273));
_tiledMap = Content.Load<TiledMap>("level-1");
var viewport = GraphicsDevice.Viewport;
//var viewport = GraphicsDevice.Viewport;
//_alphaTestEffect = new AlphaTestEffect(GraphicsDevice)
//{
// Projection =
@@ -83,19 +73,21 @@ namespace Demo.Platformer
protected override void Update(GameTime gameTime)
{
var deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
//var deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
var keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(Keys.Escape))
Exit();
if (keyboardState.IsKeyDown(Keys.D) || keyboardState.IsKeyDown(Keys.Right))
_sprite.Position += new Vector2(150, 0) * deltaTime;
//if (keyboardState.IsKeyDown(Keys.D) || keyboardState.IsKeyDown(Keys.Right))
// _sprite.Position += new Vector2(150, 0) * deltaTime;
if (keyboardState.IsKeyDown(Keys.A) || keyboardState.IsKeyDown(Keys.Left))
_sprite.Position -= new Vector2(150, 0) * deltaTime;
//if (keyboardState.IsKeyDown(Keys.A) || keyboardState.IsKeyDown(Keys.Left))
// _sprite.Position -= new Vector2(150, 0) * deltaTime;
_animator.Update(deltaTime);
//_animator.Update(deltaTime);
_entityComponentSystem.Update(gameTime);
base.Update(gameTime);
}
@@ -107,7 +99,7 @@ namespace Demo.Platformer
var viewMatrix = _camera.GetViewMatrix();
GraphicsDevice.Clear(Color.CornflowerBlue);
//_spriteBatch.Begin(
// sortMode: SpriteSortMode.FrontToBack,
// samplerState: SamplerState.PointClamp,
@@ -123,9 +115,11 @@ namespace Demo.Platformer
_tiledMap.Draw(viewMatrix);
_spriteBatch.Begin(samplerState: SamplerState.PointClamp, transformMatrix: viewMatrix);
_spriteBatch.Draw(_sprite);
_spriteBatch.End();
//_spriteBatch.Begin(samplerState: SamplerState.PointClamp, transformMatrix: viewMatrix);
//_spriteBatch.Draw(_sprite);
//_spriteBatch.End();
_entityComponentSystem.Draw(gameTime);
base.Draw(gameTime);
}
-9
View File
@@ -1,9 +0,0 @@
using Microsoft.Xna.Framework;
namespace MonoGame.Extended
{
public interface IDraw
{
void Draw(GameTime gameTime);
}
}
@@ -111,7 +111,6 @@
<Compile Include="Content\ContentReaderExtensions.cs" />
<Compile Include="EventHandlerExtensions.cs" />
<Compile Include="GameTimeExtensions.cs" />
<Compile Include="IDraw.cs" />
<Compile Include="IMovable.cs" />
<Compile Include="InputListeners\GamePadEventArgs.cs" />
<Compile Include="InputListeners\GamePadListener.cs" />
+1 -1
View File
@@ -3,7 +3,7 @@ using Microsoft.Xna.Framework;
namespace MonoGame.Extended.Screens
{
public abstract class Screen : IDraw, IDisposable
public abstract class Screen : IDisposable
{
protected Screen()
{