Fixed ECS so now components can actually be added and removed accordingly (still nothing renders though)
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
|
||||
public abstract class Component
|
||||
{
|
||||
|
||||
public Entity Entity;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.ComponentModel;
|
||||
using Silk.NET.Maths;
|
||||
using Silk.NET.OpenGL;
|
||||
|
||||
namespace EngineSharp.Core.ECS;
|
||||
|
||||
@@ -6,29 +8,88 @@ namespace EngineSharp.Core.ECS;
|
||||
// https://github.com/friflo/Friflo.Engine.ECS/tree/main
|
||||
// OR: https://github.com/genaray/Arch
|
||||
|
||||
public struct Entity
|
||||
public class Entity
|
||||
{
|
||||
public readonly long Id;
|
||||
private Scene _scene;
|
||||
|
||||
public required string Name { get; set; }
|
||||
|
||||
internal Entity(long id, Scene scene)
|
||||
{
|
||||
Id = id;
|
||||
_scene = scene;
|
||||
}
|
||||
// TODO: Maybe use a Hashset and implement a comparer or something? Should be faster than the current .OfType<>().Any() implementation
|
||||
private readonly List<LogicComponent> _logicComponents = [];
|
||||
private readonly List<DataComponent> _dataComponents = [];
|
||||
private readonly List<RenderComponent> _renderComponents = [];
|
||||
|
||||
/// <summary>
|
||||
/// If the component already exists on the entity, it will not be added.
|
||||
/// Do not set this property manually! Will be set via the ECS.
|
||||
/// </summary>
|
||||
public void AddComponent(Component component)
|
||||
{
|
||||
_scene.AddComponent(this, component);
|
||||
}
|
||||
public long Id { get; init; }
|
||||
public string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// If the component already exists on the entity, it will not be added
|
||||
/// </summary>
|
||||
public bool AddComponent<T>(T component)
|
||||
where T : Component
|
||||
{
|
||||
component.Entity = this;
|
||||
return component switch
|
||||
{
|
||||
LogicComponent logic => AddComponent(logic, _logicComponents),
|
||||
DataComponent data => AddComponent(data, _dataComponents),
|
||||
RenderComponent render => AddComponent(render, _renderComponents),
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public T? GetComponent<T>(T component)
|
||||
where T : Component
|
||||
{
|
||||
return component switch
|
||||
{
|
||||
LogicComponent => _logicComponents.OfType<T>().SingleOrDefault(),
|
||||
DataComponent => _dataComponents.OfType<T>().SingleOrDefault(),
|
||||
RenderComponent => _renderComponents.OfType<T>().SingleOrDefault(),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
internal void InitialiseComponents()
|
||||
{
|
||||
foreach (var components in _logicComponents)
|
||||
{
|
||||
components.Initialise();
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateComponents(double deltaTime)
|
||||
{
|
||||
foreach (var components in _logicComponents)
|
||||
{
|
||||
components.OnUpdate(deltaTime);
|
||||
}
|
||||
}
|
||||
internal void RenderComponents(GL gl, Matrix4X4<float> projectionMatrix, Matrix4X4<float> viewMatrix)
|
||||
{
|
||||
// TODO: retrieve the data component "Transform" which every Entity must have (although currently there is no transform component)
|
||||
var modelMatrix = Matrix4X4.CreateTranslation(0, 0, -10f);
|
||||
|
||||
foreach (var components in _renderComponents)
|
||||
{
|
||||
components.Render(gl, projectionMatrix, viewMatrix, modelMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the component already exists on the entity, it will not be added
|
||||
/// </summary>
|
||||
private static bool AddComponent<TComponent, TArchetype>(TComponent component, List<TArchetype> componentStore)
|
||||
where TArchetype : Component
|
||||
where TComponent : TArchetype
|
||||
{
|
||||
if (componentStore.OfType<TComponent>().Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
componentStore.Add(component);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public Component? GetComponent(Component component)
|
||||
{
|
||||
return _scene.GetComponent(this, component);
|
||||
}
|
||||
}
|
||||
@@ -1,113 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Silk.NET.Maths;
|
||||
using Silk.NET.Maths;
|
||||
using Silk.NET.OpenGL;
|
||||
|
||||
namespace EngineSharp.Core.ECS;
|
||||
|
||||
public class Scene
|
||||
{
|
||||
// TODO: Maybe instead of a List, use a HashSet. Implement a equality comparer to ensure, only one element per type can be present
|
||||
private readonly Dictionary<long, List<LogicComponent>> _logicComponents = new();
|
||||
private readonly Dictionary<long, List<DataComponent>> _dataComponents = new();
|
||||
private readonly Dictionary<long, List<RenderComponent>> _renderComponents = new();
|
||||
|
||||
private readonly Dictionary<long, Entity> _entities = [];
|
||||
private long _nextSceneId; // not the prettiest solution but it works
|
||||
|
||||
public Entity CreateEntity(string name)
|
||||
public TEntity CreateEntity<TEntity>(string name)
|
||||
where TEntity : Entity, new()
|
||||
{
|
||||
return new Entity(_nextSceneId++, this)
|
||||
var entity = new TEntity
|
||||
{
|
||||
Id = _nextSceneId++,
|
||||
Name = name,
|
||||
};
|
||||
_entities.Add(entity.Id, entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
internal void InitialiseComponents()
|
||||
internal void InitialiseEntities()
|
||||
{
|
||||
foreach (var components in _logicComponents.Values)
|
||||
foreach (var entity in _entities.Values)
|
||||
{
|
||||
components.ForEach(c => c.Initialise());
|
||||
entity.InitialiseComponents();
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateComponents(double deltaTime)
|
||||
{
|
||||
foreach (var components in _logicComponents.Values)
|
||||
foreach (var entity in _entities.Values)
|
||||
{
|
||||
components.ForEach(c => c.OnUpdate(deltaTime));
|
||||
entity.UpdateComponents(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RenderComponents(GL gl, Matrix4X4<float> projectionMatrix, Matrix4X4<float> viewMatrix)
|
||||
{
|
||||
foreach (var components in _renderComponents.Values)
|
||||
foreach (var entity in _entities.Values)
|
||||
{
|
||||
// TODO: retrieve the transform component of the entity to generate the model matrix
|
||||
// TODO: make a record which holds the matrices and maybe more, so the parameter list doesn't explode
|
||||
var modelMatrix = Matrix4X4<float>.Identity;
|
||||
var t = Matrix4X4.CreateTranslation(0, 0, -10f);
|
||||
components.ForEach(c => c.Render(gl, projectionMatrix, viewMatrix, t));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the component already exists on the entity, it will not be added
|
||||
/// </summary>
|
||||
internal void AddComponent<T>(Entity entity, T component)
|
||||
where T : Component
|
||||
{
|
||||
switch (component)
|
||||
{
|
||||
case LogicComponent logic:
|
||||
AddComponent(entity.Id, logic, _logicComponents);
|
||||
break;
|
||||
case DataComponent data:
|
||||
AddComponent(entity.Id, data, _dataComponents);
|
||||
break;
|
||||
case RenderComponent render:
|
||||
AddComponent(entity.Id, render, _renderComponents);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal T? GetComponent<T>(Entity entity, T component)
|
||||
where T : Component
|
||||
{
|
||||
return component switch
|
||||
{
|
||||
LogicComponent => GetComponent<T, LogicComponent>(entity.Id, _logicComponents),
|
||||
DataComponent => GetComponent<T, DataComponent>(entity.Id, _dataComponents),
|
||||
RenderComponent => GetComponent<T, RenderComponent>(entity.Id, _renderComponents),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private static TComponent? GetComponent<TComponent, TArchetype>(long id, Dictionary<long, List<TArchetype>> componentStore)
|
||||
where TComponent : Component
|
||||
{
|
||||
// ReSharper disable once ConvertIfStatementToReturnStatement
|
||||
if (componentStore.TryGetValue(id, out var components))
|
||||
{
|
||||
return components.OfType<TComponent>().SingleOrDefault();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the component already exists on the entity, it will not be added
|
||||
/// </summary>
|
||||
private static void AddComponent<TComponent, TArchetype>(long id, TComponent component, Dictionary<long, List<TArchetype>> componentStore)
|
||||
where TArchetype : Component
|
||||
where TComponent : TArchetype
|
||||
{
|
||||
if (componentStore.TryGetValue(id, out var components) && !components.OfType<TComponent>().Any())
|
||||
{
|
||||
components.Add(component);
|
||||
}
|
||||
else
|
||||
{
|
||||
componentStore.Add(id, [component]);
|
||||
entity.RenderComponents(gl, projectionMatrix, viewMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ internal class OpenGLEngine : Engine
|
||||
if (_scenes.TryGetValue(sceneName, out var scene))
|
||||
{
|
||||
_currentScene = scene;
|
||||
_currentScene.InitialiseComponents();
|
||||
_currentScene.InitialiseEntities();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -14,7 +14,6 @@ public class MeshRenderer : RenderComponent
|
||||
internal override void Render(GL gl, Matrix4X4<float> projectionMatrix, Matrix4X4<float> viewMatrix, Matrix4X4<float> modelMatrix)
|
||||
{
|
||||
GenerateRenderableMesh(gl, projectionMatrix, viewMatrix, modelMatrix);
|
||||
gl.BindVertexArray(vao);
|
||||
gl.DrawElements(PrimitiveType.Triangles, (uint)Mesh.Indices.Length, DrawElementsType.UnsignedInt, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using EngineSharp.Core.Rendering;
|
||||
using EngineSharp.Core.Rendering;
|
||||
using EngineSharp.Extensions;
|
||||
using Silk.NET.Maths;
|
||||
|
||||
namespace Engine_silk.NET;
|
||||
namespace EngineSharp;
|
||||
|
||||
public class IcoSphere
|
||||
{
|
||||
|
||||
@@ -1,34 +1,11 @@
|
||||
using Engine_silk.NET;
|
||||
using EngineSharp.Core;
|
||||
using EngineSharp.Core.Rendering;
|
||||
|
||||
namespace EngineSharp;
|
||||
|
||||
public class Planet : Core.ECS.LogicComponent
|
||||
public class Planet : Core.ECS.Entity
|
||||
{
|
||||
public MeshRenderer PlanetMesh;
|
||||
|
||||
public override void Initialise()
|
||||
public Planet()
|
||||
{
|
||||
var shader = Shader.GetShader("./assets/shaders/sphere.vert");
|
||||
var material = new Material
|
||||
{
|
||||
Shader = shader,
|
||||
};
|
||||
var sphereGenerator = new IcoSphere
|
||||
{
|
||||
Resolution = 10,
|
||||
Material = material,
|
||||
};
|
||||
|
||||
PlanetMesh = new MeshRenderer
|
||||
{
|
||||
Mesh = sphereGenerator.CreateSphere(),
|
||||
};
|
||||
}
|
||||
|
||||
public override void OnUpdate(double deltaTime)
|
||||
{
|
||||
// Nothing to do at the moment
|
||||
AddComponent(new PlanetGenerator());
|
||||
}
|
||||
}
|
||||
33
src/EngineSharp/PlanetGenerator.cs
Normal file
33
src/EngineSharp/PlanetGenerator.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using EngineSharp.Core.Rendering;
|
||||
|
||||
namespace EngineSharp;
|
||||
|
||||
public class PlanetGenerator : Core.ECS.LogicComponent
|
||||
{
|
||||
public override void Initialise()
|
||||
{
|
||||
var shader = Shader.GetShader("./assets/shaders/sphere.vert");
|
||||
var material = new Material
|
||||
{
|
||||
Shader = shader,
|
||||
};
|
||||
var sphereGenerator = new IcoSphere
|
||||
{
|
||||
Resolution = 10,
|
||||
Material = material,
|
||||
};
|
||||
|
||||
// TODO: MeshRenderer should probably not have the mesh as a required property.
|
||||
var planetMesh = new MeshRenderer
|
||||
{
|
||||
Mesh = sphereGenerator.CreateSphere(),
|
||||
};
|
||||
|
||||
Entity.AddComponent(planetMesh);
|
||||
}
|
||||
|
||||
public override void OnUpdate(double deltaTime)
|
||||
{
|
||||
// Nothing to do at the moment
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,7 @@ internal static class Program
|
||||
|
||||
var engine = EngineFactory.Create(GraphicsAPI.OpenGL, options);
|
||||
var mainScene = SceneManager.CreateDefaultScene("default");
|
||||
var planet = mainScene.CreateEntity("planet");
|
||||
|
||||
// TODO: Find a way to call "AddComponent" from a component. Otherwise the MeshRenderer I created inside the Planet-Component doesn't get registered and can't therefore be rendered
|
||||
planet.AddComponent(new Planet());
|
||||
var planet = mainScene.CreateEntity<Planet>("planet");
|
||||
|
||||
engine.Start();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user