Procházet zdrojové kódy

Implemented creation of entities and components

master
Bergmann89 před 5 roky
rodič
revize
f17497dca8
15 změnil soubory, kde provedl 560 přidání a 257 odebrání
  1. +47
    -0
      async-ecs/src/entity/builder.rs
  2. +53
    -0
      async-ecs/src/entity/entities.rs
  3. +99
    -0
      async-ecs/src/entity/entity.rs
  4. +8
    -2
      async-ecs/src/entity/mod.rs
  5. +9
    -0
      async-ecs/src/error.rs
  6. +1
    -0
      async-ecs/src/lib.rs
  7. +2
    -9
      async-ecs/src/main.rs
  8. +1
    -4
      async-ecs/src/resource/entry.rs
  9. +4
    -236
      async-ecs/src/resource/mod.rs
  10. +256
    -0
      async-ecs/src/resource/resources.rs
  11. +18
    -1
      async-ecs/src/storage/masked_storage.rs
  12. +6
    -2
      async-ecs/src/storage/mod.rs
  13. +22
    -1
      async-ecs/src/storage/storage_wrapper.rs
  14. +29
    -1
      async-ecs/src/storage/vec_storage.rs
  15. +5
    -1
      async-ecs/src/world/mod.rs

+ 47
- 0
async-ecs/src/entity/builder.rs Zobrazit soubor

@@ -0,0 +1,47 @@
use crate::{access::WriteStorage, component::Component, system::SystemData, world::World};

use super::Entity;

pub struct Builder<'a> {
world: &'a World,
entity: Entity,
built: bool,
}

impl<'a> Builder<'a> {
pub fn new(world: &'a World) -> Self {
let entity = world.entities_mut().allocate();

Self {
world,
entity,
built: false,
}
}

#[inline]
pub fn with<T: Component>(self, c: T) -> Self {
{
let mut storage = WriteStorage::<T>::fetch(&self.world);

storage.insert(self.entity, c).unwrap();
}

self
}

#[inline]
pub fn build(mut self) -> Entity {
self.built = true;

self.entity
}
}

impl Drop for Builder<'_> {
fn drop(&mut self) {
if !self.built {
self.world.entities_mut().kill(self.entity);
}
}
}

+ 53
- 0
async-ecs/src/entity/entities.rs Zobrazit soubor

@@ -0,0 +1,53 @@
use hibitset::BitSet;

use super::Entity;

#[derive(Default)]
pub struct Entities {
alive: BitSet,
cache: Vec<u32>,
generations: Vec<u32>,
}

impl Entities {
pub fn is_alive(&self, entity: Entity) -> bool {
let i = entity.index();
let g = entity.generation();

self.alive.contains(i) && self.generations.get(i as usize) == Some(&g)
}

pub fn allocate(&mut self) -> Entity {
let i = match self.cache.pop() {
Some(i) => i,
None => {
let i = self.generations.len() as u32;
let c = i.checked_add(1).expect("No entity left to allocate");

self.generations.resize(c as usize, 0);

i
}
};

let g = self.generations[i as usize].wrapping_add(1);

self.generations[i as usize] = g;
self.alive.add(i);

Entity::from_parts(i, g)
}

pub fn kill(&mut self, entity: Entity) -> bool {
if self.is_alive(entity) {
let i = entity.index();

self.alive.remove(i);
self.cache.push(i);

true
} else {
false
}
}
}

+ 99
- 0
async-ecs/src/entity/entity.rs Zobrazit soubor

@@ -0,0 +1,99 @@
use std::cmp::Ordering;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::hash::{Hash, Hasher};

#[derive(Clone, Copy)]
pub struct Entity(EntityRaw);

pub type Index = u32;
pub type Generation = u32;

#[repr(C, packed)]
#[derive(Clone, Copy)]
union EntityRaw {
id: u64,
data: EntityData,
}

#[repr(C, packed)]
#[derive(Clone, Copy)]
struct EntityData {
index: Index,
generation: Generation,
}

impl Entity {
pub fn from_id(id: u64) -> Self {
Self(EntityRaw { id })
}

pub fn from_parts(index: Index, generation: Generation) -> Self {
Self(EntityRaw {
data: EntityData { index, generation },
})
}

#[inline]
pub fn id(&self) -> u64 {
unsafe { self.0.id }
}

#[inline]
pub fn index(&self) -> Index {
unsafe { self.0.data.index }
}

#[inline]
pub fn generation(&self) -> Generation {
unsafe { self.0.data.generation }
}
}

