您现在的位置是:首页 > 文章详情

Unity 之 Pure版Entity Component System【ECS】 案例【GalacticConquest】解析【上】

日期:2018-09-25点击:519

以往没有了解过Unity ECS的小伙伴建议先看看我写过的两篇ECS文章

最近两月Unity官方一直在更新ECS的版本,有一些原来的工程在新的版本中是无法运行的,所以今天再写一篇示例解析,虽然ECS目前是测试版本,可能还会有很多的改变,正式版本上线的日期也没有明确说明,但还是希望能帮助喜欢新技术的小伙伴,互相帮助,互相学习~


有说的不准确和不正确的地方欢迎留言指正

大家的帮助是我写下去最有效的动力


点击下载工程

示例效果展示如下

img_3aa48488468484032738dbed0ed63956.gif

这个示例的规则是这样的,启动时随机生成大小位置不同的球体,然后从球体周围发射小飞船去攻击其他的星球,飞船分为红绿两队,占领后星球变成指定队伍的颜色


此次使用的Unity版本为 2018.2.9f1 Entities版本为0.0.12-preview.15。而且在启动Unity加载Entities的时候保持网路畅通,因为有朋友反映在内网无法使用Entities的情况。【最好能科学上网】

img_0dee20f4b38a5d561e5a75583f7cb9c1.png
img_5f8fc9e53cdc722d896baa2fbe8dd7ce.png

下面还是按照老规矩,分布逐渐创建工程

我们先创建一个PlanetSpawner脚本(产卵器)并添加到空物体Spawners上

img_ad7f58ee98d8917290a2100c5ed9e59b.png

作用如下:

  • 创建指定的数量的星球
  • 使星球位置随机分布
  • 向这些星球上动态添加数据
    • 星球旋转的数据
    • 星球旋所在的队伍、飞船数量、位置、半径数据(用于后期生产飞船使用)
  • 更改对应星球队伍的颜色
