在实体系统中存储组件



注意:本简介是关于实体系统的。但是,即使你不知道这些是什么,或者你自己还没有实现它们,这也是非常基本的,如果你有一般的Javascript经验,你可能会有足够的资格回答

我在T=machine博客上读到关于实体系统的文章。

作者Adam建议,实体应该只是一个id,可以用来获取其组件(即实体应该表示的实际数据)。

我选择了所有实体都应该存储在"一个地方"的模型,实现这种存储的主要怀疑是许多人使用的数组方法,这意味着动态实体id表示属于实体的组件的索引,而组件在"一个地方"按类型分组(从现在起,我只称之为"存储"),我计划将其实现为SceneScene将是一个处理实体组成、存储的对象,并且可以对实体(.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中的一个对象,即没有"真实"数组,只是索引是对象属性(字典、哈希、映射)的数字"名称"。

这个想法很简单,数组有一个长度属性,允许循环知道在哪里停止迭代。通过简单地从数组中移除一个元素(记住,它是一个Object),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']
  1. 您必须意识到JavaScript并不像您期望的那样"工作"。性能方面的对象和数组是相同的,即数组的访问方式类似于字典
  2. 作用域与其他"c风格"语言不同。只有全局和函数作用域,即没有块作用域(永远不要在另一个for(var i)中写入for(var i))
  3. 内存中未定义的值与null的值完全相同。不同之处在于,null是故意丢失值,而undefined只是意外(非故意)丢失
  4. 不要通过执行if(array['two'])来检查字段是否存在,因为字段实际上可以包含undefined、null、0、"、false的错误值,并计算为false。始终使用if('two' in array)进行检查
  5. 当使用for(key in array)循环时,总是使用if(array.hasOwnProperty(key)),这样就不会迭代原型的属性(可以说是父属性)。此外,构造函数创建的对象也可能使用"constructor"键循环

最新更新