impl Display for Entity {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{:08X}", self.id())
}
}

impl Debug for Entity {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{:08X}", self.id())
}
}

impl Hash for Entity {
fn hash<H: Hasher>(&self, state: &mut H) {
self.index().hash(state);
self.generation().hash(state);
}
}

impl Eq for Entity {}

impl PartialEq<Entity> for Entity {
fn eq(&self, other: &Entity) -> bool {
self.id() == other.id()
}
}

impl Ord for Entity {
fn cmp(&self, other: &Self) -> Ordering {
if self.generation() < other.generation() {
Ordering::Less
} else if self.generation() > other.generation() {
Ordering::Greater
} else if self.index() < other.index() {
Ordering::Less
} else if self.index() > other.index() {
Ordering::Greater
} else {
Ordering::Equal
}
}
}

impl PartialOrd for Entity {
fn partial_cmp(&self, other: &Entity) -> Option<Ordering> {
Some(Ord::cmp(self, other))
}
}

+ 8
- 2
async-ecs/src/entity/mod.rs Zobrazit soubor

@@ -1,2 +1,8 @@
#[derive(Default)]
pub struct Entities;
pub mod builder;
pub mod entities;
#[allow(clippy::module_inception)]
pub mod entity;

pub use builder::Builder;
pub use entities::Entities;
pub use entity::{Entity, Generation, Index};

+ 9
- 0
async-ecs/src/error.rs Zobrazit soubor

@@ -0,0 +1,9 @@
use thiserror::Error;

use crate::entity::Entity;

#[derive(Error, Debug)]
pub enum Error {
#[error("Entity is not alive: {0}!")]
EntityIsNotAlive(Entity),
}

+ 1
- 0
async-ecs/src/lib.rs Zobrazit soubor

@@ -1,6 +1,7 @@
pub mod access; pub mod access;
pub mod component; pub mod component;
pub mod entity; pub mod entity;
pub mod error;
pub mod misc; pub mod misc;
pub mod resource; pub mod resource;
pub mod storage; pub mod storage;


+ 2
- 9
async-ecs/src/main.rs Zobrazit soubor

@@ -4,13 +4,6 @@ use async_ecs::{VecStorage, World};
use async_ecs_derive::Component; use async_ecs_derive::Component;
use rand::random; use rand::random;


/*
use std::time::{Duration, Instant};

use async_ecs::Resources;

*/

fn main() { fn main() {
env_logger::builder() env_logger::builder()
.filter_level(log::LevelFilter::Info) .filter_level(log::LevelFilter::Info)
@@ -24,7 +17,6 @@ fn main() {
world.register_component::<Velocity>(); world.register_component::<Velocity>();
world.register_component::<Acceleration>(); world.register_component::<Acceleration>();


/*
for _ in 0..ENTITY_COUNT { for _ in 0..ENTITY_COUNT {
world world
.create_entity() .create_entity()
@@ -36,6 +28,7 @@ fn main() {


info!("World initialized!"); info!("World initialized!");


/*
let mut dispatcher = DispatcherBuilder::new() let mut dispatcher = DispatcherBuilder::new()
.with(Move, "move", &[]) .with(Move, "move", &[])
.with(Accelerate, "accelerate", &["move"]) .with(Accelerate, "accelerate", &["move"])
@@ -65,7 +58,7 @@ fn main() {
*/ */
} }


const ENTITY_COUNT: usize = 3_000_000;
const ENTITY_COUNT: usize = 100_000;
const REPEAT_COUNT: u32 = 100; const REPEAT_COUNT: u32 = 100;


#[derive(Debug, Component)] #[derive(Debug, Component)]


+ 1
- 4
async-ecs/src/resource/entry.rs Zobrazit soubor

@@ -33,9 +33,6 @@ where
let inner = self.inner.or_insert_with(move || Cell::new(Box::new(f()))); let inner = self.inner.or_insert_with(move || Cell::new(Box::new(f())));
let inner = inner.borrow_mut().map(Box::as_mut); let inner = inner.borrow_mut().map(Box::as_mut);


RefMut {
inner,
phantom: PhantomData,
}
RefMut::new(inner)
} }
} }

