注意:本介绍是关于实体系统的。但是,即使您不知道这些是什么,或者没有自己实现它们,这也是非常基础的,如果您有一般的 Javascript 经验,您可能有足够的资格来回答。
我正在阅读articles关于 T=machine 博客上的实体系统。
作者 Adam 建议实体应该只是一个 id,可用于获取其组件(即该实体应该表示的实际数据)。
我选择了所有实体都应存储在“一个地方”的模型,我对实现此存储的主要怀疑是许多人使用的数组的数组方法,这意味着动态实体 id 表示属于实体的组件的索引,而组件则按类型在该“一个位置”进行分组(从现在起我将其称为“存储”),我将其称为“存储”。计划作为场景
来实现。 Scene
将是一个处理实体组合、存储的对象,并且可以对实体执行一些基本操作(.addComponent(entityID, component)
等)。
我不关心Scene
对象,我很确定这是一个很好的设计,但我不确定的是存储的实现。
我有两个选择:
A) 采用数组的数组方法,其中存储如下所示:
//storage[i][j] - i denotes component type, while j denotes the entity, this returns a component instance
//j this is the entity id
[
[ComponentPosition, ComponentPosition, ComponentPosition],
[ComponentVelocity, undefined, ComponentVelocity],
[ComponentCamera, undefined, undefined]
]
//It's obvious that the entity `1` doesn't have the velocity and camera components, for example.
B) 将存储对象实现为字典(技术上是 Javascript 中的对象)
{
"componentType":
{
"entityId": ComponentInstance
}
}
字典方法意味着实体 ID 是静态,这对于在实体系统本身之外实现游戏循环和其他功能来说似乎是一件非常好的事情。此外,这意味着系统可以轻松存储他们感兴趣的实体 ID 数组。显然,entityId 变量也可以是字符串,而不是整数索引。
我反对数组数组方法的原因是,删除实体会使其他实体 ID 在删除单个实体时发生更改。
实际的实现细节可能会很谨慎,但我想知道哪种方法性能更好?
我也感兴趣的事情(请尽可能跨平台,但如果需要,请使用 V8 作为示例):
- 访问属性时的开销有多大?它是如何实现的?假设它们是从本地范围内访问的。
- 内存中的
undefined
是什么,它占用了多少内存?我问这个问题是因为在数组数组方法中,所有内部数组都必须具有相同的长度,并且如果实体没有特定组件,则该字段将设置为undefined
.
最佳答案
不用担心数组。它是 JavaScript 中的一个对象,即没有“真正的”数组,它只是索引是对象属性(字典、哈希、映射)的数字“名称”。
这个想法很简单,数组有一个 length 属性,允许循环知道在哪里停止迭代。通过简单地从数组中删除一个元素(记住,它是一个对象),长度属性实际上并没有改变。所以...
// create an array object
var array = ['one','two', 'three'];
console.log(array.length); // 3
// these don't actually change the length
delete array['two']; // 'remove' the property with key 'two'
console.log(array.length); // 3
array['two'] = undefined; // put undefined as the value to the property with key 'two'
console.log(array.length); // 3
array.splice(1,1); // remove the second element, and reorder
console.log(array.length); // 2
console.log(array); // ['one','three']
- 您必须意识到 JavaScript 并不像您期望的那样“工作”。性能方面的对象和数组是相同的,即数组像字典一样访问;
- 范围与其他“c 风格”语言不同。 只有全局作用域和函数作用域,即没有 block 作用域(永远不要将 for(var i) 写在另一个 for(var i) 中);
- 内存中未定义的数量与 null 完全相同。区别在于 null 是故意缺失值,而 undefined 只是意外(非故意)缺失;
- 不要通过执行
if(array['two'])
来检查字段是否存在,因为字段实际上可以保存 undefined, null, 0, "", false 并评估为 false。始终使用if('two' in array)
; 检查
- 使用
for(key in array) 循环
时,始终使用if(array.hasOwnProperty(key))
,这样就不会迭代原型(prototype)的属性(父级的属性)从某种意义上说)。此外,由构造函数创建的对象也可能使用“构造函数”键循环。
关于javascript - 在实体系统中存储组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11595500/