RealityKit Basics: Entities and Components

Taking a deeper look at creating entities with multiple components.

Overview

RealityKit uses an Entity Component System {ECS) pattern. We can think of an entity as an empty container and components as content/behaviors we can add to that container. Let’s look at two simple ways to create entities.

Create a entity manually

This will create a small red sphere. We create an entity, make a material, and generate a shape. Then we combine the mesh (shape) and the material to create a model component that we can add to our entity.

let subjectEntity = Entity()
subjectEntity.name = "Subject"

// Let's start with a material
var material = PhysicallyBasedMaterial()
material.baseColor.tint = .stepRed
material.roughness = 0.5
material.metallic = 0.0

// Then we will create a shape for our model
let shape = MeshResource.generateSphere(radius: 0.2)

// We can create a model component using a mesh (shape) and one or more materials
let model = ModelComponent(mesh: shape, materials: [material])

// Add the model component to the entity
subjectEntity.components.set(model)

Using ModelEntity

ModelEntity is a convenient way to create an entity with a model, shapes, and material. This assumes we already have a material we can use.

let sphere = ModelEntity(
    mesh: .generateSphere(radius: 0.03),
    materials: [material])

Adding a few more components

RealityKit has a lot of build-in component and we can even create our own. Let’s start by adding some components to support interactivity with our entity.

// visionOS will apply a hover effect component when we look at this entity
subjectEntity.components.set(HoverEffectComponent())

// InputTargetComponent will allow us to use system gestures such as tag and drag
subjectEntity.components.set(InputTargetComponent())

// Our entity must also have a CollisionComponent for gestures to work. There are a lot of other uses for collisions that we will cover later
subjectEntity.components.set(CollisionComponent(shapes: [ShapeResource.generateSphere(radius: 0.2)]))

Adding our entities to the scene

If you try to run the code above nothing will happen. That is because we haven’t added our entities to the scene. We can add our subject entity to the RealityView content. Then we can add our second entity as a child of the subject.

// Any root entities should be added to content like this.
content.add(subjectEntity)

// Add the sphere as a child to the subject.
subjectEntity.addChild(sphere)

// content.add(sphere) ❌ 
// we don't need to add the sphere to the content since we are adding it as a child to the subject

Bonus: adding a tap gesture

Let’s add a tap gesture to show that our hover, input, and collision components are working. You can learn more about system gestures on the Learn visionOS page.

.gesture(TapGesture()
    .targetedToAnyEntity() // allows us to target any entity in a RealityView
    .onEnded { value in
        let currentTransform = value.entity.transform
        let rotationDelta = simd_quatf(angle: .pi/12, axis: [0, 1, 0])
        let newOrientation = currentTransform.rotation * rotationDelta
        value.entity.setOrientation(newOrientation, relativeTo: nil)
    }
)

Video Demo

Full Example Code

struct Example039: View {
    var body: some View {
        RealityView { content in

            // 1. Create a subject entity for our scene
            let subjectEntity = Entity()
            subjectEntity.name = "Subject"

            // Let's start with a material
            var material = PhysicallyBasedMaterial()
            material.baseColor.tint = .stepRed
            material.roughness = 0.5
            material.metallic = 0.0

            // Then we will create a shape for our model
            let shape = MeshResource.generateSphere(radius: 0.2)

            // We can create a model component using a mesh (shape) and one or more materials
            let model = ModelComponent(mesh: shape, materials: [material])

            // Add the model component to the entity
            subjectEntity.components.set(model)

            // Let's add some more components
            // visionOS will apply a hover effect component when we look at this entity
            subjectEntity.components.set(HoverEffectComponent())
            // InputTargetComponent will allow us to use system gestures such as tag and drag
            subjectEntity.components.set(InputTargetComponent())
            // Our entity must also have a CollisionComponent for gestures to work. There are a lot of other uses for collisions that we will cover later
            subjectEntity.components.set(CollisionComponent(shapes: [ShapeResource.generateSphere(radius: 0.2)]))

            // 2. ModelEntity is a convenient way to create an entity with a model, shapes, and material.
            material.baseColor.tint = .stepBlue // Hack: use the same material and change the color *
            let sphere = ModelEntity(
                mesh: .generateSphere(radius: 0.03),
                materials: [material])
            // Set the position of the sphere relative to the subject
            sphere.setPosition([-0.25, 0.2, 0.1], relativeTo: subjectEntity)

            // 3. Add our entities to the scene
            // Make sure to add our subject to the scene. Any root entities should be added to content like this.
            content.add(subjectEntity)

            // Add the sphere as a child to the subject.
            subjectEntity.addChild(sphere)
            // content.add(sphere) ❌ we don't need to add the sphere to the content since we are adding it as a child to the subject

            // * Note about the material hack above. Once a component is added to an entity, changes to it in code are not reflected on that entity. In the code above, we created a red material and added it to the subject. Then we changed the color to blue and it did not change the color of the subject. This pattern holds true for all components in RealityKit.
        }
        // You can learn a lot more about system gestures from our series on Input
        .gesture(TapGesture()
            .targetedToAnyEntity()
            .onEnded { value in
                let currentTransform = value.entity.transform
                let rotationDelta = simd_quatf(angle: .pi/12, axis: [0, 1, 0])
                let newOrientation = currentTransform.rotation * rotationDelta
                value.entity.setOrientation(newOrientation, relativeTo: nil)
            }
        )
    }
}

Support our work so we can continue to bring you new examples and articles.

Download the Xcode project with this and many more examples from Step Into Vision.
Some examples are provided as standalone Xcode projects. You can find those here.

Questions or feedback?