If I assign a sound in Reality Composer, can I stop it programmatically in RealityKit?
Asked Answered
W

1

1

I assign a sound to a scene in Reality Composer as in the image below

enter image description here

I would like to create an UIButton that can be pressed to stop the sound. Can I do that?

War answered 5/1, 2023 at 15:29 Comment(0)
I
1

I created three Reality Composer actions for box scene: they are Play Sound, Spin and Notify. As you can see loop playback is turned ON.

enter image description here

SwiftUI

My code is as simple as that. Primary stop() methods are for immediate stop of audio and animation. Secondary stop() methods are the content of completion handler for onAction property.

import SwiftUI
import RealityKit

struct ContentView : View {
    
    @State var completion: ((Entity?) -> ()) = { _ in }
    @State var arView = ARView(frame: .zero)
    @State var boxScene = try! Experience.loadBox()
    
    var body: some View {
        ZStack {
            ARViewContainer(arView: $arView, boxScene: $boxScene)
               .ignoresSafeArea()
            VStack {
                Button("Stop") {
                    boxScene.steelBox?.stopAllAudio()              // 1
                    boxScene.steelBox?.stopAllAnimations()         // 1
                    print("Actions are stopped.")
                    
                    completion = {
                        $0?.stopAllAudio()                         // 2
                        $0?.stopAllAnimations()                    // 2
                        $0?.scale = [1,15,1]
                        print("Both actions were completely stopped.")
                    }

                    boxScene.actions.occured.onAction = completion
                }
                Spacer()
            }
        }
    }
}

And here's an ordinary binding.

struct ARViewContainer : UIViewRepresentable {
    
    @Binding var arView: ARView
    @Binding var boxScene: Experience.Box
    
    func makeUIView(context: Context) -> ARView {
        arView.scene.anchors.append(boxScene)
        return arView
    }
    func updateUIView(_ view: ARView, context: Context) { }
}

Note that Notify action is the last one in the sequence. After what time the secondary stop() methods will be applied (I mean the content of the completion handler), depends on the duration of the audio file or animation (my audio's duration is 14 sec).

enter image description here

UIKit

UIKit solution is somehow different and slightly simpler.

import UIKit
import RealityKit

class ViewController : UIViewController {       
    @IBOutlet var arView: ARView!
    let boxScene = try! Experience.loadBox()
    var completion: ((Entity?) -> Void)? = { _ in }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let rect = CGRect(x: 50, y: 50, width: 100, height: 50)
        let stopButton = UIButton(frame: rect)
        stopButton.setTitle("Stop", for: .normal)
        stopButton.addTarget(self, 
                           action: #selector(stopPlayingAudioAndAnime), 
                              for: .touchUpInside)
        arView.addSubview(stopButton)
        arView.scene.anchors.append(boxScene)
    }

    @objc private func stopPlayingAudioAndAnime() {            
        boxScene.steelBox?.stopAllAudio()                     // 1
        boxScene.steelBox?.stopAllAnimations()                // 1
        
        completion = { entity in
            entity?.stopAllAudio()                            // 2
            entity?.stopAllAnimations()                       // 2
            print("Completely Stopped")
        }           
        boxScene.actions.occured.onAction = completion
    }
}

P. S.

However, if you'll be using Play Music action (in other words, on the scene basis, not on the object basis, like Play Sound action), you can implement your idea this way:

arView.scene.anchors[0].children[0].stopAllAudio()
    
completion = { entity in
    entity?.scene?.anchors[0].children[0].stopAllAudio()
}
Infusive answered 6/1, 2023 at 12:37 Comment(3)
Can I use arView.scene.anchors[0].stopAllAudio() ?War
Nice! Additionally, I have 2 question: 1) Why do you need to add .children[0] to the code? and 2) Can I use .playAudio() to resume playback after stopping?War
1) It's hierarchy. AnchorEntity doesn't have needed components. 2) post another question.Infusive

© 2022 - 2024 — McMap. All rights reserved.