+ 4
- 236
async-ecs/src/resource/mod.rs Zobrazit soubor

@@ -1,137 +1,18 @@
pub mod cell; pub mod cell;
pub mod entry; pub mod entry;
pub mod resources;

pub use resources::{Ref, RefMut, Resources};


use std::any::TypeId; use std::any::TypeId;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};


use hashbrown::HashMap;
use mopa::Any; use mopa::Any;


use cell::{Cell, Ref as CellRef, RefMut as CellRefMut};
use entry::Entry;

#[derive(Default)]
pub struct Resources {
resources: HashMap<ResourceId, Cell<Box<dyn Resource>>>,
}
pub trait Resource: Any + Send + Sync + 'static {}


#[derive(Debug, Hash, Eq, PartialEq)] #[derive(Debug, Hash, Eq, PartialEq)]
pub struct ResourceId(TypeId); pub struct ResourceId(TypeId);


pub trait Resource: Any + Send + Sync + 'static {}

pub struct Ref<'a, R: 'a> {
inner: CellRef<'a, dyn Resource>,
phantom: PhantomData<&'a R>,
}

pub struct RefMut<'a, R: 'a> {
inner: CellRefMut<'a, dyn Resource>,
phantom: PhantomData<&'a R>,
}

macro_rules! fetch_panic {
() => {{
panic!(
"\
Tried to fetch resource from the resources map, but the resource does not exist.\n\
\n\
Resource: `{resource_name_full}`\n\
\n\
You may ensure the resource exists!\
",
resource_name_full = std::any::type_name::<R>(),
)
}};
}

/* Resources */

impl Resources {
pub fn entry<R>(&mut self) -> Entry<R>
where
R: Resource,
{
Entry::new(self.resources.entry(ResourceId::new::<R>()))
}

pub fn insert<R>(&mut self, r: R)
where
R: Resource,
{
self.resources
.insert(ResourceId::new::<R>(), Cell::new(Box::new(r)));
}

pub fn remove<R>(&mut self) -> Option<R>
where
R: Resource,
{
self.resources
.remove(&ResourceId::new::<R>())
.map(Cell::into_inner)
.map(|x: Box<dyn Resource>| x.downcast())
.map(|x: Result<Box<R>, _>| x.ok().unwrap())
.map(|x| *x)
}

pub fn contains<R>(&self) -> bool
where
R: Resource,
{
self.resources.contains_key(&ResourceId::new::<R>())
}

pub fn borrow<R>(&self) -> Ref<R>
where
R: Resource,
{
self.try_borrow().unwrap_or_else(|| fetch_panic!())
}

pub fn try_borrow<R>(&self) -> Option<Ref<R>>
where
R: Resource,
{
self.resources.get(&ResourceId::new::<R>()).map(|r| Ref {
inner: CellRef::map(r.borrow(), Box::as_ref),
phantom: PhantomData,
})
}

pub fn borrow_mut<R>(&self) -> RefMut<R>
where
R: Resource,
{
self.try_borrow_mut().unwrap_or_else(|| fetch_panic!())
}

pub fn try_borrow_mut<R>(&self) -> Option<RefMut<R>>
where
R: Resource,
{
self.resources.get(&ResourceId::new::<R>()).map(|r| RefMut {
inner: r.borrow_mut().map(Box::as_mut),
phantom: PhantomData,
})
}

pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> {
self.get_resource_mut(ResourceId::new::<R>())
.map(|res| res.downcast_mut().unwrap())
}

pub fn get_resource_mut(&mut self, id: ResourceId) -> Option<&mut dyn Resource> {
self.resources
.get_mut(&id)
.map(Cell::get_mut)
.map(Box::as_mut)
}
}

/* ResourceId */

impl ResourceId { impl ResourceId {
pub fn new<R>() -> Self pub fn new<R>() -> Self
where where
@@ -140,116 +21,3 @@ impl ResourceId {
Self(TypeId::of::<R>()) Self(TypeId::of::<R>())
} }
} }

/* Resource */

impl<T> Resource for T where T: Any + Send + Sync {}

