Skip to content

Commit

Permalink
Updated ComponentManager to implement IEnumerable<ComponentMapper> (#823
Browse files Browse the repository at this point in the history
) (#888)
  • Loading branch information
mbowersjr authored Jun 11, 2024
1 parent 6627756 commit 82daabb
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 6 deletions.
73 changes: 69 additions & 4 deletions source/MonoGame.Extended.Entities/ComponentManager.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,48 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using Microsoft.Xna.Framework;
using MonoGame.Extended.Collections;
using MonoGame.Extended.Entities.Systems;

namespace MonoGame.Extended.Entities
{
public interface IComponentMapperService
public interface IComponentMapperService : IEnumerable<ComponentMapper>
{
ComponentMapper<T> GetMapper<T>() where T : class;
ComponentMapper this[Type type] { get; }
}

public class ComponentManager : UpdateSystem, IComponentMapperService
public class ComponentManager : UpdateSystem, IComponentMapperService, IEnumerable<ComponentMapper>
{
public ComponentManager()
{
_componentMappers = new Bag<ComponentMapper>();
_componentTypes = new Dictionary<Type, int>();
}

private readonly Bag<ComponentMapper> _componentMappers;
private readonly Dictionary<Type, int> _componentTypes;
internal readonly Bag<ComponentMapper> _componentMappers;
internal readonly Dictionary<Type, int> _componentTypes;

public Action<int> ComponentsChanged;

private ComponentMapper CreateMapperForType(Type type, int componentTypeId)
{
if (!type.IsClass)
throw new ArgumentException("Type must be a class type.", nameof(type));

// TODO: We can probably do better than this without a huge performance penalty by creating our own bit vector that grows after the first 32 bits.
if (componentTypeId >= 32)
throw new InvalidOperationException("Component type limit exceeded. We currently only allow 32 component types for performance reasons.");

var mapperType = typeof(ComponentMapper<>).MakeGenericType(type);
var mapper = Activator.CreateInstance(mapperType, args: new object[] { componentTypeId, ComponentsChanged });
_componentMappers[componentTypeId] = (ComponentMapper)mapper;
return (ComponentMapper)mapper;
}

private ComponentMapper<T> CreateMapperForType<T>(int componentTypeId)
where T : class
{
Expand Down Expand Up @@ -86,5 +104,52 @@ public void Destroy(int entityId)
public override void Update(GameTime gameTime)
{
}

public ComponentMapper this[Type type]
{
get
{
var componentTypeId = GetComponentTypeId(type);

if (_componentMappers[componentTypeId] != null)
return _componentMappers[componentTypeId];

return CreateMapperForType(type, componentTypeId);
}
}

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

public IEnumerator<ComponentMapper> GetEnumerator() => new ComponentMapperEnumerator(this);

public struct ComponentMapperEnumerator : IEnumerator<ComponentMapper>
{
private readonly ComponentManager _componentManager;
private IEnumerator<ComponentMapper> _enumerator;

internal ComponentMapperEnumerator(ComponentManager componentManager)
{
_componentManager = componentManager;
_enumerator = _componentManager._componentMappers.GetEnumerator();
}

public bool MoveNext()
{
return _enumerator.MoveNext();
}

public void Reset()
{
_enumerator.Reset();
}

object IEnumerator.Current => _enumerator.Current;
public ComponentMapper Current => _enumerator.Current;

public void Dispose()
{
_enumerator?.Dispose();
}
}
}
}
42 changes: 40 additions & 2 deletions tests/MonoGame.Extended.Entities.Tests/ComponentManagerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using MonoGame.Extended.Sprites;
using System.Collections.Generic;
using MonoGame.Extended.Sprites;
using Xunit;

namespace MonoGame.Extended.Entities.Tests
Expand Down Expand Up @@ -43,5 +44,42 @@ public void GetComponentTypeId()

// Assert.Equal(0b101, identity);
//}


[Fact]
public void GetMapperForTypeByIndexer()
{
var componentManager = new ComponentManager();
var transformMapper = componentManager[typeof(Transform2)];
var spriteMapper = componentManager[typeof(Sprite)];

Assert.IsType<ComponentMapper<Transform2>>(transformMapper);
Assert.IsType<ComponentMapper<Sprite>>(spriteMapper);
Assert.Equal(0, transformMapper.Id);
Assert.Equal(1, spriteMapper.Id);
Assert.Same(spriteMapper, componentManager[typeof(Sprite)]);
}

[Fact]
public void EnumerateMappers()
{
var componentManager = new ComponentManager();

var transformMapper = componentManager.GetMapper<Transform2>();
var spriteMapper = componentManager.GetMapper<Sprite>();
var cameraMapper = componentManager.GetMapper<OrthographicCamera>();

List<ComponentMapper> enumeratedMappers = new List<ComponentMapper>();

foreach (ComponentMapper mapper in componentManager)
{
enumeratedMappers.Add(mapper);
}

Assert.Equal(3, enumeratedMappers.Count);
Assert.Contains(transformMapper, enumeratedMappers);
Assert.Contains(spriteMapper, enumeratedMappers);
Assert.Contains(cameraMapper, enumeratedMappers);
}
}
}
}

0 comments on commit 82daabb

Please sign in to comment.