Add attribute system with core stats and gameplay components
This commit is contained in:
11
GameCore/ECS/Entity.cs
Normal file
11
GameCore/ECS/Entity.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace GameCore.ECS;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a unique object in the game world.
|
||||
/// It's a simple struct containing an ID to keep it lightweight.
|
||||
/// All data associated with an Entity is stored in Components.
|
||||
/// </summary>
|
||||
public readonly struct Entity(int id)
|
||||
{
|
||||
public readonly int Id = id;
|
||||
}
|
||||
7
GameCore/ECS/HitResult.cs
Normal file
7
GameCore/ECS/HitResult.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace GameCore.ECS;
|
||||
|
||||
public struct HitResult
|
||||
{
|
||||
public bool DidHit;
|
||||
public Entity HitEntity;
|
||||
}
|
||||
12
GameCore/ECS/Interfaces/IComponent.cs
Normal file
12
GameCore/ECS/Interfaces/IComponent.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace GameCore.ECS.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// A marker interface for all components.
|
||||
/// Components are simple data containers (structs or classes) that hold the state
|
||||
/// for a specific aspect of an Entity (e.g., its position, health, or inventory).
|
||||
/// They contain no logic.
|
||||
/// </summary>
|
||||
public interface IComponent
|
||||
{
|
||||
|
||||
}
|
||||
6
GameCore/ECS/Interfaces/IEntityPresenter.cs
Normal file
6
GameCore/ECS/Interfaces/IEntityPresenter.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace GameCore.ECS.Interfaces;
|
||||
|
||||
public interface IEntityPresenter
|
||||
{
|
||||
public Entity CoreEntity { get; set; }
|
||||
}
|
||||
8
GameCore/ECS/Interfaces/IPresenterComponent.cs
Normal file
8
GameCore/ECS/Interfaces/IPresenterComponent.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace GameCore.ECS.Interfaces;
|
||||
|
||||
public interface IPresenterComponent
|
||||
{
|
||||
void Initialize(Entity coreEntity, World world);
|
||||
void SyncToPresentation(float delta);
|
||||
void SyncToCore(float delta);
|
||||
}
|
||||
17
GameCore/ECS/Interfaces/ISystem.cs
Normal file
17
GameCore/ECS/Interfaces/ISystem.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace GameCore.ECS.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// The contract for all game logic systems.
|
||||
/// Systems are stateless classes that contain all the logic.
|
||||
/// They operate on entities that possess a specific set of components.
|
||||
/// For example, a MovementSystem would operate on entities with PositionComponent and VelocityComponent.
|
||||
/// </summary>
|
||||
public interface ISystem
|
||||
{
|
||||
/// <summary>
|
||||
/// The main update method for a system, called once per game loop.
|
||||
/// </summary>
|
||||
/// <param name="world">A reference to the main World object to query for entities and components.</param>
|
||||
/// <param name="deltaTime">The time elapsed since the last frame.</param>
|
||||
void Update(World world, float deltaTime);
|
||||
}
|
||||
9
GameCore/ECS/Interfaces/IWorldQuery.cs
Normal file
9
GameCore/ECS/Interfaces/IWorldQuery.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using GameCore.Math;
|
||||
|
||||
namespace GameCore.ECS.Interfaces;
|
||||
|
||||
public interface IWorldQuery
|
||||
{
|
||||
HitResult Raycast(Vector3 from, Vector3 to, Entity? ownerToExclude = null);
|
||||
Vector3 RotateVectorByYaw(Vector3 vector, float yawRadians);
|
||||
}
|
||||
10
GameCore/ECS/PresenterData.cs
Normal file
10
GameCore/ECS/PresenterData.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using GameCore.ECS.Interfaces;
|
||||
|
||||
namespace GameCore.ECS;
|
||||
|
||||
public readonly struct PresenterData(Entity entity, IEntityPresenter presenter, List<IPresenterComponent> components)
|
||||
{
|
||||
public readonly Entity Entity = entity;
|
||||
public readonly IEntityPresenter Presenter = presenter;
|
||||
public readonly List<IPresenterComponent> Components = components;
|
||||
}
|
||||
113
GameCore/ECS/World.cs
Normal file
113
GameCore/ECS/World.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using GameCore.Config;
|
||||
using GameCore.ECS.Interfaces;
|
||||
using GameCore.Events;
|
||||
using GameCore.Events.Interfaces;
|
||||
using GameCore.Input.Interfaces;
|
||||
|
||||
namespace GameCore.ECS;
|
||||
|
||||
/// <summary>
|
||||
/// The central container for the entire game state.
|
||||
/// It manages all entities, components, and systems, and orchestrates the main game loop.
|
||||
/// This class is the primary point of interaction for the Engine/Presentation layer.
|
||||
/// </summary>
|
||||
public class World(IInputService inputService, IWorldQuery worldQuery, SimulationConfig config)
|
||||
{
|
||||
private readonly Dictionary<int, List<IComponent>> _componentsByEntityId = new();
|
||||
private readonly EventBus _eventBus = new();
|
||||
private readonly List<ISystem> _systems = [];
|
||||
|
||||
public readonly SimulationConfig Config = config;
|
||||
public readonly IInputService InputService = inputService;
|
||||
public readonly IWorldQuery WorldQuery = worldQuery;
|
||||
private int _nextEntityId;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new, unique entity.
|
||||
/// </summary>
|
||||
/// <returns>The newly created Entity.</returns>
|
||||
public Entity CreateEntity()
|
||||
{
|
||||
var id = _nextEntityId++;
|
||||
_componentsByEntityId[id] = [];
|
||||
return new Entity(id);
|
||||
}
|
||||
|
||||
public void DestroyEntity(Entity entity)
|
||||
{
|
||||
_componentsByEntityId.Remove(entity.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a component to a given entity.
|
||||
/// </summary>
|
||||
public void AddComponent(Entity entity, IComponent component)
|
||||
{
|
||||
_componentsByEntityId[entity.Id].Add(component);
|
||||
}
|
||||
|
||||
public void RemoveComponent<T>(Entity entity) where T : class, IComponent
|
||||
{
|
||||
if (!_componentsByEntityId.TryGetValue(entity.Id, out var components)) return;
|
||||
|
||||
var componentToRemove = components.OfType<T>().FirstOrDefault();
|
||||
if (componentToRemove != null) components.Remove(componentToRemove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a specific type of component from an entity.
|
||||
/// </summary>
|
||||
/// <returns>The component instance, or null if the entity doesn't have it.</returns>
|
||||
public T? GetComponent<T>(Entity entity) where T : class, IComponent
|
||||
{
|
||||
return _componentsByEntityId.TryGetValue(entity.Id, out var components)
|
||||
? components.OfType<T>().FirstOrDefault()
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all entities that have a specific component type.
|
||||
/// </summary>
|
||||
public IEnumerable<Entity> GetEntitiesWith<T>() where T : IComponent
|
||||
{
|
||||
return from pair in _componentsByEntityId
|
||||
where pair.Value.Any(c => c is T)
|
||||
select new Entity(pair.Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a system to be run in the game loop.
|
||||
/// </summary>
|
||||
public void RegisterSystem(ISystem system)
|
||||
{
|
||||
_systems.Add(system);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a single tick of the game simulation, updating all registered systems.
|
||||
/// </summary>
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
foreach (var system in _systems) system.Update(this, deltaTime);
|
||||
}
|
||||
|
||||
public void PublishEvent(IEvent gameEvent)
|
||||
{
|
||||
_eventBus.Publish(gameEvent);
|
||||
}
|
||||
|
||||
public void ProcessEvents()
|
||||
{
|
||||
_eventBus.ProcessEvents();
|
||||
}
|
||||
|
||||
public void Subscribe<T>(Action<T> handler) where T : IEvent
|
||||
{
|
||||
_eventBus.Subscribe(handler);
|
||||
}
|
||||
|
||||
public void Unsubscribe<T>(Action<T> handler) where T : IEvent
|
||||
{
|
||||
_eventBus.Unsubscribe(handler);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user