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.

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

报告相同问题?

悬赏问题

  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计