Unity DOTS自定義Authoring類:連接GameObject與Entity的設計橋梁
1. 項目概述為什么我們需要自定義Authoring類如果你已經(jīng)開始接觸Unity的DOTSData-Oriented Technology Stack并且嘗試過將一些簡單的GameObject轉(zhuǎn)換為Entity那么你一定用過ConvertToEntity組件。這個組件很方便但它更像是一個“黑盒”把整個GameObject及其子物體一股腦地轉(zhuǎn)換成一個或多個Entity。對于快速原型或者簡單的對象這沒問題。但當我們開始構(gòu)建一個稍微復雜點的系統(tǒng)比如一個RTS游戲里的士兵單位或者一個模擬城市里的建筑時問題就來了。這個士兵單位可能包含一個用于渲染的Mesh、一個用于碰撞的物理體、一個代表生命值的組件、一個代表所屬陣營的標簽、一個存儲當前攻擊目標的引用以及一堆用于行為樹或狀態(tài)機的數(shù)據(jù)。如果只用ConvertToEntity這些數(shù)據(jù)要么散落在不同的MonoBehaviour里轉(zhuǎn)換邏輯混雜不清要么你不得不寫一個龐大而復雜的IConvertGameObjectToEntity實現(xiàn)里面充滿了if-else和GetComponent調(diào)用難以維護更違背了DOTS數(shù)據(jù)驅(qū)動、清晰分離的初衷。這就是自定義Authoring類登場的時刻。它不是一個高深莫測的黑魔法而是DOTS工作流中連接我們熟悉的面向?qū)ο缶庉嬈魇澜鏕ameObject, MonoBehaviour與高效的面向數(shù)據(jù)運行時世界Entity, ComponentData的設計橋梁。簡單說它允許你為特定的、復雜的游戲?qū)ο箢愋投x一個專屬的、可視化的、在Inspector里友好配置的“轉(zhuǎn)換藍圖”。想象一下你有一個“炮塔”預制體。通過自定義Authoring類TurretAuthoring你可以在Inspector里直接設置炮塔的傷害值、攻擊范圍、炮彈預制體引用和開火速率。點擊播放后TurretAuthoring會負責將這些配置值高效、準確地填充到一個包含TurretComponentData存儲數(shù)值和TurretPrefabReference存儲Entity引用的純凈Entity中。這比在運行時用GetComponent去一個個查找要高效和清晰得多。所以掌握自定義Authoring類核心是掌握一種設計模式。它讓你能以一種可控、可預測的方式將編輯器中的設計意圖翻譯成DOTS運行時能高效處理的數(shù)據(jù)結(jié)構(gòu)。這是從“能用DOTS”到“善用DOTS”的關(guān)鍵一步。2. 核心概念與設計思路拆解在動手寫代碼之前我們必須把幾個關(guān)鍵概念和它們之間的關(guān)系理清楚。DOTS的轉(zhuǎn)換流程看似復雜但一旦理解了其設計哲學就會變得非常直觀。2.1 Authoring、Runtime Data與Conversion的世界DOTS架構(gòu)明確區(qū)分了“編輯時”和“運行時”。Authoring創(chuàng)作期這就是我們在Unity編輯器中操作的階段。我們使用GameObject、MonoBehaviour和預制體來搭建場景、配置參數(shù)。自定義的Authoring類本質(zhì)上就是一個特殊的MonoBehaviour它掛載在GameObject上唯一目的就是持有數(shù)據(jù)并告訴Unity“嘿我這個對象需要被轉(zhuǎn)換成Entity轉(zhuǎn)換規(guī)則在我這里定義”。Runtime Data運行時數(shù)據(jù)這是SubsystemECS系統(tǒng)直接操作的對象。它們必須是IComponentData接口的結(jié)構(gòu)體或Blob Asset、Dynamic Buffer等特點是純數(shù)據(jù)、沒有方法、盡可能簡單以利于CPU緩存和并行處理。例如struct HealthComponent : IComponentData { public float Value; }。Conversion轉(zhuǎn)換期這是連接上述兩個世界的橋梁發(fā)生在進入Play模式或烘焙Baking時。轉(zhuǎn)換系統(tǒng)會掃描場景中所有掛載了Authoring組件的GameObject并執(zhí)行其定義的轉(zhuǎn)換邏輯生成對應的Entity和ComponentData。自定義Authoring類的核心工作就是在Conversion階段讀取自身及關(guān)聯(lián)GameObject上的配置數(shù)據(jù)然后創(chuàng)建或配置目標Entity為其添加或設置相應的Runtime Data。2.2 轉(zhuǎn)換工作流的三種模式Unity提供了不同粒度和控制級別的轉(zhuǎn)換方式理解它們的區(qū)別至關(guān)重要自動轉(zhuǎn)換ConvertToEntity最簡單。掛上這個組件Unity會嘗試自動將GameObject及其子物體轉(zhuǎn)換為Entity。它會為每個GameObject生成一個Entity并自動轉(zhuǎn)換Transform、MeshRenderer等常見組件??刂屏ψ钊踹m合簡單對象或快速起步。基于接口的轉(zhuǎn)換IConvertGameObjectToEntity在Authoring類中實現(xiàn)這個接口。它提供了一個Convert方法讓你可以編寫自定義邏輯來操作轉(zhuǎn)換生成的Entity。這是自定義Authoring類最常用的方式。它給予你對單個GameObject如何轉(zhuǎn)換為其對應Entity的完全控制權(quán)?;谙到y(tǒng)的轉(zhuǎn)換GameObjectConversionSystem最強大、最靈活。你可以創(chuàng)建自己的ConversionSystem在整個轉(zhuǎn)換過程中對所有GameObject進行掃描和處理。它可以實現(xiàn)跨GameObject的依賴處理、批量操作、生成額外的管理Entity等復雜邏輯??刂屏ψ顝姷珡碗s度也最高通常用于處理整個子系統(tǒng)或機制的轉(zhuǎn)換如導航網(wǎng)格、場景光照數(shù)據(jù)的轉(zhuǎn)換。對于大多數(shù)自定義游戲?qū)ο箢愋湍J?IConvertGameObjectToEntity是我們的最佳選擇。它平衡了控制力與復雜度讓我們能專注于定義“我這個類型的對象應該包含什么數(shù)據(jù)”。2.3 設計一個自定義Authoring類的思考過程假設我們要為一個“收集品”比如金幣創(chuàng)建Authoring類。設計思路應該是自頂向下的定義運行時需要什么數(shù)據(jù)Runtime Data一個收集品Entity需要什么一個CollectibleTag用于系統(tǒng)篩選一個RotationSpeed旋轉(zhuǎn)速度一個PickupEffectPrefab拾取特效的Entity預制體引用先定義好這些IComponentData結(jié)構(gòu)體。設計編輯時的配置界面Authoring創(chuàng)建一個CollectibleAuthoring : MonoBehaviour, IConvertGameObjectToEntity。它應該有哪些可配置的公共字段float RotationSpeed在Inspector里用滑塊控制、GameObject PickupEffectPrefab拖拽一個預制體進來。這些字段就是為了方便設計師配置。實現(xiàn)轉(zhuǎn)換邏輯Conversion在Convert方法里將RotationSpeed賦值給Entity的RotationSpeedComponent將PickupEffectPrefab通過GetEntity轉(zhuǎn)換成一個EntityPrefabReference并附加給Entity。這個思考過程確保了我們的設計是數(shù)據(jù)驅(qū)動的Authoring類僅僅是一個友好的數(shù)據(jù)輸入界面。3. 手把手創(chuàng)建第一個自定義Authoring類理論說再多不如動手做一遍。我們來創(chuàng)建一個經(jīng)典的例子一個會上下漂浮、自身旋轉(zhuǎn)的“能量晶體”。3.1 步驟一定義運行時組件首先我們定義這個晶體在運行時所需要的數(shù)據(jù)。在Runtime文件夾建議的代碼組織方式下創(chuàng)建兩個組件。// FloatComponent.cs - 用于上下漂浮 using Unity.Entities; public struct FloatComponent : IComponentData { public float Amplitude; // 浮動幅度 public float Frequency; // 浮動頻率 public float InitialY; // 初始Y軸位置用于計算偏移 } // RotateComponent.cs - 用于自身旋轉(zhuǎn) using Unity.Entities; public struct RotateComponent : IComponentData { public float Speed; // 旋轉(zhuǎn)速度度/秒 }這兩個是純粹的標簽數(shù)據(jù)不包含任何邏輯。3.2 步驟二創(chuàng)建Authoring類并實現(xiàn)轉(zhuǎn)換接下來創(chuàng)建Authoring類。我習慣把它放在Authoring文件夾下以明確其職責。// CrystalAuthoring.cs using UnityEngine; using Unity.Entities; public class CrystalAuthoring : MonoBehaviour, IConvertGameObjectToEntity { // 在Inspector中可配置的參數(shù) public float floatAmplitude 0.5f; public float floatFrequency 1.0f; public float rotateSpeed 90.0f; public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { // 1. 添加浮動組件并設置數(shù)據(jù) dstManager.AddComponentData(entity, new FloatComponent { Amplitude floatAmplitude, Frequency floatFrequency, InitialY transform.position.y // 記錄轉(zhuǎn)換時的初始高度 }); // 2. 添加旋轉(zhuǎn)組件并設置數(shù)據(jù) dstManager.AddComponentData(entity, new RotateComponent { Speed rotateSpeed }); // 注意這里我們沒有處理Transform。因為ConvertToEntity或默認轉(zhuǎn)換系統(tǒng) // 會自動為帶有Transform的GameObject生成LocalToWorld等Transform組件。 // 我們的FloatSystem和RotateSystem會去讀寫這些組件。 } }關(guān)鍵點解析IConvertGameObjectToEntity接口強制實現(xiàn)Convert方法。這是轉(zhuǎn)換的核心。Convert方法參數(shù)entity: 這個GameObject將要轉(zhuǎn)換成的目標Entity。系統(tǒng)已經(jīng)為我們創(chuàng)建好了。dstManager:目標世界的EntityManager用于對目標Entity進行添加/移除組件、設置數(shù)據(jù)等操作。conversionSystem: 轉(zhuǎn)換系統(tǒng)本身可以用來查詢其他對象的轉(zhuǎn)換結(jié)果例如通過GetPrimaryEntity獲取另一個GameObject對應的Entity。AddComponentData: 這是為Entity添加組件并同時設置其初始值的標準方法。transform.position.y: 我們在轉(zhuǎn)換時捕獲了GameObject的初始世界Y坐標。這是一個非常重要的技巧。因為轉(zhuǎn)換后GameObject的Transform數(shù)據(jù)會被處理成ECS的LocalToWorld等組件其值可能會被重置或基于父Entity重新計算。如果我們的浮動邏輯需要基于原始位置就必須在轉(zhuǎn)換時把這個“初始狀態(tài)”保存到自定義組件里。3.3 步驟三創(chuàng)建對應的ECS系統(tǒng)數(shù)據(jù)有了需要系統(tǒng)來驅(qū)動它們。創(chuàng)建兩個簡單的Job System。// FloatSystem.cs using Unity.Entities; using Unity.Jobs; using Unity.Mathematics; using Unity.Transforms; public partial class FloatSystem : SystemBase { protected override void OnUpdate() { float elapsedTime (float)Time.ElapsedTime; Entities .ForEach((ref LocalToWorld transform, in FloatComponent floatData) { // 計算新的Y軸位置初始位置 正弦波偏移 float newY floatData.InitialY math.sin(elapsedTime * floatData.Frequency) * floatData.Amplitude; // 獲取當前的位置和旋轉(zhuǎn) float3 position transform.Position; quaternion rotation transform.Rotation; // 構(gòu)造新的LocalToWorld矩陣 transform.Value float4x4.TRS( new float3(position.x, newY, position.z), rotation, new float3(1.0f, 1.0f, 1.0f) ); }).ScheduleParallel(); } } // RotateSystem.cs using Unity.Entities; using Unity.Jobs; using Unity.Mathematics; using Unity.Transforms; public partial class RotateSystem : SystemBase { protected override void OnUpdate() { float deltaTime Time.DeltaTime; Entities .ForEach((ref Rotation rotation, in RotateComponent rotateData) { // 繞Y軸旋轉(zhuǎn) quaternion deltaRotation quaternion.EulerXYZ(new float3(0, rotateData.Speed * deltaTime * math.PI / 180f, 0)); rotation.Value math.mul(rotation.Value, deltaRotation); }).ScheduleParallel(); } }3.4 步驟四在場景中測試在場景中創(chuàng)建一個Cube或Sphere命名為“EnergyCrystal”。移除默認的MeshRenderer和MeshFilter如果你用了其他渲染方案如Hybrid Renderer則按需處理。掛載ConvertToEntity組件這是觸發(fā)轉(zhuǎn)換的開關(guān)。掛載我們剛寫好的CrystalAuthoring組件。在Inspector中調(diào)整floatAmplitude、floatFrequency和rotateSpeed參數(shù)。點擊播放你應該能看到這個晶體一邊旋轉(zhuǎn)一邊上下漂浮。至此你已經(jīng)完成了第一個功能完整的自定義Authoring類。它成功地將編輯器中的配置參數(shù)轉(zhuǎn)換成了ECS世界里驅(qū)動行為的純凈數(shù)據(jù)。4. 處理復雜依賴與引用現(xiàn)實項目中的對象很少是孤立的。一個炮塔需要引用炮彈預制體一個技能需要引用特效一個生產(chǎn)建筑需要引用它產(chǎn)出的單位。在DOTS中這些“引用”不再是GameObject或MonoBehaviour而是Entity或BlobAssetReference。處理這些依賴是自定義Authoring類的進階課題。4.1 引用其他預制體Prefab Reference這是最常見的需求。我們希望Authoring類里能拖拽一個Prefab然后在運行時實例化它。錯誤做法在Convert方法里直接保存GameObject或Prefab的引用。這行不通因為轉(zhuǎn)換后GameObject世界不存在了。正確做法使用轉(zhuǎn)換系統(tǒng)GameObjectConversionSystem提供的GetPrimaryEntity方法將Prefab也轉(zhuǎn)換成一個Entity并獲取其引用。這個引用是一個Entity但它代表的是預制體模板。// TurretAuthoring.cs using UnityEngine; using Unity.Entities; public class TurretAuthoring : MonoBehaviour, IConvertGameObjectToEntity { public GameObject projectilePrefab; // 在Inspector中拖拽炮彈預制體 public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { // 關(guān)鍵步驟將GameObject預制體轉(zhuǎn)換為Entity預制體引用 Entity projectileEntityPrefab conversionSystem.GetPrimaryEntity(projectilePrefab); // 將Entity引用存儲到一個組件中 dstManager.AddComponentData(entity, new TurretShootingComponent { ProjectilePrefab projectileEntityPrefab, Cooldown 1.0f, Damage 10.0f }); } } // TurretShootingComponent.cs using Unity.Entities; public struct TurretShootingComponent : IComponentData { public Entity ProjectilePrefab; // 這里存儲的是Entity引用 public float Cooldown; public float Damage; public float CurrentCooldown; }在炮塔的射擊系統(tǒng)里你可以使用EntityManager.Instantiate(ProjectilePrefab)來創(chuàng)建炮彈Entity。重要提示GetPrimaryEntity方法要求被引用的Prefab也參與到轉(zhuǎn)換流程中。通常這意味著它也需要掛載ConvertToEntity組件或者通過其他方式如放在Subscene中確保它被轉(zhuǎn)換。如果引用的Prefab沒有被轉(zhuǎn)換GetPrimaryEntity可能會返回Entity.Null導致運行時錯誤。4.2 引用場景中的其他對象非預制體有時你需要引用場景中另一個特定的GameObject比如一個目標點、一個出生點。處理方式類似但需要確保被引用的對象先于引用它的對象完成轉(zhuǎn)換。這通常不是問題因為轉(zhuǎn)換順序默認是基于場景層級或依賴關(guān)系的但為了安全你可以使用DeclareReferencedPrefab或依賴GameObjectConversionSystem的Dependency屬性來處理。更簡單的做法是如果這個引用是靜態(tài)的比如場景裝飾物之間的關(guān)聯(lián)可以在Authoring中記錄對方的GameObject然后在Convert中通過conversionSystem.GetPrimaryEntity(targetGameObject)來獲取其Entity。同樣要確保targetGameObject也被轉(zhuǎn)換。4.3 處理Blob Asset引用復雜共享數(shù)據(jù)對于更復雜的、只讀的共享數(shù)據(jù)如武器屬性表、技能配置、AI行為樹BlobAsset是比IComponentData更高效的選擇。Authoring類也可以參與BlobAsset的創(chuàng)建。流程通常是在Authoring類中定義配置數(shù)據(jù)結(jié)構(gòu)使用GameObject和普通字段。在Convert方法中使用BlobAssetBuilder來構(gòu)造BlobAsset數(shù)據(jù)。通過conversionSystem.BlobAssetStore.AddUniqueBlobAsset來注冊這個BlobAsset并獲取一個BlobAssetReferenceT。將這個引用存儲到目標Entity的某個組件中。由于BlobAsset的創(chuàng)建涉及非托管內(nèi)存和序列化代碼稍復雜但它對于共享只讀數(shù)據(jù)的性能提升是巨大的。一個典型的例子是為所有同類型敵人共享一套屬性// 1. 定義Blob數(shù)據(jù)結(jié)構(gòu) public struct EnemyStatsBlob { public float MaxHealth; public float MoveSpeed; public float AttackDamage; public BlobString Name; } // 2. 在Authoring的Convert中創(chuàng)建 public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { var builder new BlobBuilder(Allocator.Temp); ref EnemyStatsBlob stats ref builder.ConstructRootEnemyStatsBlob(); stats.MaxHealth maxHealth; stats.MoveSpeed moveSpeed; stats.AttackDamage attackDamage; builder.AllocateString(ref stats.Name, enemyName); var blobReference builder.CreateBlobAssetReferenceEnemyStatsBlob(Allocator.Persistent); builder.Dispose(); // 存儲引用 dstManager.AddComponentData(entity, new EnemyStatsReference { Value blobReference }); }5. 高級技巧與最佳實踐當你熟悉了基礎操作后下面這些技巧能讓你的Authoring類更健壯、更高效。5.1 利用[GenerateAuthoringComponent]屬性簡化流程對于非常簡單的組件其Authoring類可能只是幾個公共字段加上一個Convert方法。Unity提供了一個便捷屬性[GenerateAuthoringComponent]可以自動為你生成這個Authoring類。// 在你的IComponentData結(jié)構(gòu)體上添加這個屬性 [GenerateAuthoringComponent] public struct HealthComponent : IComponentData { public float Value; }編譯后Unity會自動生成一個名為HealthComponentAuthoring的MonoBehaviour它包含一個float Value字段并自動實現(xiàn)轉(zhuǎn)換邏輯。你只需要把這個生成的組件掛到GameObject上即可。這極大地簡化了簡單組件的創(chuàng)建工作流。注意事項自動生成的Authoring類功能有限。它只能處理簡單的字段類型float, int, Entity等無法處理復雜邏輯如引用其他Prefab、創(chuàng)建BlobAsset。對于復雜組件仍需手動編寫Authoring類。5.2 轉(zhuǎn)換中的依賴管理與執(zhí)行順序當你的轉(zhuǎn)換邏輯依賴于其他GameObject的轉(zhuǎn)換結(jié)果時比如上面獲取其他Entity的引用執(zhí)行順序就變得重要。IConvertGameObjectToEntity的Convert方法調(diào)用順序是不確定的。解決方案使用GameObjectConversionSystem。你可以創(chuàng)建自己的ConversionSystem并覆寫OnUpdate方法。在這個系統(tǒng)里你可以通過Dependency屬性確保所有IConvertGameObjectToEntity都執(zhí)行完畢后再執(zhí)行你的邏輯或者通過Entities.ForEach遍歷所有具有特定Authoring組件的Entity來進行后處理。例如你需要在所有“隊伍”對象轉(zhuǎn)換完成后為每個“士兵”對象分配其所屬隊伍的Entity引用。這需要在ConversionSystem中分兩步完成。5.3 調(diào)試轉(zhuǎn)換過程Debug.Log與Entity Debugger轉(zhuǎn)換過程在編輯模式下進行Debug.Log是可以正常工作的。在Convert方法中添加日志可以幫助你理解轉(zhuǎn)換的觸發(fā)時機和順序。更強大的工具是Unity編輯器的Entity Debugger(Window Analysis Entity Debugger)。在播放模式下你可以查看所有存在的Entity、它們的組件數(shù)據(jù)。通過對比轉(zhuǎn)換前后你可以清晰地看到你的Authoring類是否正確地為Entity添加了預期的組件和數(shù)據(jù)。這是調(diào)試DOTS轉(zhuǎn)換問題的首選工具。5.4 性能考量與常見陷阱避免在Convert中進行昂貴操作Convert方法在進入播放模式時運行雖然不直接影響運行時幀率但過長的轉(zhuǎn)換時間會影響測試迭代速度。避免在這里進行復雜的計算、同步加載資源等。謹慎使用GetComponent在Convert方法中如果需要獲取同一GameObject上其他MonoBehaviour的數(shù)據(jù)使用GetComponent是可行的。但頻繁調(diào)用或在不必要的對象上調(diào)用會影響轉(zhuǎn)換性能。盡量將配置數(shù)據(jù)集中在一個Authoring類中。處理好Null引用Inspector中配置的字段可能為空。在Convert方法中務必對可能的null引用進行檢查提供默認值或跳過某些邏輯避免運行時崩潰。Subscene與Live Conversion對于大型場景使用Subscene進行分批轉(zhuǎn)換和流式加載是推薦做法。自定義Authoring類在Subscene中工作方式相同。注意開啟“Live Conversion”時對Authoring組件屬性的修改會實時觸發(fā)部分轉(zhuǎn)換這可能帶來一些復雜性但也是強大的迭代工具。6. 實戰(zhàn)案例構(gòu)建一個簡單的RTS單位讓我們綜合運用所學為一個極簡的RTS游戲創(chuàng)建一個“士兵”單位的完整Authoring方案。這個士兵需要視覺表現(xiàn)、生命值、移動速度、攻擊力、以及所屬隊伍的標識。6.1 定義運行時組件// Runtime/Components/UnitComponents.cs using Unity.Entities; // 生命值 public struct Health : IComponentData { public float Value; public float MaxValue; } // 移動屬性 public struct MoveSpeed : IComponentData { public float Value; } // 攻擊屬性 public struct AttackDamage : IComponentData { public float Value; } // 隊伍歸屬使用SharedComponent便于按隊伍篩選 public struct Team : ISharedComponentData { public int Id; }6.2 創(chuàng)建士兵的Authoring類// Authoring/UnitAuthoring.cs using UnityEngine; using Unity.Entities; public class UnitAuthoring : MonoBehaviour, IConvertGameObjectToEntity { [Header(Basic Stats)] public float maxHealth 100f; public float moveSpeed 5f; public float attackDamage 10f; [Header(Team)] public int teamId 0; [Header(Visuals (Optional))] public GameObject selectionRingPrefab; // 選中時的光環(huán)效果預制體 public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) { // 添加基礎屬性組件 dstManager.AddComponentData(entity, new Health { Value maxHealth, MaxValue maxHealth }); dstManager.AddComponentData(entity, new MoveSpeed { Value moveSpeed }); dstManager.AddComponentData(entity, new AttackDamage { Value attackDamage }); // 添加共享組件注意頻繁改變SharedComponent會影響性能這里假設隊伍不變 dstManager.AddSharedComponentData(entity, new Team { Id teamId }); // 處理可選視覺效果引用 if (selectionRingPrefab ! null) { Entity ringPrefabEntity conversionSystem.GetPrimaryEntity(selectionRingPrefab); if (ringPrefabEntity ! Entity.Null) { dstManager.AddComponentData(entity, new SelectionRingPrefab { Value ringPrefabEntity }); } else { Debug.LogWarning($Selection ring prefab for {gameObject.name} is not a convertible GameObject. Reference ignored., this); } } // 可以添加一個標簽組件方便系統(tǒng)查詢“所有士兵單位” dstManager.AddComponentUnitTag(entity); } } // 標簽組件和預制體引用組件 public struct UnitTag : IComponentData {} public struct SelectionRingPrefab : IComponentData { public Entity Value; }6.3 創(chuàng)建對應的系統(tǒng)示例移動系統(tǒng)// Systems/UnitMoveSystem.cs using Unity.Entities; using Unity.Jobs; using Unity.Mathematics; using Unity.Transforms; public partial class UnitMoveSystem : SystemBase { protected override void OnUpdate() { float deltaTime Time.DeltaTime; Entities .WithName(MoveUnits) .ForEach((ref Translation translation, in MoveSpeed speed, in UnitMoveTarget target) { // 簡單的向目標點移動邏輯 float3 direction target.Value - translation.Value; float distance math.length(direction); if (distance 0.1f) { direction math.normalize(direction); translation.Value direction * speed.Value * deltaTime; } }).ScheduleParallel(); } } // 移動目標組件可由其他系統(tǒng)如玩家輸入系統(tǒng)設置 public struct UnitMoveTarget : IComponentData { public float3 Value; }6.4 在編輯器中配置與測試創(chuàng)建一個士兵的預制體例如一個膠囊體加上顏色材質(zhì)。為預制體根節(jié)點添加ConvertToEntity組件。添加UnitAuthoring組件。在Inspector中配置maxHealth、moveSpeed、attackDamage和teamId。可選創(chuàng)建一個表示選中光環(huán)的簡單圓形預制體拖拽到selectionRingPrefab字段。將預制體拖入場景點擊播放。通過Entity Debugger你可以看到這個士兵Entity已經(jīng)擁有了Health、MoveSpeed、AttackDamage、Team、UnitTag等所有組件。這個案例展示了一個中等復雜度的自定義Authoring類如何將設計師友好的編輯器配置系統(tǒng)地、結(jié)構(gòu)化地轉(zhuǎn)換為一套高效的ECS運行時數(shù)據(jù)為后續(xù)實現(xiàn)移動、攻擊、選擇等游戲邏輯打下了堅實的基礎。通過這種方式即使是非常復雜的游戲?qū)ο笃滢D(zhuǎn)換邏輯也能保持清晰和可維護。