using System.Collections.Generic; using System.Linq; using Data; using Unity.Entities; using Unity.Mathematics; using UnityEngine; public class PlanetSpawner : MonoBehaviour { /// <summary> /// 星球预制体 /// </summary> [SerializeField] GameObject _planetPrefab; /// <summary> /// 初始化星球的个数 /// </summary> [SerializeField] int _initialCount = 20; /// <summary> /// 产生星球的随机半径 /// </summary> [SerializeField] readonly float radius = 100.00f; /// <summary> /// 场景中所有的Entity的控制者、容器 /// </summary> EntityManager _entityManager; /// <summary> /// 灰色 红色 绿色对应材质数组 /// </summary> [SerializeField] public Material[] _teamMaterials; /// <summary> /// 飞船Entity对应的GameObject 的字典 /// </summary> static Dictionary<Entity, GameObject> entities = new Dictionary<Entity, GameObject>(); public static Material[] TeamMaterials; void Awake() { TeamMaterials = _teamMaterials; } void OnEnable() { _entityManager = World.Active.GetOrCreateManager<EntityManager>(); //初始化 Instantiate(_initialCount); } /// <summary> /// 初始化 /// </summary> /// <param name="count">产生星球的数量</param> void Instantiate(int count) { //产生飞船队伍列表 1绿色 2 红色 var planetOwnership = new List<int> { 1, 1, 2, 2 }; for (var i = 0; i < count; ++i) { //获取星球对应的半径 var sphereRadius = UnityEngine.Random.Range(5.0f, 20.0f); var safe = false; float3 pos; int attempts = 0; do { if (++attempts >= 500) { Debug.Log("新创建的行星找不到合适的位置"); return; } //在半径为1的范围内返回一个随机点(只读) var randomValue = (Vector3)UnityEngine.Random.insideUnitSphere; randomValue.y = 0; //星球的实际位置 pos = (randomValue * radius) + new Vector3(transform.position.x, transform.position.z); //检测星球是否有重合的物体 var collisions = Physics.OverlapSphere(pos, sphereRadius); //如果没有重合的地方就是安全地 if (!collisions.Any()) safe = true; } while (!safe); //在半径为1的范围内返回一个随机点(只读) var randomRotation = UnityEngine.Random.insideUnitSphere; //实例化星球 var go = GameObject.Instantiate(_planetPrefab, pos, quaternion.identity); go.name = "Sphere_" + i; //获取星球上对应的 GameObjectEntity var planetEntity = go.GetComponent<GameObjectEntity>().Entity; //获取渲染星球的对应的子物体 var meshGo = go.GetComponentsInChildren<Transform>().First(c => c.gameObject != go).gameObject; //获取碰撞体 var collider = go.GetComponent<SphereCollider>(); //获取渲染星球的对应的子物体的 GameObjectEntity var meshEntity = meshGo.GetComponent<GameObjectEntity>().Entity; //把碰撞体的半径设置和圆球一直 collider.radius = sphereRadius; //半径*2等于实际扩大的倍数 meshGo.transform.localScale = new Vector3(sphereRadius * 2.0f, sphereRadius * 2.0f, sphereRadius * 2.0f); var planetData = new PlanetData { //星球所在的队伍 TeamOwnership = 0, //星球的半径 Radius = sphereRadius, //星球的位置 Position = pos }; var rotationData = new RotationData { RotationSpeed = randomRotation }; //队伍列表是否有任何元素 没有元素的划分为灰色星球 if (planetOwnership.Any()) { //给星球分队 【红队或者绿色队伍】 planetData.TeamOwnership = planetOwnership.First(); //移除对应的队伍 planetOwnership.Remove(planetData.TeamOwnership); } else { //设定飞船数量 planetData.Occupants = UnityEngine.Random.Range(1, 100); } //设置字典对应的GameObject entities[planetEntity] = go; //设置对于队伍的颜色 1绿色 2红色 SetColor(planetEntity, planetData.TeamOwnership); //动态添加对应的数据 减少了拖拖拽拽 _entityManager.AddComponentData(planetEntity, planetData); _entityManager.AddComponentData(meshEntity, rotationData); } } /// <summary> /// 设置对应星球的颜色 /// </summary> /// <param name="entity">对应字典</param> /// <param name="team"></param> public static void SetColor(Entity entity, int team) { var go = entities[entity]; go.GetComponentsInChildren<MeshRenderer>().First(c => c.gameObject != go).material = TeamMaterials[team]; } } 

解读一

初始化获取场景中所有的Entity的控制者、容器

img_2172c35828aae0f2e6249b1f9a5c4683.png

解读二

创建并初始化一个队伍列表,数组就是中的数字就是【_teamMaterials】中的索引,更改颜色会用到

img_c63e2e3dda2484372cd98c8dc56432ec.png

解读三

尝试为一个星球找到一个不与其他星球重叠的位置,最多尝试500,还找不到的化就会出现Log信息,其中一个与以往不同,也是ECS中大量使用的float3而不是原来常用的Vector3,这是因为float3更小巧,没有多余的信息数据占用内存。

img_1e9858f9853632ee439fe64b986b3785.png

解读四

常规实例化,不在熬述


img_dd4b620bb88a6cd6ccad7ca762f8c8ab.png

解读五

img_4d78a041f72ec5c08a65f43014b11420.png

这不部分就是ECS这种套路对数据相关的操作,事先准备好初始化的数据,然后用_entityManager对相应的实体添加纯数据,有点原来AddComponent的意思,也避免了拖拖拽拽。这里的数据分别为【PlanetData】【RotationData】

效果如下

img_afb85d56c1ab07004129a7b43e8e6d9c.gif

创建OccupantsTextUpdater脚本并添加到对应的Text上,他的作用是

  • 更新所在星球上含有的飞船数量并显示
img_154f7aaf06c92c86f7f6168b7b0a3cf7.png
using Data; using Unity.Entities; using UnityEngine; namespace Other { /// <summary> /// Just updates the text on the planets to represent the occupant count from the attached PlanetData /// 更新含有飞船的数量 /// </summary> public class OccupantsTextUpdater : MonoBehaviour { Entity _planetEntity; TextMesh _text; int LastOccupantCount = -1; [SerializeField] EntityManager _entityManager; void Start() { _entityManager = World.Active.GetOrCreateManager<EntityManager>(); _planetEntity = transform.parent.GetComponent<GameObjectEntity>().Entity; _text = GetComponent<TextMesh>(); } void Update() { if(!_entityManager.Exists(_planetEntity)) return; //获取所在星球上的PlanetData数据 var data = _entityManager.GetComponentData<PlanetData>(_planetEntity); if (data.Occupants == LastOccupantCount) return; LastOccupantCount = data.Occupants; _text.text = LastOccupantCount.ToString(); } } } 
img_1880df086105dc450e8a77a0cde58ed3.png

创建一个旋转系统,作用

  • 让每一个星球转动起来
using Data; using Unity.Collections; using Unity.Entities; using Unity.Jobs; using UnityEngine; using UnityEngine.Jobs; namespace Systems { /// <summary> /// 星球自转系统 /// </summary> public class RotationSystem : JobComponentSystem { //筛选实体(符合星球规则的) struct Planets { public readonly int Length; public ComponentDataArray<RotationData> Data; public TransformAccessArray Transforms; } //继承为 IJobParallelForTransform 而非 IJobParallelFor 或 IJob 这是一个专门为transform操作的接口 struct RotationJob : IJobParallelForTransform { public ComponentDataArray<RotationData> Rotations; public void Execute(int index, TransformAccess transform) { //设定旋转 transform.rotation = transform.rotation * Quaternion.Euler( Rotations[index].RotationSpeed); } } [Inject] Planets _planets; protected override JobHandle OnUpdate(JobHandle inputDeps) { var job = new RotationJob { Rotations = _planets.Data }; return job.Schedule(_planets.Transforms, inputDeps); } } } 

解析一

套路还是和以往IJobParallelFor类似,但是此次需要注意的是里面的继承是【IJobParallelForTransform】,一个专门实现transform并行执行的接口


创建一个增加飞船系统,作用

  • 接下来我们需要往红色和绿色的星球上添加飞船,每0.1s增加一次【可指定】
using Data; using Unity.Collections; using UnityEngine; using Unity.Entities; using Unity.Jobs; namespace Systems { /// <summary> /// 增加可以从行星上发送的船只的数量 /// </summary> // [UpdateAfter(typeof(ShipSpawnSystem))] public class OccupantIncreaseSystem : JobComponentSystem { float spawnCounter = 0.0f; float spawnInterval = 0.1f; //每次增加100个飞船 int occupantsToSpawn = 100; //筛选含有PlanetData数据的实体 struct Planets { public readonly int Length; public ComponentDataArray<PlanetData> Data; } struct PlanetsOccupantsJob : IJobParallelFor { public ComponentDataArray<PlanetData> Data; [ReadOnly] public int OccupantsToSpawn; public void Execute(int index) { //向除了中立星球意外的星球添加飞船 每次OccupantsToSpawn个 var data = Data[index]; if (data.TeamOwnership == 0) return; data.Occupants += OccupantsToSpawn; Data[index] = data; } } [Inject] Planets planets; protected override JobHandle OnUpdate(JobHandle inputDeps) { //指定时间运行一次 Execute var deltaTime = Time.deltaTime; spawnCounter += deltaTime; if (spawnCounter < spawnInterval) return inputDeps; spawnCounter = 0.0f; var job = new PlanetsOccupantsJob { Data = planets.Data, OccupantsToSpawn = occupantsToSpawn }; return job.Schedule(planets.Length, 32, inputDeps); } } } 

解析一

设置步接参数和筛选的实体

img_8588eaa8cbf739ddb54c088c089287ad.png

解析二

每次更改数据相关业务逻辑,根据获取的PlanetData每次增加OccupantsToSpawn数量的飞船

img_a8e38a04ae1e5d4da88924fba0b37394.png

解析三

赋值Data并按照规定时间执行Execute内先关的逻辑

img_6b306c2b95eb2462cf08e0c53d761f7c.png

效果如下

img_4fa6cc6b9ecdc47c3721faea827d8c57.gif

下面就开始写飞船有关的业务逻辑了。在写之前我先说下示例的设计思想,星球本地的飞船数量数据在不断增加,然后通过脚本,每一帧把红色或者绿色的飞船数量数据提取出来,然后放在等待实例化的数据中,再为每个飞船添加出生点、目标等信息。

创建脚本 AutoPlay添加到任意物体上 作用

  • 红色或者绿色星球寻找除自己以外的攻击目标
img_7dbf228666fdd4dba5f3a0030f74e5fa.png
using System.Collections.Generic; using Data; using Unity.Entities; using UnityEngine; namespace Other { public class AutoPlay : MonoBehaviour { /// <summary> /// 攻击间隔 /// </summary> [SerializeField] float attackInterval = 0.1f; /// <summary> /// 攻击计时器 /// </summary> [SerializeField] float attackCountdown = 0.1f; /// <summary> /// 所有星球物体 /// </summary> GameObject[] planets; EntityManager entityManager { get; set; } public void Start() { //获取场景中所有的Planet planets = GameObject.FindGameObjectsWithTag("Planet"); entityManager = World.Active.GetOrCreateManager<EntityManager>(); } public void Update() { //飞船攻击倒计时 attackCountdown -= Time.deltaTime; if (attackCountdown > 0.0f) return; attackCountdown = attackInterval; if(planets.Length <= 1) Debug.LogError("没有发现任何星球!!!"); //随机获取飞船索引 var sourcePlanetIndex = Random.Range(0, planets.Length); var sourcePlanetEntity = planets[sourcePlanetIndex].GetComponent<GameObjectEntity>().Entity; if(!entityManager.Exists(sourcePlanetEntity)) { //可以在场景卸载过程中发生 enabled = false; return; } //获取对应星球的 PlanetData 数据 var planetData = PlanetUtility.GetPlanetData(sourcePlanetEntity, entityManager); //防止找到的是【0队伍】的星球 while (planetData.TeamOwnership == 0) { //随机获取星球列表的索引 sourcePlanetIndex = Random.Range(0, planets.Length); sourcePlanetEntity = planets[sourcePlanetIndex].GetComponent<GameObjectEntity>().Entity; if(!entityManager.Exists(sourcePlanetEntity)) { // Can happen during scene unload enabled = false; return; } planetData = PlanetUtility.GetPlanetData(sourcePlanetEntity, entityManager); } var targetPlanetIndex = Random.Range(0, planets.Length); //防止找到的攻击目标星球是自己 while (targetPlanetIndex == sourcePlanetIndex) { targetPlanetIndex = Random.Range(0, planets.Length); } PlanetUtility.AttackPlanet(planets[sourcePlanetIndex], planets[targetPlanetIndex], entityManager); } } } 

解析一

这部分逻辑主要有两部分循环遍历,第一部分是随机找到一个红色或者绿色要攻击别的人的星球
然后找到需要攻击的星球, 第二部遍历的主要作用是防止攻击的目标是自己

img_118cf4bca89d63dbbeccbddbe01c63e1.png

然后创建PlanetUtility脚本 作用

  • 对准备发动攻击的飞船添加相关数据
  • ** 获取星球对应的PlanetData数据**
using System.Linq; using Data; using Unity.Entities; using UnityEngine; namespace Other { /// <summary> /// Some shared functionality between AutoPlay and UserInputSystem /// 自动布局和用户输入系统之间的一些共享功能 /// </summary> public static class PlanetUtility { /// <summary> /// 攻击设定 /// </summary> /// <param name="fromPlanet">发射飞船的星球</param> /// <param name="toPlanet">需要攻击的星球</param> /// <param name="entityManager"></param> public static void AttackPlanet(GameObject fromPlanet, GameObject toPlanet, EntityManager entityManager) { //获取发射星球的GameObjectEntity var entity = fromPlanet.GetComponent<GameObjectEntity>().Entity; //获取渲染星球对应的GameObjectEntity var meshComponent = fromPlanet.GetComponentsInChildren<GameObjectEntity>().First(c => c.gameObject != fromPlanet.gameObject); //获取 PlanetData var occupantData = entityManager.GetComponentData<PlanetData>(entity); //获取需要攻击星球的GameObjectEntity var targetEntity = toPlanet.GetComponent<GameObjectEntity>().Entity; //飞船发射数据 var launchData = new PlanetShipLaunchData { //目标星球 TargetEntity = targetEntity, //设定队伍 TeamOwnership = occupantData.TeamOwnership, //设定飞船数量 NumberToSpawn = occupantData.Occupants, //设定产卵的位置 SpawnLocation = fromPlanet.transform.position, //产卵半径(直径*0.5f) SpawnRadius = meshComponent.transform.lossyScale.x * 0.5f }; //发射完飞船数量剩余0 occupantData.Occupants = 0; //重新赋值 entityManager.SetComponentData(entity, occupantData); //向发射星球的entity上设定 飞船发射数据 有就更改 没有则添加 if (entityManager.HasComponent<PlanetShipLaunchData>(entity)) { entityManager.SetComponentData(entity, launchData); return; } entityManager.AddComponentData(entity, launchData); } /// <summary> /// 获取星球对应的PlanetData数据 /// </summary> /// <param name="planet">对应星球</param> /// <param name="entityManager"></param> /// <returns></returns> public static PlanetData GetPlanetData (GameObject planet, EntityManager entityManager) { var entity = planet.GetComponent<GameObjectEntity>().Entity; var data = GetPlanetData(entity, entityManager); return data; } /// <summary> /// 获取星球对应的PlanetData数据 /// </summary> /// <param name="entity">星球对应的 entity</param> /// <param name="entityManager"></param> /// <returns></returns> public static PlanetData GetPlanetData(Entity entity, EntityManager entityManager) { return entityManager.GetComponentData<PlanetData>(entity); } } } 

解析 一

根据fromPlanet与toPlanet两个GameObject获取对应的Entity和他们身上含有的纯数据

img_e9214f11f6b77fd8a67b57e833b776e3.png

解析二

根据提取出来的数据为新建纯数据 PlanetShipLaunchData 赋值,他里面含有飞船从创建到攻击用到的一切数据,然后对初始化完的PlanetShipLaunchData数据添加到对应的星球(entity)上

img_53ba582d04253bc9f1dc3e0785a0d567.png

效果如下

img_e1a02f2a89f434553b9f51cb50883c32.gif

在这里要特别说名一下,如果一个系统脚本中的Inject注入属性没有完成,是不会执行OnStartRunning函数的执行顺序如下

using System; using Data; using Other; using Unity.Collections; using Unity.Entities; using Unity.Transforms; using Unity.Mathematics; using Unity.Rendering; using UnityEngine; using Random = UnityEngine.Random; namespace Systems { public class OneSystem : ComponentSystem { public OneSystem() { Debug.Log("OneSystem"); } protected override void OnCreateManager() { Debug.Log("OnCreateManager"); base.OnCreateManager(); } protected override void OnStartRunning() { Debug.Log("OnStartRunning"); base.OnStartRunning(); } protected override void OnUpdate() { Debug.Log("OnUpdate"); } protected override void OnStopRunning() { Debug.Log("OnStopRunning"); base.OnStopRunning(); } protected override void OnDestroyManager() { Debug.Log("OnDestroyManager"); base.OnDestroyManager(); } } } 
img_53e822cbd857b65a6ea9c99b994258a2.png

现在我们已经明确知道了出发地点和攻击目标,接下来我们就开始实例化飞船,创建ShipSpawnSystem脚本

  • 作用实例化飞船
using System; using Data; using Other; using Unity.Collections; using Unity.Entities; using Unity.Transforms; using Unity.Mathematics; using Unity.Rendering; using UnityEngine; using Random = UnityEngine.Random; namespace Systems { // [UpdateAfter(typeof(UserInputSystem))] public class ShipSpawnSystem : ComponentSystem { public ShipSpawnSystem() { _entityManager = World.Active.GetOrCreateManager<EntityManager>(); } //产卵的星球 struct SpawningPlanets { public readonly int Length; public ComponentDataArray<PlanetShipLaunchData> Data; } /// <summary> /// 飞船产卵需要的数据 所在的星球 和这个星球上飞船的信息 /// </summary> struct ShipSpawnData { public PlanetShipLaunchData PlanetShipLaunchData; public PlanetData TargetPlanetData; public int ShipCount; } protected override void OnCreateManager() { _shipsToSpawn = new NativeList<ShipSpawnData>(Allocator.Persistent);//产生持续的数据 } protected override void OnDestroyManager() { _shipsToSpawn.Dispose(); } protected override void OnStartRunning() { _prefabManager = GameObject.FindObjectOfType<PrefabManager>(); if (_shipRenderer != null) return; //找到飞船的渲染 var prefabRenderer = _prefabManager.ShipPrefab.GetComponent<MeshInstanceRendererComponent>().Value; //找到飞船的产卵器 var planetSpawner = GameObject.FindObjectOfType<PlanetSpawner>(); //2种颜色的飞船 _shipRenderer = new MeshInstanceRenderer[planetSpawner._teamMaterials.Length]; //填充渲染具体数据 网格 自发光颜色 for (var i = 0; i < _shipRenderer.Length; ++i) { _shipRenderer[i] = prefabRenderer; _shipRenderer[i].material = new Material(prefabRenderer.material) { color = planetSpawner._teamMaterials[i].color }; _shipRenderer[i].material.SetColor("_EmissionColor", planetSpawner._teamMaterials[i].color); } base.OnStartRunning(); } /// <summary> /// 产卵星集合 注入没有完成对应的OnStartRunning不会触发 /// </summary> [Inject] SpawningPlanets _planets; /// <summary> /// 飞船预制体 /// </summary> PrefabManager _prefabManager; EntityManager _entityManager; //待生产飞船的队列 NativeList<ShipSpawnData> _shipsToSpawn; MeshInstanceRenderer[] _shipRenderer; protected override void OnUpdate() { //遍历所有产卵星球 for (var planetIndex = 0; planetIndex < _planets.Length; planetIndex++) { //获取需要产卵需要的数据 var planetLaunchData = _planets.Data[planetIndex]; if (planetLaunchData.NumberToSpawn == 0) { continue; } //获取飞船数量 var shipsToSpawn = planetLaunchData.NumberToSpawn; //为了实例化飞船时出现卡顿,设定一个阈值 var dt = Time.deltaTime; var deltaSpawn = Math.Max(1, Convert.ToInt32(1000.0f * dt)); //设定每次释放的飞船数量的最大限量 if (deltaSpawn < shipsToSpawn) shipsToSpawn = deltaSpawn; //获取需要攻击的目标星球信息 var targetPlanet = _entityManager.GetComponentData<PlanetData>(planetLaunchData.TargetEntity); //添加到生产的列表 _shipsToSpawn.Add(new ShipSpawnData { ShipCount = shipsToSpawn, PlanetShipLaunchData = planetLaunchData, TargetPlanetData = targetPlanet }); //更新使用过的PlanetShipLaunchData数据 var launchData = new PlanetShipLaunchData { TargetEntity = planetLaunchData.TargetEntity, //剩余的飞船数量 NumberToSpawn = planetLaunchData.NumberToSpawn - shipsToSpawn, TeamOwnership = planetLaunchData.TeamOwnership, SpawnLocation = planetLaunchData.SpawnLocation, SpawnRadius = planetLaunchData.SpawnRadius }; _planets.Data[planetIndex] = launchData; } //遍历需要生产的飞船的列表 for (int spawnIndex = 0; spawnIndex < _shipsToSpawn.Length; ++spawnIndex) { //生产飞船的数量 var spawnCount = _shipsToSpawn[spawnIndex].ShipCount; //生成飞船位置等信息 var planet = _shipsToSpawn[spawnIndex].PlanetShipLaunchData; //这批飞船的目标 var targetPlanet = _shipsToSpawn[spawnIndex].TargetPlanetData; //生产飞船所在星球的位置 var planetPos = planet.SpawnLocation; //与目标星球的距离 var planetDistance = Vector3.Distance(planetPos, targetPlanet.Position); //生产的半径 var planetRadius = planet.SpawnRadius; //实例化飞船 var prefabShipEntity = _entityManager.Instantiate(_prefabManager.ShipPrefab); //添加渲染数据(哪个队伍,颜色不同) _entityManager.SetSharedComponentData(prefabShipEntity, _shipRenderer[planet.TeamOwnership]); var entities = new NativeArray<Entity>(spawnCount, Allocator.Temp); //实例化这批需要生产的飞船(一次性生产指定个数) _entityManager.Instantiate(prefabShipEntity, entities); //删掉原型 _entityManager.DestroyEntity(prefabShipEntity); //对这个批次所有产生的飞船出生位置操作 for (int i = 0; i < spawnCount; i++) { //飞船出生点 float3 shipPos; do { var insideCircle = Random.insideUnitCircle.normalized; //转换float3 因为数据更小 var onSphere = new float3(insideCircle.x, 0, insideCircle.y); shipPos = planetPos + (onSphere * (planetRadius + _prefabManager.ShipPrefab.transform.localScale.x)); } while (math.lengthsq(shipPos - planetPos) > planetDistance * planetDistance); var data = new ShipData { TargetEntity = planet.TargetEntity, TeamOwnership = planet.TeamOwnership }; _entityManager.AddComponentData(entities[i], data); var spawnPosition = new Position { Value = shipPos }; var spawnScale = new Scale { Value = new float3(1.0f, 1.0f, 1.0f) // Value = new float3(0.02f, 0.02f, 0.02f) }; _entityManager.SetComponentData(entities[i], spawnScale); _entityManager.SetComponentData(entities[i], spawnPosition); } entities.Dispose(); } _shipsToSpawn.Clear(); } } } 

解析一

准备相应的数据模板并初始化,其中NativeList是unity自定义的泛型List,且显示泛型为Struct

img_f9864f9f309fa2635bd0ae37d6637d59.png

解析二

初始化相关属性参数,为后面的实例化做准别

img_5d22b284f0700cd94f801aff8f26c387.png

解析三

限制每次实例化数量并把需要实例化的数据添加到生产列表

img_c8c1da51f64c10be1f4e22e6bb41f183.png

解析四

更新使用过的PlanetShipLaunchData数据

img_cc393fcaff80dc03ffdb4cb5c7697910.png

解析五

遍历生产清单开始实例化

img_33f9c64b929bc0a3cd84a15deffcea32.png

解析六

设定每个飞船的位置

img_1b84f87c3bf19d86c53d5bbe21e1a781.png

效果如下

img_5b2ff598be072a590d6f96aa443bec20.gif

Unity 之 Pure版Entity Component System 案例【GalacticConquest】解析【下】

原文链接:https://yq.aliyun.com/articles/666164
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章