mod __resource_mopafy_scope {
#![allow(clippy::all)]

use mopa::mopafy;

use super::Resource;

mopafy!(Resource);
}

/* Ref */

impl<'a, R> Deref for Ref<'a, R>
where
R: Resource,
{
type Target = R;

fn deref(&self) -> &R {
unsafe { self.inner.downcast_ref_unchecked() }
}
}

impl<'a, R> Clone for Ref<'a, R> {
fn clone(&self) -> Self {
Ref {
inner: self.inner.clone(),
phantom: PhantomData,
}
}
}

/* RefMut */

impl<'a, R> Deref for RefMut<'a, R>
where
R: Resource,
{
type Target = R;

fn deref(&self) -> &R {
unsafe { self.inner.downcast_ref_unchecked() }
}
}

impl<'a, R> DerefMut for RefMut<'a, R>
where
R: Resource,
{
fn deref_mut(&mut self) -> &mut R {
unsafe { self.inner.downcast_mut_unchecked() }
}
}

#[cfg(test)]
mod tests {
use super::*;

#[derive(Default)]
struct Res;

#[test]
fn insert() {
struct Foo;

let mut resources = Resources::default();
resources.insert(Res);

assert!(resources.contains::<Res>());
assert!(!resources.contains::<Foo>());
}

#[test]
#[should_panic(expected = "but it was already borrowed")]
fn read_write_fails() {
let mut resources = Resources::default();
resources.insert(Res);

let _read = resources.borrow::<Res>();
let _write = resources.borrow_mut::<Res>();
}

#[test]
#[should_panic(expected = "but it was already borrowed mutably")]
fn write_read_fails() {
let mut resources = Resources::default();
resources.insert(Res);

let _write = resources.borrow_mut::<Res>();
let _read = resources.borrow::<Res>();
}

#[test]
fn remove_insert() {
let mut resources = Resources::default();
resources.insert(Res);

assert!(resources.contains::<Res>());

resources.remove::<Res>().unwrap();

assert!(!resources.contains::<Res>());

resources.insert(Res);

assert!(resources.contains::<Res>());
}
}

+ 256
- 0
async-ecs/src/resource/resources.rs Zobrazit soubor

@@ -0,0 +1,256 @@
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};

use hashbrown::HashMap;
use mopa::Any;

use super::{
cell::{Cell, Ref as CellRef, RefMut as CellRefMut},
entry::Entry,
Resource, ResourceId,
};

#[derive(Default)]
pub struct Resources {
resources: HashMap<ResourceId, Cell<Box<dyn Resource>>>,
}

pub struct Ref<'a, R: 'a> {
inner: CellRef<'a, dyn Resource>,
phantom: PhantomData<&'a R>,
}

pub struct RefMut<'a, R: 'a> {
inner: CellRefMut<'a, dyn Resource>,
phantom: PhantomData<&'a R>,
}

macro_rules! fetch_panic {
() => {{
panic!(
"\
Tried to fetch resource from the resources map, but the resource does not exist.\n\
\n\
Resource: `{resource_name_full}`\n\
\n\
You may ensure the resource exists!\
",
resource_name_full = std::any::type_name::<R>(),
)
}};
}

/* Resources */

impl Resources {
pub fn entry<R>(&mut self) -> Entry<R>
where
R: Resource,
{
Entry::new(self.resources.entry(ResourceId::new::<R>()))
}

pub fn insert<R>(&mut self, r: R)
where
R: Resource,
{
self.resources
.insert(ResourceId::new::<R>(), Cell::new(Box::new(r)));
}

pub fn remove<R>(&mut self) -> Option<R>
where
R: Resource,
{
self.resources
.remove(&ResourceId::new::<R>())
.map(Cell::into_inner)
.map(|x: Box<dyn Resource>| x.downcast())
.map(|x: Result<Box<R>, _>| x.ok().unwrap())
.map(|x| *x)
}

pub fn contains<R>(&self) -> bool
where
R: Resource,
{
self.resources.contains_key(&ResourceId::new::<R>())
}

pub fn borrow<R>(&self) -> Ref<R>
where
R: Resource,
{
self.try_borrow().unwrap_or_else(|| fetch_panic!())
}

pub fn try_borrow<R>(&self) -> Option<Ref<R>>
where
R: Resource,
{
self.resources.get(&ResourceId::new::<R>()).map(|r| Ref {
inner: CellRef::map(r.borrow(), Box::as_ref),
phantom: PhantomData,
})
}

pub fn borrow_mut<R>(&self) -> RefMut<R>
where
R: Resource,
{
self.try_borrow_mut().unwrap_or_else(|| fetch_panic!())
}

pub fn try_borrow_mut<R>(&self) -> Option<RefMut<R>>
where
R: Resource,
{
self.resources.get(&ResourceId::new::<R>()).map(|r| RefMut {
inner: r.borrow_mut().map(Box::as_mut),
phantom: PhantomData,
})
}

pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> {
self.get_resource_mut(ResourceId::new::<R>())
.map(|res| res.downcast_mut().unwrap())
}

pub fn get_resource_mut(&mut self, id: ResourceId) -> Option<&mut dyn Resource> {
self.resources
.get_mut(&id)
.map(Cell::get_mut)
.map(Box::as_mut)
}
}

