Component是ECS中最简单的部分,它表示的是Entity上的数据部分,类似于Unity中GameObject上的Component,一个Component可以有许多属性,也可以是空的等等,下面一一来看。
空的Component
[Game]
public class TestComponent : IComponent
{
}
任何自定义的Component(可以是class,struct,interface)都需要实现IComponent,创建好后,点击Tools/Jenny/Generate,生成Generate Code,代码如下:
public partial class GameEntity {
static readonly TestComponent testComponent = new TestComponent();
public bool isTest {
get { return HasComponent(GameComponentsLookup.Test); }
set {
if (value != isTest) {
var index = GameComponentsLookup.Test;
if (value) {
var componentPool = GetComponentPool(index);
var component = componentPool.Count > 0
? componentPool.Pop()
: testComponent;
AddComponent(index, component);
} else {
RemoveComponent(index);
}
}
}
}
}
- 所以我们可以通过
entity.isTest
查看该entity是否还有该Component,可以通过entity.isTest = true
为entity添加该Component,通过entity.isTest = false
删除组件。 - 我们可以为空的Component添加前缀(FlagPrefix),如下所示:
[Game,FlagPrefix("My")]
public class TestComponent : IComponent
{
}
再次生成一次Generate Code,如下:
public partial class GameEntity {
static readonly TestComponent testComponent = new TestComponent();
public bool myTest {
get { return HasComponent(GameComponentsLookup.Test); }
set {
if (value != myTest) {
var index = GameComponentsLookup.Test;
if (value) {
var componentPool = GetComponentPool(index);
var component = componentPool.Count > 0
? componentPool.Pop()
: testComponent;
AddComponent(index, component);
} else {
RemoveComponent(index);
}
}
}
}
}
只是isTest变为了myTest,my刚好是我们自定义的前缀,所以这样可以帮助我们更好的理解这个Component的含义,而使用方法却没有任何区别。
- 若FlagPrefix修饰的Component不是空的,则FlagPrefix不会生效。
- Component上的Game是一个Contexts标记,也就是说任何包含了该Component的entity 都可以在GameContext中找到,而我们可以通过如下方式,添加完成点击Generate,其中game对应于GameContext,input对应于InputContext。
只包含值类型的数据
[Game]
public class PositionComponent : IComponent
{
public Vector3 pos;
}
生成代码如下:
public partial class GameEntity {
public PositionComponent position { get { return (PositionComponent)GetComponent(GameComponentsLookup.Position); } }
public bool hasPosition { get { return HasComponent(GameComponentsLookup.Position); } }
public void AddPosition(UnityEngine.Vector3 newPos) {
var index = GameComponentsLookup.Position;
var component = (PositionComponent)CreateComponent(index, typeof(PositionComponent));
component.pos = newPos;
AddComponent(index, component);
}
public void ReplacePosition(UnityEngine.Vector3 newPos) {
var index = GameComponentsLookup.Position;
var component = (PositionComponent)CreateComponent(index, typeof(PositionComponent));
component.pos = newPos;
ReplaceComponent(index, component);
}
public void RemovePosition() {
RemoveComponent(GameComponentsLookup.Position);
}
}
- 可以通过
entity.position
获取Component, - 通过
entity.hasPosition
查看是否包含该组件, - 通过
entity.AddPosition(1,2)
为entity添加Component, - 通过
entity.RemovePosition()
删除组件。
最重要的是每个entity只可以含有一个同类型的Component,使用ReplaceComponent方法可以避免由于创建Component而导致的性能消耗
引用Component
[Game]
public class ViewComponent : IComponent
{
public GameObject obj;
}
生成代码如下:
public partial class GameEntity {
public ViewComponent view { get { return (ViewComponent)GetComponent(GameComponentsLookup.View); } }
public bool hasView { get { return HasComponent(GameComponentsLookup.View); } }
public void AddView(UnityEngine.GameObject newObj) {
var index = GameComponentsLookup.View;
var component = (ViewComponent)CreateComponent(index, typeof(ViewComponent));
component.obj = newObj;
AddComponent(index, component);
}
public void ReplaceView(UnityEngine.GameObject newObj) {
var index = GameComponentsLookup.View;
var component = (ViewComponent)CreateComponent(index, typeof(ViewComponent));
component.obj = newObj;
ReplaceComponent(index, component);
}
public void RemoveView() {
RemoveComponent(GameComponentsLookup.View);
}
}
与上面的PositionComponent生成代码类似,只不过其属性变为了引用类型的对象,而且其引用的对象可能很复杂。
一些常用的Attribute(修改Component或者其属性)
Unique
表示只有一个entity可以拥有此Component,若超过一个便会报错。例如:
[Game,Unique]
public class TestComponent : IComponent
{
public Vector3 pos;
}
其生成代码如下:
public partial class GameContext {
public GameEntity testEntity { get { return GetGroup(GameMatcher.Test).GetSingleEntity(); } }
public TestComponent test { get { return testEntity.test; } }
public bool hasTest { get { return testEntity != null; } }
public GameEntity SetTest(UnityEngine.Vector3 newPos) {
if (hasTest) {
throw new Entitas.EntitasException("Could not set Test!\n" + this + " already has an entity with TestComponent!",
"You should check if the context already has a testEntity before setting it or use context.ReplaceTest().");
}
var entity = CreateEntity();
entity.AddTest(newPos);
return entity;
}
public void ReplaceTest(UnityEngine.Vector3 newPos) {
var entity = testEntity;
if (entity == null) {
entity = SetTest(newPos);
} else {
entity.ReplaceTest(newPos);
}
}
public void RemoveTest() {
testEntity.Destroy();
}
}
public partial class GameEntity {
public TestComponent test { get { return (TestComponent)GetComponent(GameComponentsLookup.Test); } }
public bool hasTest { get { return HasComponent(GameComponentsLookup.Test); } }
public void AddTest(UnityEngine.Vector3 newPos) {
var index = GameComponentsLookup.Test;
var component = (TestComponent)CreateComponent(index, typeof(TestComponent));
component.pos = newPos;
AddComponent(index, component);
}
public void ReplaceTest(UnityEngine.Vector3 newPos) {
var index = GameComponentsLookup.Test;
var component = (TestComponent)CreateComponent(index, typeof(TestComponent));
component.pos = newPos;
ReplaceComponent(index, component);
}
public void RemoveTest() {
RemoveComponent(GameComponentsLookup.Test);
}
}
通过生成代码可以看到,与其他Component不同的是,我们可以直接通过GameContext访问拥有这个Component的entity和操作这个Component.例如:
GameContext gameContext = Contexts.sharedInstance.game;
if (!gameContext.hasTest)
{
gameContext.SetTest(Vector3.one); //添加
}
gameContext.ReplaceTest(Vector3.down);//替换
gameContext.RemoveTest();//删除
等价于
GameContext gameContext = Contexts.sharedInstance.game;
var entity = gameContext.CreateEntity();
entity.AddTest(Vector3.one);//添加
entity.ReplaceTest(Vector3.down);//替换
entity.RemoveTest();//删除
PrimaryEntityIndex
修饰Component的属性,所修饰的属性的值不能有重复的,可以用于修饰id例如:
[Game]
public class TestComponent : IComponent
{
[PrimaryEntityIndex]
public Vector3 pos; //表示拥有这个组件的pos不可以是重复的
}
可以通过GetEntityWithTest
快速获得有某个pos的entity。
GameContext gameContext = Contexts.sharedInstance.game;
gameContext.GetEntityWithTest(Vector3.back);
EntityIndex
修饰Component的属性,类似于unity里的tag,例如:
[Game]
public class TestComponent : IComponent
{
[EntityIndex]
public Vector3 pos;
}
可以利用GetEntitiesWithTest
获得拥有该Component,并且Component的值为传入的值的所有entity
GameContext gameContext = Contexts.sharedInstance.game;
gameContext.GetEntitiesWithTest(Vector3.back);