相關(guān)新聞

【單片機課程設計/畢業(yè)設計】基于 51/STM32 單片機的醫(yī)護端優(yōu)先級呼叫處理設備設計 無線傳輸型 8 床位智能呼叫與定時播報系統(tǒng)開發(fā)(020301)

【單片機課程設計/畢業(yè)設計】基于 51/STM32 單片機的醫(yī)護端優(yōu)先級呼叫處理設備設計 無線傳輸型 8 床位智能呼叫與定時播報系統(tǒng)開發(fā)(020301)

博主介紹:??碼農(nóng)一枚 ,專注于大學生項目實戰(zhàn)開發(fā)、講解和畢業(yè)🚢文撰寫修改等。全棧領域優(yōu)質(zhì)創(chuàng)作者,博客之星、掘金/華為云/阿里云/InfoQ等平臺優(yōu)質(zhì)作者、專注于嵌入式單片機,Java、小程序技術(shù)領域和畢業(yè)項目實戰(zhàn) ??…

2026/8/2 20:07:03 閱讀更多
XMBOX:開源Android視頻播放器盒子完全指南

XMBOX:開源Android視頻播放器盒子完全指南

XMBOX:開源Android視頻播放器盒子完全指南 【免費下載鏈接】XMBOX 空盒子應用 項目地址: https://gitcode.com/gh_mirrors/xm/XMBOX XMBOX是一款功能強大的開源Android視頻播放器盒子,支持TV、手機和平板三端適配,采用模塊化架構(gòu)設計&…

