Unity Entity Component System --- System State Components

SystemStateComponentData的作用,是跟踪资源在系统内部的状态,以提供机会在适当的时机创建和销毁资源,而不是依靠某个回调函数。

SystemStateComponentData和SystemStateSharedComponentData跟ComponentData以及SharedComponentData一样,各自都有一个重要的概念:
SystemStateComponentData在销毁entity时,不会被销毁。

销毁的简单流程如下:

  • 找到引用该entity ID的所有的Component Data
  • 删除这些components
  • 回收entity ID,以重复使用。

然而,如果entity有SystemStateComponentData,它不会被移除。这给system机会来清理该entity ID的资源以及相关状态。只有当SystemStateComponentData被移除后,entity ID才能被重复使用。

Motivation 目的

  • System可能需要维持基于ComponentData的一个内部状态。例如,资源是否分配。
  • System需要能管理由其它系统对该值或状态的更新。例如,值改变了,或者相关组件添加或删除。
  • “无回调”,时ECS设计准则的重要概念。

Concept

一个用法是镜像一个用户的组件的内部状态。
例如:

  1. FooComponent(ComponentData,用户创建)
  2. FooStateComponent(SystemComponentData,系统创建)

Detecting Component Add 监测添加组件

当添加FooComponent时,FooStateComponent还不存在。Foo System查询到添加了FooComponent但是没有FooStateComponent,则可以推断出该FooComponent是新添加的。同时Foo System会添加FooStateComponent及其它需要的内部状态。

Detecting Component Remove 检测删除组件

当删除FooComponent组件时,FooStateComponent依然存在。Foo System更新时发现有FooStateComponent但是没有FooComponent,则可以推断出FooComponent被删除了。这时Foo System会删除FooStateComponent并根据需要恢复其它内部状态。

Detecting Destroy Entity 监测销毁实体

实体的销毁,可以简化为步骤:

  1. 查找到引用该entity ID的所有的components
  2. 删除这些components
  3. 回收 entity ID

然而,调用Destroy Entity时,SystemStateComponentData没有被移除,entity ID也不会被回收,直到最后一个组件被删除。这让系统可以用与删除组件相同的方式,清理内部状态。

SystemStateComponent

SystemStateComponent和ComponentData类似,用法也类似:

struct FooStateComponent : ISystemStateComponent
{
}

对于成员,也可以用public,private,protected来修饰可访问性。但是,我们最好在创建该组件的系统内更新,改变它的值,而在该系统之外,是只读的。

SystemStateSharedComponent

SystemStateSharedComponent与SharedComponentData用法类似:

struct FooStateSharedComponent : ISystemStateSharedComponentData
{
	public int Value;
}

Example system using state components

下面的例子,用一个简单的系统,展示了如何利用system state component来管理entities。例子定义了一个普通的IComponentData和它的ISystemStateComponentData实例,还定义了三个对该类entities的query查询:

  1. m_newEntities 选择有普通component但是没有system state component的entities,这些entities是新创建的。系统执行job,为它们添加system state component。
  2. m_activeEneities选择同时有component和system state component的entities。在实际应用中,其它系统也可能会处理或者销毁这些entities。
  3. m_destroyedEntities选择了有system state component但是没有component的entities,这些实体是被本系统,或者其它系统删除的entities。该系统运行一个job将system state component从entities上删除,以便ECS可以回收该entity ID。

注意我们的这个简化的例子,并没有处理任何状态,system state component的一个作用就是跟踪资源的分配和清理。

using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using UnityEngine; 

public struct GeneralPurposeComponentA : IComponentData
{    
	public bool IsAlive;
} 

public struct StateComponentB : ISystemStateComponentData
{    
	public int State;
} 

