I’m implementing a RealityKit Entity Component System and have an issue with my component definition. Here is a simple example of what I’m doing:
import Foundation
import RealityFoundation
public struct ValueComponent: Component, Codable {
public var value: Int = 0
}
public protocol HasValueComponent {}
extension HasValueComponent where Self: Entity {
var value: Int {
get { components[ValueComponent.self]?.value ?? 0 }
set { components[ValueComponent.self]?.value = newValue }
}
}
After creating a entity I initialize the component like this:
// ValueComponent.registerComponent()
modelEntity.name = "model"
modelEntity.components[ValueComponent.self] = .init(value: 123)
Later I try to access the component but always get nil. The component exists in the components set but the cast to HasValueComponent fails.
guard let model = arView.scene.findEntity(named: "model") as? Entity & HasValueComponent else { return }
print(model.components[ValueComponent.self])
What am I doing wrong? Thanks!
CodePudding user response:
In RealityKit (and RealityFoundation), you have to call a registerComponent()
type method once for every custom component type that you want to use in your app, before you use it.
public static func registerComponent()
You don’t need to call this method for built-in component types, like CollisionComponent
.
ValueComponent.registerComponent()
The same way you have to call registerSystem()
type method to use a System (ECS pattern).
Solution
Seems that type casting to protocols that implement custom components isn't supported. Inexplicably why. Thus, use the following solution.
import UIKit
import RealityKit
protocol HasValueComponent { }
extension HasValueComponent where Self: Entity {
var val: Int {
get { components[ValueComponent.self]?.val ?? .zero }
set { components[ValueComponent.self]?.val = newValue }
}
}
struct ValueComponent: Component {
var val: Int
}
Primitive.swift
class Primitive: Entity, HasModel, HasValueComponent {
required init() {
super.init()
ValueComponent.registerComponent()
self.model = ModelComponent(mesh: .generateSphere(radius: 0.1),
materials: [SimpleMaterial()])
self.name = "model"
self.components.set(ValueComponent(val: 1234))
}
}
ViewController.swift
class ViewController: UIViewController {
var arView = ARView(frame: .zero)
let primitive = Primitive()
override func viewDidLoad() {
super.viewDidLoad()
arView.frame = self.view.frame
self.view.addSubview(arView)
let anchor = AnchorEntity()
anchor.addChild(self.primitive)
arView.scene.addAnchor(anchor)
if let model = arView.scene.findEntity(named: "model") {
print(model.components[ValueComponent.self]!.val) // 1234
print(model.components.has(ValueComponent.self)) // true
}
}
}