2026/8/2 21:17:10 閱讀更多
Excel COUNTIF函數(shù)精確統(tǒng)計全解析:從通配符陷阱到高級組合應用

Excel COUNTIF函數(shù)精確統(tǒng)計全解析:從通配符陷阱到高級組合應用

1. 項目概述:為什么COUNTIF的“精確統(tǒng)計”是個技術(shù)活?干了這么多年數(shù)據(jù)分析,處理過的表格少說也有幾千張,我發(fā)現(xiàn)一個挺有意思的現(xiàn)象:很多人覺得Excel里的COUNTIF函數(shù)簡單得不能再簡單了,不就是數(shù)個數(shù)嘛。但…

2026/8/2 21:07:10 閱讀更多
3分鐘搞定!QQ空間歷史說說完整備份終極指南

3分鐘搞定!QQ空間歷史說說完整備份終極指南

3分鐘搞定!QQ空間歷史說說完整備份終極指南 【免費下載鏈接】GetQzonehistory 獲取QQ空間發(fā)布的歷史說說 項目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾想過,那些年發(fā)過的QQ空間說說,那些記錄青春的文字…

2026/8/2 0:04:01 閱讀更多
3分鐘搞定!QQ空間歷史說說完整備份終極指南

3分鐘搞定!QQ空間歷史說說完整備份終極指南

3分鐘搞定!QQ空間歷史說說完整備份終極指南 【免費下載鏈接】GetQzonehistory 獲取QQ空間發(fā)布的歷史說說 項目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾想過,那些年發(fā)過的QQ空間說說,那些記錄青春的文字…

2026/8/2 0:04:01 閱讀更多
AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O分配PCB板是應用材料(Applied Materials)公司生產(chǎn)的一款用于半導體設備的I/O信號分配電路板。該型號(0100-02186)的核心特點如下:專用于Endura等半導體工藝腔室。集成信號路由與分配功能。連接控制…

2026/8/2 2:51:21 閱讀更多
Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機是日本日清(Nissei)品牌的一款工業(yè)用三相異步電機,適用于自動化設備及通用機械驅(qū)動。該型號(FFMN-32L-10-T0 40AX)的核心特點如下:三相交流異步電動機。額定…

2026/8/2 2:52:49 閱讀更多