douchi1945 2017-12-01 22:42
浏览 56
已采纳

什么是实现实体组件系统的最佳方法

Recently I am planing to implement a Entity-Component-System like Overwatch.

The major challenge(and difficulty) in my project is that, my engine allows user defined customized map, in which user was allowed to define customized unit. In another word, user can select which component they need for an entity type

For example

type Component interface {
    ComponentName() string
}

type HealthComponent struct {
    HP uint
}

type ManaComponent struct {
    MP uint
}

type ChiComponent struct{
        Chi uint
}
// assuming each component has implemented Component interface

The corresponding Entity definition is:

type Entity struct {
    ID         EID
    EntityName string
    Components map[string]Component
}

A user will have some entity definition in JSON format:

{
 "EntityName": "FootMan",
 "Components": {
  "HealthComponent": {
   "HP": 500
  }
 }
}
---------------------------------------------
{
 "EntityName": "Warlock",
 "Components": {
  "HealthComponent": {
   "HP": 250
  },
  "ManaComponent": {
   "MP": 100
  }
 }
}
---------------------------------------------
{
 "EntityName": "Monk",
 "Components": {
  "HealthComponent": {
   "HP": 250
  },
  "ChiComponent": {
   "Chi": 100
  }
 }
}

Please notice that ID is not included in JSON because we need to initialize it at run-time

So here comes the problem:
What is the most efficient way to build such an entity with a given JSON definition? Currently my solution is using a registry to maintain a map between struct type and component name

var componentRegistry = make(map[string]reflect.Type)
func init() {

    componentRegistry["ChiComponent"] = reflect.TypeOf(ChiComponent{})
    componentRegistry["HealthComponent"] = reflect.TypeOf(HealthComponent{})
    componentRegistry["ManaComponent"] = reflect.TypeOf(ManaComponent{})

}

The builder code is

func ComponentBuilder(name string) Component {

    v := reflect.New(componentRegistry[name])
    fmt.Println(v)
    return v.Interface().(Component)
}

func EntityBuilder(EntityName string, RequireComponent []string) *Entity {

    var entity = new(Entity)
    entity.ID = getNextAvailableID()
    entity.EntityName = EntityName
    entity.Components = make(map[string]Component)
    for _, v := range RequireComponent {
        entity.Components[v] = ComponentBuilder(v)
    }

    return entity
}

For each system that want to access a component in this entity needs to do following:

var d = monk_entity.Components["ChiComponent"].(*ChiComponent)
d.Chi = 13

var h = monk_entity.Components["HealthComponent"].(*HealthComponent)
h.HP = 313

It works, but I am using too much reflection in this approach and I am not able to assign initial value to entity, which was store in user defined JSON file. Is there any better way to do so?

  • 写回答

1条回答 默认 最新

  • douzhuo5671 2017-12-01 23:03
    关注

    Gor one thing, you can just use functions instead of reflection:

    type componentMaker func() Component  // Define generator signature
    
    var componentRegistry = make(map[string]componentMaker) // Update map type
    
    componentRegistry["ChiComponent"] = func() Component { return new(ChiComponent) } // Define generators
    
    entity.Components[v] = componentRegistry[v]() // Call generator
    

    And so on. No reflection needed.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥30 vmware exsi重置后的密码
  • ¥15 易盾点选的cb参数怎么解啊
  • ¥15 MATLAB运行显示错误,如何解决?
  • ¥15 c++头文件不能识别CDialog
  • ¥15 Excel发现不可读取的内容
  • ¥15 关于#stm32#的问题:CANOpen的PDO同步传输问题
  • ¥20 yolov5自定义Prune报错,如何解决?
  • ¥15 电磁场的matlab仿真
  • ¥15 mars2d在vue3中的引入问题
  • ¥50 h5唤醒支付宝并跳转至向小荷包转账界面