Динамически изменять текст сущности RealityKit

Я создал очень простую сцену ("SpeechScene"), используя Reality Composer, с одним объектом речевого выноски ("Speech Bubble"), привязанным к Face якорь.

Я загрузил эту сцену в код следующим образом:

let speechAnchor = try! Experience.loadSpeechScene()
arView.scene.anchors.append(speechAnchor)

let bubble = (arView.scene as? Experience.SpeechScene)?.speechBubble

Он отображается как ожидалось. Однако я хотел бы динамически изменять текст этой существующей сущности.

Я нашел аналогичный вопрос здесь, но непонятно мне, как обратиться кmeshResource свойство ванили RealityKit.Entity объект.

Это возможно? Спасибо!

3 ответа

Первый подход

Сначала вам нужно выяснить, что представляет собой иерархия в сцене Reality Composer, содержащая Bubble Speechобъект. Для этого я использовал простую команду print():

print(textAnchor.swift!.children[0].components.self)   /* Bubble Plate */

print(textAnchor.swift!.children[1].components.self)   /* Text Object */

Теперь я могу извлечь объект текстовой сущности:

let textEntity: Entity = textAnchor.swift!.children[1].children[0].children[0]

И объект сущности пузырьковой пластины:

let bubbleEntity: Entity = textAnchor.swift!.children[0]

Вот окончательная версия кода, которую вы можете адаптировать под свои нужды:

import RealityKit

class GameViewController: UIViewController {
    
    @IBOutlet var arView: ARView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let textAnchor = try! SomeText.loadTextScene()
        
        let textEntity: Entity = textAnchor.swift!.children[1].children[0].children[0]

        textAnchor.swift!.parent!.scale = [4,4,4]  // Scale for both objects
        
        var textModelComp: ModelComponent = (textEntity.components[ModelComponent])!
                
        var material = SimpleMaterial()
        material.baseColor = .color(.red)
        textModelComp.materials[0] = material

        textModelComp.mesh = .generateText("Obj-C",
                            extrusionDepth: 0.01,
                                      font: .systemFont(ofSize: 0.08),
                            containerFrame: CGRect(),
                                 alignment: .left,
                             lineBreakMode: .byCharWrapping)

        textEntity.position = [-0.1,-0.05, 0.01]

        textAnchor.swift!.children[1].children[0].children[0].components.set(textModelComp)
        arView.scene.anchors.append(textAnchor)
    }
}


Второй подход

И вы всегда можете использовать более простой подход для этого случая - создать несколько сцен в Reality Composer, каждая из которых должна содержать разные speech-object.

Учтите, что этот код не для отслеживания, это просто тест для динамического переключения двух объектов с использованием Tap Gesture. Затем вам нужно адаптировать этот код для отслеживания лиц.

import RealityKit

class ViewController: UIViewController {
    
    @IBOutlet var arView: ARView!
    var counter = 0
    var bonjourObject: FaceExperience.Bonjour? = nil
    var holaObject: FaceExperience.Hola? = nil
     
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // Reality Composer Scene named "Bonjour"
        // Model name – "french"
        bonjourObject = try! FaceExperience.loadBonjour()
        bonjourObject?.french?.scale = SIMD3(x: 2, y: 2, z: 2)
        bonjourObject?.french?.position.y = 0.25
        
        // Reality Composer Scene named "Hola"
        // Model name – "spanish"
        holaObject = try! FaceExperience.loadHola()
        holaObject?.spanish?.scale = SIMD3(x: 2, y: 2, z: 2)
        holaObject?.spanish?.position.z = 0.3
    }
    @IBAction func tapped(_ sender: UITapGestureRecognizer) {            
        if (counter % 2) == 0 {
            arView.scene.anchors.removeAll()
            arView.scene.anchors.append(holaObject!)
        } else {
            arView.scene.anchors.removeAll()
            arView.scene.anchors.append(bonjourObject!)
        }
        counter += 1
    }
}

Если вы хотите, чтобы текстовая часть находилась на одном месте - просто скопируйте и вставьте объект из одной сцены в другую.

@maxxfrazer прав в своем утверждении, что в настоящее время единственный способ динамически изменять текст - это заменить ModelComponentиз Entity при условии, конечно, что он придерживается HasModel Protocol.

Я написал простое расширение, которое может в этом помочь:

//-------------------------
//MARK: - Entity Extensions
//-------------------------

extension Entity{

  /// Changes The Text Of An Entity
  /// - Parameters:
  ///   - content: String
  func setText(_ content: String){ self.components[ModelComponent] = self.generatedModelComponent(text: content) }

  /// Generates A Model Component With The Specified Text
  /// - Parameter text: String
  func generatedModelComponent(text: String) -> ModelComponent{

    let modelComponent: ModelComponent = ModelComponent(

      mesh: .generateText(text, extrusionDepth: TextElements().extrusionDepth, font: TextElements().font,
                          containerFrame: .zero, alignment: .center, lineBreakMode: .byTruncatingTail),

      materials: [SimpleMaterial(color: TextElements().colour, isMetallic: true)]

    )

    return modelComponent
  }

}

//--------------------
//MARK:- Text Elements
//--------------------

/// The Base Setup Of The MeshResource
struct TextElements{

  let initialText = "Cube"
  let extrusionDepth: Float = 0.01
  let font: MeshResource.Font = MeshResource.Font.systemFont(ofSize: 0.05, weight: .bold)
  let colour: UIColor = .white

}

Допустим, вы создали Entity называется textEntity:

 var textEntity = Entity()

Затем вы можете установить динамическое изменение текста, заменив ModelComponent и установка MeshResource в любое время, просто вызвав следующий метод:

textEntity.setText("stackru")

Конечно, что касается центрирования или выравнивания текста, вам нужно будет проделать некоторые простые вычисления (которые я здесь опустил).

Надеюсь, это поможет.

Найдите сущность своей модели (возможно, поставив точку останова и сначала просмотрев дочерние элементы), найдите сущность, которая соответствует протоколу HasModel, затем замените ее модель другой с помощью generatetext:

https://developer.apple.com/documentation/realitykit/meshresource/3244422-generatetext

Другие вопросы по тегам