public class StatefulSystem : JobComponentSystem
{    
	private EntityQuery m_newEntities;    
	private EntityQuery m_activeEntities;    
	private EntityQuery m_destroyedEntities;    
	private EntityCommandBufferSystem m_ECBSource;     
	protected override void OnCreate()    
	{        
		// Entities with GeneralPurposeComponentA but not StateComponentB        
		m_newEntities = GetEntityQuery(new EntityQueryDesc()        
		{            
			All = new ComponentType[] {ComponentType.ReadOnly<GeneralPurposeComponentA>()},
			None = new ComponentType[] {ComponentType.ReadWrite<StateComponentB>()}        
		});         

		// Entities with both GeneralPurposeComponentA and StateComponentB        
		m_activeEntities = GetEntityQuery(new EntityQueryDesc()        
		{            
			All = new ComponentType[] 
				{
					ComponentType.ReadWrite<GeneralPurposeComponentA>(),
					ComponentType.ReadOnly<StateComponentB>()            
				}        
		});
		
		// Entities with StateComponentB but not GeneralPurposeComponentA        
		m_destroyedEntities = GetEntityQuery(new EntityQueryDesc()        
		{            
			All = new ComponentType[] {ComponentType.ReadWrite<StateComponentB>()},            
			None = new ComponentType[] {ComponentType.ReadOnly<GeneralPurposeComponentA>()}        
		});         

		m_ECBSource = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();    
	}     

	struct NewEntityJob : IJobForEachWithEntity<GeneralPurposeComponentA>    
	{        
		public EntityCommandBuffer.Concurrent ConcurrentECB;         

		public void Execute(Entity entity, int index, [ReadOnly] ref GeneralPurposeComponentA gpA)        
		{            
			// Add an ISystemStateComponentData instance            
			ConcurrentECB.AddComponent<StateComponentB>(index, entity, new StateComponentB() {State = 1});        
		}    
	}     

	struct ProcessEntityJob : IJobForEachWithEntity<GeneralPurposeComponentA>    
	{        
		public EntityCommandBuffer.Concurrent ConcurrentECB;         

		public void Execute(Entity entity, int index, ref GeneralPurposeComponentA gpA)        
		{            
			// Process entity, possibly setting IsAlive false --            
			// In which case, destroy the entity            
			if (!gpA.IsAlive)            
			{                
				ConcurrentECB.DestroyEntity(index, entity);            
			}        
		}    
	}     

	struct CleanupEntityJob : IJobForEachWithEntity<StateComponentB>    
	{        
		public EntityCommandBuffer.Concurrent ConcurrentECB;         

		public void Execute(Entity entity, int index, [ReadOnly] ref StateComponentB state)        
		{            
			// This system is responsible for removing any ISystemStateComponentData instances it adds
			// Otherwise, the entity is never truly destroyed.            
			ConcurrentECB.RemoveComponent<StateComponentB>(index, entity);        
		}    
	}     

	protected override JobHandle OnUpdate(JobHandle inputDependencies)    
	{        
		var newEntityJob = new NewEntityJob()        
		{            
			ConcurrentECB = m_ECBSource.CreateCommandBuffer().ToConcurrent()        
		};        
		var newJobHandle = newEntityJob.ScheduleSingle(m_newEntities, inputDependencies);
		m_ECBSource.AddJobHandleForProducer(newJobHandle);
		         
		var processEntityJob = new ProcessEntityJob()            
		{
			ConcurrentECB = m_ECBSource.CreateCommandBuffer().ToConcurrent()
		};        
		var processJobHandle = processEntityJob.Schedule(m_activeEntities, newJobHandle);
		m_ECBSource.AddJobHandleForProducer(processJobHandle);         

		var cleanupEntityJob = new CleanupEntityJob()        
		{            
			ConcurrentECB = m_ECBSource.CreateCommandBuffer().ToConcurrent()        
		};        
		var cleanupJobHandle = cleanupEntityJob.ScheduleSingle(m_destroyedEntities, processJobHandle);
		m_ECBSource.AddJobHandleForProducer(cleanupJobHandle);         

		return cleanupJobHandle;    
	}     

	protected override void OnDestroy()    
	{        
		// Implement OnDestroy to cleanup any resources allocated by this system.        
		// (This simplified example does not allocate any resources.)    
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值