/* Resource */

impl<T> Resource for T where T: Any + Send + Sync {}

mod __resource_mopafy_scope {
#![allow(clippy::all)]

use mopa::mopafy;

use super::super::Resource;

mopafy!(Resource);
}

/* Ref */

impl<'a, R> Ref<'a, R> {
pub fn new(inner: CellRef<'a, dyn Resource>) -> Self {
Self {
inner,
phantom: PhantomData,
}
}
}

impl<'a, R> Deref for Ref<'a, R>
where
R: Resource,
{
type Target = R;

fn deref(&self) -> &R {
unsafe { self.inner.downcast_ref_unchecked() }
}
}

impl<'a, R> Clone for Ref<'a, R> {
fn clone(&self) -> Self {
Ref {
inner: self.inner.clone(),
phantom: PhantomData,
}
}
}

/* RefMut */

impl<'a, R> RefMut<'a, R> {
pub fn new(inner: CellRefMut<'a, dyn Resource>) -> Self {
Self {
inner,
phantom: PhantomData,
}
}
}

impl<'a, R> Deref for RefMut<'a, R>
where
R: Resource,
{
type Target = R;

fn deref(&self) -> &R {
unsafe { self.inner.downcast_ref_unchecked() }
}
}

impl<'a, R> DerefMut for RefMut<'a, R>
where
R: Resource,
{
fn deref_mut(&mut self) -> &mut R {
unsafe { self.inner.downcast_mut_unchecked() }
}
}

#[cfg(test)]
mod tests {
use super::*;

#[derive(Default)]
struct Res;

#[test]
fn insert() {
struct Foo;

let mut resources = Resources::default();
resources.insert(Res);

assert!(resources.contains::<Res>());
assert!(!resources.contains::<Foo>());
}

#[test]
#[should_panic(expected = "but it was already borrowed")]
fn read_write_fails() {
let mut resources = Resources::default();
resources.insert(Res);

let _read = resources.borrow::<Res>();
let _write = resources.borrow_mut::<Res>();
}

#[test]
#[should_panic(expected = "but it was already borrowed mutably")]
fn write_read_fails() {
let mut resources = Resources::default();
resources.insert(Res);

let _write = resources.borrow_mut::<Res>();
let _read = resources.borrow::<Res>();
}

#[test]
fn remove_insert() {
let mut resources = Resources::default();
resources.insert(Res);

assert!(resources.contains::<Res>());

resources.remove::<Res>().unwrap();

assert!(!resources.contains::<Res>());

resources.insert(Res);

assert!(resources.contains::<Res>());
}
}

+ 18
- 1
async-ecs/src/storage/masked_storage.rs Zobrazit soubor

@@ -1,6 +1,8 @@
use std::mem::swap;

use hibitset::BitSet; use hibitset::BitSet;


use crate::component::Component;
use crate::{component::Component, entity::Entity, storage::Storage};


pub struct MaskedStorage<T: Component> { pub struct MaskedStorage<T: Component> {
mask: BitSet, mask: BitSet,
@@ -14,4 +16,19 @@ impl<T: Component> MaskedStorage<T> {
inner, inner,
} }
} }

