赞
踩
英文原文:
https://coffeebraingames.wordpress.com/2020/11/29/getting-started-with-blob-asset/
Blob资产是Unity的DOTS中使用的一个概念。基本上,它是一种存储在非托管内存中的资产,可以在组件中访问。这里的 "资产 "并不是指传统意义上的资产,如预制件、纹理、3D模型、音频剪辑等。这个术语是指数据。它可以是简单的整数,也可以是复杂的结构数组,或者NativeHashMap形式的查找表。只允许非空类型,所以没有类和委托等引用类型。我们建议使用blob资产来保存不可变的数据…目前是这样。
一个明显的用法是存储一个静态值的数据库,它的引用可以被添加到一个组件中。(你不能在组件中存储普通的集合。你可以有集合,但它们与本地集合不同,而且有限制。)
让我们来做一个blob Asset的基本用法。让我们制作一个存储单一整数值的blob资产,通过Entites.ForEach()在组件中访问它。下面是拥有引用的组件和准备blob Asset和实体的系统的代码。
private struct TestComponent : IComponentData { public BlobAssetReference<int> reference; } private class BlobAssetSystem : SystemBase { private const int DATA_VALUE = 111; private BlobAssetReference<int> reference; protected override void OnCreate() { base.OnCreate(); // Prepare the blob asset BlobBuilder builder = new BlobBuilder(Allocator.TempJob); ref int data = ref builder.ConstructRoot<int>(); data = DATA_VALUE; this.reference = builder.CreateBlobAssetReference<int>(Allocator.Persistent); builder.Dispose(); Entity entity = this.EntityManager.CreateEntity(typeof(TestComponent)); this.EntityManager.SetComponentData(entity, new TestComponent() { reference = this.reference }); } protected override void OnDestroy() { this.reference.Dispose(); } protected override void OnUpdate() { // Used non burst here so that Assert will work // Rest assured that ScheduleParallel() works here when Assert.IsTrue() // is removed this.Entities.ForEach(delegate(in TestComponent component) { Debug.Log($"value: {component.reference.Value}"); Assert.IsTrue(component.reference.Value == DATA_VALUE); }).WithoutBurst().Run(); } public void CreateNewEntity() { Entity entity = this.EntityManager.CreateEntity(typeof(TestComponent)); this.EntityManager.SetComponentData(entity, new TestComponent() { reference = this.reference }); } }
创建blob Asset实际上只意味着拥有指向此类资产或数据的BlobAssetReference。要创建一个,我们要创建一个BlobBuilder。使用ConstructRoot()并通过引用获得一个变量。将数值存储到该变量中,然后调用CreateBlobAssetReference(),该变量已经返回BlobAssetReference。
在BlobAssetSystem.OnCreate()中,我们创建了一个实体,添加了一个测试组件,该组件使用我们创建的BlobAssetReference。在OnUpdate()中,我们显示来自BlobAssetReference的值,同时断言它仍然等于我们为其设置的值(DATA_VALUE)。
我添加了一个方法CreateNewEntity(),所以我们可以看到一个BlobAssetReference可以被多个实体使用。下面是测试代码:
[Test]
public void BasicUsageTest() {
BlobAssetSystem system = this.World.GetOrCreateSystem<BlobAssetSystem>();
system.Update();
system.CreateNewEntity();
system.Update();
}
该测试通过并显示。
有3条调试日志,因为在系统创建时已经创建了一个实体。在调用第一个Update()时,它显示了第一行。然后我们调用CreateNewEntity(),创建一个新的类似实体。现在有两个了。在第二次调用Update()时,它显示Debug.Log()的后两行。
现在让我们尝试一下半真实的游戏用法。假设我们有一个武器统计的数据库。一个单一的武器数据条目看起来像这样:
public readonly struct WeaponData {
public readonly int damage;
public readonly float projectileSpeed;
public WeaponData(int damage, float projectileSpeed) {
this.damage = damage;
this.projectileSpeed = projectileSpeed;
}
}
一个WeaponData的集合将被存储在NativeHashMap<FixedString64, WeaponData>中,其中FixedString64是武器的ID。换句话说,它是一个WeaponData的查询表。我们可以为这个哈希图创建一个blob Asset并在组件中使用它。假设这个组件看起来像这样:
public readonly struct Weapon : IComponentData {
public readonly FixedString64 weaponId;
private readonly BlobAssetReference<NativeHashMap<FixedString64, WeaponData>> weaponMapReference;
public Weapon(FixedString64 weaponId, BlobAssetReference<NativeHashMap<FixedString64, WeaponData>> weaponMapReference) {
this.weaponId = weaponId;
this.weaponMapReference = weaponMapReference;
}
public int Damage {
get {
return this.weaponMapReference.Value[this.weaponId].damage;
}
}
}
这是个很有创意的例子,但请听我说。我们有这样一个组件,它有一个weaponId,用来作为使用BlobAssetReference查询WeaponData的关键。例如,属性Damage的值是通过使用weaponMapReference从查询表中获取的。
这里有一个系统,把这一切放在一起。
private class WeaponSystem : SystemBase { private NativeHashMap<FixedString64, WeaponData> weaponMap; private BlobAssetReference<NativeHashMap<FixedString64, WeaponData>> weaponMapReference; protected override void OnCreate() { // 填充武器Map // 你可以想象,这里的数据可能来自于一个ScriptableObject。 // 或JSON或XML或来自另一个服务器 this.weaponMap = new NativeHashMap<FixedString64, WeaponData>(2, Allocator.Persistent); this.weaponMap["Bow"] = new WeaponData(1, 5.0f); this.weaponMap["Gun"] = new WeaponData(2, 100.0f); // 准备 BlobAssetReference BlobBuilder builder = new BlobBuilder(Allocator.Persistent); ref NativeHashMap<FixedString64, WeaponData> blobData = ref builder.ConstructRoot<NativeHashMap<FixedString64, WeaponData>>(); blobData = this.weaponMap; this.weaponMapReference = builder.CreateBlobAssetReference<NativeHashMap<FixedString64, WeaponData>>(Allocator.TempJob); builder.Dispose(); // 创建实体 Entity bowEntity = this.EntityManager.CreateEntity(typeof(Weapon)); this.EntityManager.SetComponentData(bowEntity, new Weapon("Bow", this.weaponMapReference)); Entity gunEntity = this.EntityManager.CreateEntity(typeof(Weapon)); this.EntityManager.SetComponentData(gunEntity, new Weapon("Gun", this.weaponMapReference)); } protected override void OnUpdate() { // This is needed so it can be used inside the ForEach() NativeHashMap<FixedString64, WeaponData> localMap = this.weaponMap; this.Entities.ForEach(delegate(in Weapon weapon) { Debug.Log($"{weapon.weaponId}: {weapon.Damage}"); Assert.IsTrue(localMap[weapon.weaponId].damage == weapon.Damage); }).WithoutBurst().Run(); } protected override void OnDestroy() { this.weaponMapReference.Dispose(); this.weaponMap.Dispose(); } }
在OnCreate()中,我们填充了武器Map,这将是我们的查询表。然后我们为该Map创建一个BlobAssetReference。之后,我们用武器组件创建两个实体,一个是弓,一个是枪。
在OnUpdate()中,我们显示每个武器的伤害,并将它们与原始的NativeHashMap进行比较,以验证它们是否仍然相同。
如果我们运行这个测试:
[Test]
public void NativeCollectionInSystemUsage() {
WeaponSystem weaponSystem = this.World.GetOrCreateSystem<WeaponSystem>();
weaponSystem.Update();
}
它将通过,并将显示每个实体的武器伤害。
在写这篇文章时,我意识到我可以用这个方法来做一个实用的方法。
public static class BlobAssetUtils {
public static BlobAssetReference<T> CreateReference<T>(T value, Allocator allocator) where T : struct {
BlobBuilder builder = new BlobBuilder(Allocator.TempJob);
ref T data = ref builder.ConstructRoot<T>();
data = value;
BlobAssetReference<T> reference = builder.CreateBlobAssetReference<T>(allocator);
builder.Dispose();
return reference;
}
}
这样,之前系统中的blob资产创建部分就可以简化为:
// Basic usage system
this.reference = BlobAssetUtils.CreateReference(DATA_VALUE, Allocator.Persistent);
// Weapon example system
this.weaponMapReference = BlobAssetUtils.CreateReference(this.weaponMap, Allocator.Persistent);
这就是我现在所拥有的一切。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。