This commit is contained in:
LunarAkai 2025-08-04 23:06:15 +02:00
commit 022fac5966
8 changed files with 150 additions and 13 deletions

1
Cargo.lock generated
View file

@ -791,6 +791,7 @@ version = "0.1.0"
name = "moonhare_game"
version = "0.1.0"
dependencies = [
"moonhare_ecs",
"moonhare_graphics",
"moonhare_log",
"moonhare_window",

View file

@ -0,0 +1,20 @@
use std::{collections::HashMap, marker::PhantomData};
use crate::generational_index::{self, GenerationalIndex};
#[derive(Debug)]
pub struct Component<T: ComponentType> {
id: usize,
component_type: T,
}
pub trait ComponentType{}
impl<T: ComponentType> Component<T> {
pub fn new(id: usize, component_type: T) -> Self {
Self {
id,
component_type
}
}
}

View file

@ -0,0 +1,36 @@
use crate::{entity, generational_index::{GenerationalIndex, GenerationalIndexAllocator}};
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Hash)]
pub struct Entity(GenerationalIndex);
impl Entity {
pub fn index(&self) -> usize {
self.0.index()
}
pub fn generation(&self) -> u64 {
self.0.generation()
}
}
#[derive(Debug, Clone)]
pub struct EntityAllocator(GenerationalIndexAllocator);
impl EntityAllocator {
pub fn new() -> Self {
EntityAllocator(GenerationalIndexAllocator::new())
}
pub fn allocate(&mut self) -> Entity {
Entity(self.0.allocate())
}
pub fn deallocate(&mut self, entity: Entity) -> bool {
self.0.deallocate(entity.0)
}
pub fn is_live(&self, entity: Entity) -> bool {
self.0.is_live(entity.0)
}
}

View file

@ -1,4 +1,4 @@
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct GenerationalIndex {
index: usize,
generation: u64,
@ -14,17 +14,22 @@ impl GenerationalIndex {
}
}
#[derive(Debug, Clone)]
struct AllocatorEntry {
is_live: bool,
generation: u64,
}
#[derive(Debug, Clone, Default)]
pub struct GenerationalIndexAllocator {
entries: Vec<AllocatorEntry>,
free: Vec<usize>,
}
impl GenerationalIndexAllocator {
pub fn new() -> Self {
Default::default()
}
pub fn allocate(&mut self) -> GenerationalIndex {
match self.free.pop() {
Some(index) =>{
@ -64,6 +69,10 @@ impl GenerationalIndexAllocator {
pub fn is_live(&self, index: GenerationalIndex) -> bool {
index.index() < self.entries.len() && self.entries[index.index()].generation == index.generation && self.entries[index.index()].is_live
}
pub fn max_allocated_index(&self) -> usize {
self.entries.len()
}
}
struct ArrayEntry<T> {
@ -74,7 +83,15 @@ struct ArrayEntry<T> {
pub struct GenerationalIndexArray<T>(Vec<Option<ArrayEntry<T>>>);
impl <T> GenerationalIndexArray<T> {
pub fn set(&mut self, index: GenerationalIndex, value: T) {
pub fn new() -> GenerationalIndexArray<T> {
GenerationalIndexArray(Vec::new())
}
pub fn clear(&mut self) {
self.0.clear();
}
pub fn insert(&mut self, index: GenerationalIndex, value: T) {
while self.0.len() <= index.index() {
self.0.push(None);
}

View file

@ -1,15 +1,55 @@
use std::{collections::HashMap, path::{Component, Components}};
use anymap::AnyMap;
use crate::generational_index::{GenerationalIndex, GenerationalIndexAllocator, GenerationalIndexArray};
use crate::{entity::{Entity, EntityAllocator}, generational_index::{GenerationalIndex, GenerationalIndexAllocator, GenerationalIndexArray}};
pub mod generational_index;
pub mod world;
pub mod entity;
pub mod component;
pub type Entity = GenerationalIndex;
pub type EntityMap<T> = GenerationalIndexArray<T>;
// based on: https://kyren.github.io/2018/09/14/rustconf-talk.html
/* Moonhare ECS Design
--------------------------------------
Game
🠟
Systems
(RenderSystem, PhysicsSystem, EnemyAISystem, EnemyCollisionSystem,...)
🠟
Entity
🠟
Components
--------------------------------------
*/
#[derive(Debug)]
pub struct ECS {
pub entity_allocator: GenerationalIndexAllocator,
pub entity_components: AnyMap,
pub resources: AnyMap
}
entities: EntityAllocator,
components: AnyMap
}
impl ECS {
pub fn new() -> ECS {
ECS {
entities: EntityAllocator::new(),
components: AnyMap::new(),
}
}
pub fn add_entity(&mut self) -> Entity {
self.entities.allocate()
}
pub fn entity_is_live(&self, entity: Entity) -> bool {
self.entities.is_live(entity)
}
pub fn register_component() {
}
}

View file

@ -0,0 +1,22 @@
use std::fmt::Error;
use anymap::AnyMap;
use crate::{Entity, ECS};
/// stores Entitys, Components and resources
/// provides methods to search for specific Entitys
#[derive(Debug)]
pub struct World {
ecs: ECS,
resources: AnyMap
}
impl World {
pub fn new() -> Self {
Self {
ecs: ECS::new(),
resources: AnyMap::new(),
}
}
}

View file

@ -5,6 +5,7 @@ version.workspace = true
authors.workspace = true
[dependencies]
moonhare_ecs = { path = "../moonhare_ecs" }
moonhare_graphics = { path = "../moonhare_graphics" }
moonhare_log = { path = "../moonhare_log" }
moonhare_window = { path = "../moonhare_window" }

View file

@ -2,6 +2,8 @@
use std::rc::Rc;
use moonhare_ecs::world::World;
use moonhare_graphics::{color::Color, glium::{backend::Context, glutin::api::egl::context}};
use moonhare_log::*;
use moonhare_window::{platforms::glfw_window::GLFWWindow};
@ -10,19 +12,17 @@ pub mod basic;
/// Only one Game may exist per project
#[derive(Debug)]
pub struct Game {
pub world: World,
pub is_running: bool,
pub name: String,
pub context: moonhare_window::WindowRenderContext,
pub glfw_window: Option<moonhare_window::platforms::glfw_window::GLFWWindow>,
}
impl Default for Game {
fn default() -> Self {
Self {
world: World::new(),
is_running: true,
name: default_game_name(),
context: moonhare_window::WindowRenderContext::OPENGLGLFW,
glfw_window: None,
}
}
}