pub fn insert(&mut self, entity: Entity, mut component: T) -> Option<T> {
let index = entity.index();

if self.mask.contains(index) {
swap(&mut component, self.inner.get_mut(index));

Some(component)
} else {
self.mask.add(index);
self.inner.insert(index, component);

None
}
}
} }

+ 6
- 2
async-ecs/src/storage/mod.rs Zobrazit soubor

@@ -6,6 +6,10 @@ pub use masked_storage::MaskedStorage;
pub use storage_wrapper::StorageWrapper; pub use storage_wrapper::StorageWrapper;
pub use vec_storage::VecStorage; pub use vec_storage::VecStorage;


use crate::misc::TryDefault;
use crate::{entity::Index, misc::TryDefault};


pub trait Storage<T>: TryDefault {}
pub trait Storage<T>: TryDefault {
fn get(&self, index: Index) -> &T;
fn get_mut(&mut self, index: Index) -> &mut T;
fn insert(&mut self, index: Index, value: T);
}

+ 22
- 1
async-ecs/src/storage/storage_wrapper.rs Zobrazit soubor

@@ -1,6 +1,13 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::DerefMut;


use crate::{entity::Entities, resource::Ref};
use crate::{
component::Component,
entity::{Entities, Entity},
error::Error,
resource::Ref,
storage::MaskedStorage,
};


pub struct StorageWrapper<'a, T, D> { pub struct StorageWrapper<'a, T, D> {
data: D, data: D,
@@ -17,3 +24,17 @@ impl<'a, T, D> StorageWrapper<'a, T, D> {
} }
} }
} }

impl<'a, T, D> StorageWrapper<'a, T, D>
where
T: Component,
D: DerefMut<Target = MaskedStorage<T>>,
{
pub fn insert(&mut self, entity: Entity, component: T) -> Result<Option<T>, Error> {
if !self.entities.is_alive(entity) {
return Err(Error::EntityIsNotAlive(entity));
}

Ok(self.data.insert(entity, component))
}
}

+ 29
- 1
async-ecs/src/storage/vec_storage.rs Zobrazit soubor

@@ -1,10 +1,38 @@
use std::mem::MaybeUninit; use std::mem::MaybeUninit;


use crate::entity::Index;

use super::Storage; use super::Storage;


pub struct VecStorage<T>(Vec<MaybeUninit<T>>); pub struct VecStorage<T>(Vec<MaybeUninit<T>>);


impl<T> Storage<T> for VecStorage<T> {}
impl<T> Storage<T> for VecStorage<T> {
fn get(&self, index: Index) -> &T {
unsafe { &*self.0.get_unchecked(index as usize).as_ptr() }
}

fn get_mut(&mut self, index: Index) -> &mut T {
unsafe { &mut *self.0.get_unchecked_mut(index as usize).as_mut_ptr() }
}

fn insert(&mut self, index: Index, value: T) {
let index = index as usize;

if self.0.len() <= index {
let delta = index + 1 - self.0.len();

self.0.reserve(delta);

unsafe {
self.0.set_len(index + 1);
}
}

unsafe {
*self.0.get_unchecked_mut(index) = MaybeUninit::new(value);
}
}
}


impl<T> Default for VecStorage<T> { impl<T> Default for VecStorage<T> {
fn default() -> Self { fn default() -> Self {


+ 5
- 1
async-ecs/src/world/mod.rs Zobrazit soubor

@@ -7,7 +7,7 @@ use std::ops::{Deref, DerefMut};
use crate::{ use crate::{
access::{Read, ReadStorage, WriteStorage}, access::{Read, ReadStorage, WriteStorage},
component::Component, component::Component,
entity::Entities,
entity::{Builder, Entities},
resource::{Ref, RefMut, Resource, Resources}, resource::{Ref, RefMut, Resource, Resources},
storage::MaskedStorage, storage::MaskedStorage,
system::SystemData, system::SystemData,
@@ -59,6 +59,10 @@ impl World {
pub fn component_mut<T: Component>(&self) -> WriteStorage<T> { pub fn component_mut<T: Component>(&self) -> WriteStorage<T> {
WriteStorage::fetch(&self) WriteStorage::fetch(&self)
} }

pub fn create_entity(&mut self) -> Builder {
Builder::new(self)
}
} }


impl Default for World { impl Default for World {


Načítá se…
Zrušit
Uložit