我尝试制作Set
- 就像只接受trait
的每个子类型的一个条目一样。我的代码目前是:
import scala.reflect.ClassTag
trait Component
case class Entity(id: Int)
case class ComponentA(some: String) extends Component
case class ComponentB(other: Int) extends Component
case class ComponentC(thing: Boolean) extends Component
val components: Map[Entity, Set[Component]] = Map(Entity(1) -> Set(ComponentA("A"), ComponentB(1)))
def getComponent[A <: Component: ClassTag](entity: Entity): Option[A] = {
components.getOrElse(entity, Nil).collectFirst { case c: A => c }
}
getComponent[ComponentA](Entity(1))
getComponent[ComponentB](Entity(1))
getComponent[ComponentC](Entity(1))
我的实体列表将是数百万,但每个实体的组件将是~100。是否有更快的方法,使用某种类型的索引映射,以防止每次读取时出现 O(n) collectFirst
,并且每个读取都有filter
+ append
更新
我尝试了很多东西,但是我能得到的最接近的是创建一个用于ComponentType
密钥的Map[ComponentType, Component]
特征,然后让getComponent
方法返回一个通用Component
而不是实际请求的类型。这使我在运行时或每个调用者的模式匹配中键入cast,而不是让编译器为我投射它。
任何其他类型,枚举的组合,可能是专门的Set
或Map
类型,可以在这里提供帮助吗?
也许所有类型检查都不值得平均只进行100/2次迭代的开销,但我没有什么比较性能的。
答案 0 :(得分:3)
您可以使用ComponentType
作为二级结构来避免创建Map[ClassTag[_], Component]
特征:
val components: Map[Entity, Map[ClassTag[_], Component]] = Map(
Entity(1) -> Map(
implicitly[ClassTag[ComponentA]] -> ComponentA("A"),
implicitly[ClassTag[ComponentB]] -> ComponentB(1))
)
def getComponent[A <: Component](entity: Entity)(implicit tag: ClassTag[A]): Option[A] = {
components.getOrElse(entity, Map[ClassTag[_], Component]()).get(tag).asInstanceOf[Option[A]]
}
与实施addComponent
的方式相同:
def addComponent[A <: Component](entity: Entity, component: A)(implicit tag: ClassTag[A]): Map[Entity, Map[ClassTag[_], Component]] = {
components + (entity -> (components.getOrElse(entity, Map[ClassTag[_], Component]()) + (tag -> component)))
}
你无法避免以这种方式进行投射,但如果你在components
课程中隐藏Components
地图的实现,那么我认为你可以非常安全:
class Components {
private val components: Map[Entity, Map[ClassTag[_], Component]] = Map()
def getComponent[A <: Component](entity: Entity)(implicit tag: ClassTag[A]): Option[A] = ???
def addComponent[A <: Component](entity: Entity, component: A)(implicit tag: ClassTag[A]): Components = ???
}
<强>更新强>: 基于@RüdigerKlaehn的评论,我修改了代码,它现在更具可读性:
val components: Map[(Entity, ClassTag[_]), Component] = Map(
(Entity(1), implicitly[ClassTag[ComponentA]]) -> ComponentA("A"),
(Entity(1), implicitly[ClassTag[ComponentB]]) -> ComponentB(1)
)
def getComponent[A <: Component](entity: Entity)(implicit tag: ClassTag[A]): Option[A] = {
components.get((entity, tag)).asInstanceOf[Option[A]]
}
def addComponent[A <: Component](entity: Entity, component: A)(implicit tag: ClassTag[A]): Map[(Entity, ClassTag[_]), Component] = {
components + ((entity, tag) -> component)
}