swiftuirealitykitvisionos

What are the `registerSystem()` and `registerComponent()` methods?


I want to know the actual function of the registerComponent method. It seems that it don't have any effect whether or not call this method.

In my demo, I have a custom component, I can set it on an entity and get it without calling ControllableComponent.registerComponent()

struct ControllableComponent: Component { ... }
let entity = Entity()
entity.components.set(ControllableComponent())
let component = entity.components[ControllableComponent.self]

In the apple example Hello Word, I commented out RotationComponent.registerComponent() but the earth model still rotated

init() {
    // Register all the custom components and systems that the app uses.
    // RotationComponent.registerComponent()
    RotationSystem.registerSystem()
    TraceComponent.registerComponent()
    TraceSystem.registerSystem()
    SunPositionComponent.registerComponent()
    SunPositionSystem.registerSystem()
}

Solution

  • Registering Systems and custom Components in RealityKit

    The registerSystem() static method defines a System in RealityKit's Entity-Component-System (ECS for short) pattern. You need to call this method only once before using your System. The same way, you should use the registerComponent() static method for a custom component registration. However, at the moment, using the registerComponent() method (in iOS, macOS, and visionOS) seems to be pro forma, although this is not specified anywhere in the official documentation. So, to implement a system and a custom component type for your scene, use RealityKit's regular protocol-oriented paradigm and composition-oriented design:

    import SwiftUI
    import RealityKit
        
    struct VampireComponent : Component {
        var str: String
    }
    

    class MakeVampireTransparent : System {
        static let query = EntityQuery(where: .has(VampireComponent.self))
        
        required init(scene: RealityKit.Scene) {
            print("RealityKit ECS pattern is running")
        }
        func update(context: SceneUpdateContext) {
            for (_, e) in context.scene.performQuery(Self.query).enumerated() {
                e.components[OpacityComponent.self] = .init(opacity: 0.25)
            }
        }
    }
    

    Registering the system:

    @main struct TheMightyVampiresApp : App {
        var body: some Scene {
            ImmersiveSpace() {
                ContentView()
                    .onAppear {
                        // mandatory usage of registerSystem() method
                        MakeVampireTransparent.registerSystem()
                    }
            }
        }
    }
    

    Registering and adding the component.

    Formally, according to the documentation, you are required to call registerComponent() method once if you are creating a custom component in RealityKit. But in practice, you don't have to call this method - your custom component will work anyway. Apparently, somewhere in the depths of the framework's code it has already been written by engineers from Cupertino.

    struct ContentView : View {
        let peasant = try! Entity.load(named: "Peasant.usdz")
        let vampire = try! Entity.load(named: "Vampire.usdz")
        
        init() {
            peasant.position = [-0.5, 0.0,-3.0]
            vampire.position = [+0.5, 0.0,-3.0]
    
            // formal usage of registerComponent() method
            VampireComponent.registerComponent()
    
            vampire.components[VampireComponent.self] = .init(str: "vamp01")
        }
    
        var body: some View {
            RealityView { rvc in
                rvc.add(peasant)    // Opacity = 1.00
                rvc.add(vampire)    // Opacity = 0.25
    
                print(peasant.components.has(VampireComponent.self)) // FALSE
                print(vampire.components.has(VampireComponent.self)) // TRUE
        }
    }
    

    Now you can use a custom VampireComponent for multiple models in a scene, along with the MakeVampireTransparent system's capabilities, to efficiently handle updates of those models. With systems, RealityKit calls only one update method every frame (out of 90 frames/sec on visionOS, and out of 60 frames/sec on iOS) per system, not per each entity.