我想在 Entity 不仅加载而且已成功附加到 AnchorEntity 时运行一些函数。
现在我的实体正在异步加载。
func addModel(to planeAnchor: AnchorEntity) {
Entity.loadAsync(named: "SomeUSDZModel")
.sink { completion in
switch completion {
case .finished:
print("Ok")
case .failure(let error):
print(error.localizedDescription)
}
} receiveValue: { model in
// Entity should be added before the animation is started.
planeAnchor.addChild(model)
if let walkingAnimation = model.availableAnimations.first {
model.playAnimation(walkingAnimation.repeat(duration: .infinity),
transitionDuration: 1.25,
blendLayerOffset: 0,
separateAnimatedValue: false,
startsPaused: false)
}
self.model = model
}
.store(in: &subscriptions)
doSomething()
}
方法 doSomething() 过早触发。我想在一些条件检查后将其关闭。
我设法采取不同的方法来解决这个问题。
首先我创建了一个 AnchorEntity
let planeAnchor = AnchorEntity(
plane: .horizontal,
classification: .any,
minimumBounds: [0.5,0.5]
)
我立即将机器人添加到此锚点:
robot = MegaRobot(anchorEntity: planeAnchor, arView: self, gameSettings: gameSettings)
此时,我保持动画暂停但激活。
现在我要订阅我的飞机上 AnimationEvents 的更改Anchor:
func observeAnchorState() {
if let robot = robot {
self.gameSettings.gameStatus = .planeSearching
// 1. Subscribe to changes
self.anchorEntitySubscribtion = self.scene.subscribe(
to: SceneEvents.AnchoredStateChanged.self,
on: planeAnchor) { anchored in
// 3. if the change is the desired one, perform extra setup
if anchored.isAnchored {
robot.robotMode()
robot.animationController?.resume()
robot.activateRobotDragging()
self.gameSettings.gameStatus = .positioning
DispatchQueue.main.async {
// 4. Remove subscriber if further observations are not needed
self.anchorEntitySubscribtion?.cancel()
self.anchorEntitySubscribtion = nil
}
}
}
// 2. add planeAnchor to the scene
self.scene.anchors.append(planeAnchor)
} else {
print("Fail to load")
}
}
基于@Jakub Gawecki ansswer,我想要一种轻松知道实体何时锚定的方法,以便我可以添加动画,而不必为每个订阅声明变量。
特定于
RealityKit
的代码
extension Entity {
@MainActor
func onAnchored(content: any RealityViewContentProtocol, _ completion: @escaping () -> ()) {
assert(self.anchor != nil, "Should add the entity to an anchor first")
if self.isAnchored {
completion()
return
}
var subscriptionWrapper: EventSubscriptionWrapper? = .init()
subscriptionWrapper!.subscription = content.subscribe(
to: SceneEvents.AnchoredStateChanged.self,
on: self.anchor
) { anchor in
if anchor.isAnchored {
completion()
DispatchQueue.main.async {
subscriptionWrapper?.subscription?.cancel()
subscriptionWrapper = nil
}
}
}
}
/// We need a reference type in order to be able to use the subscription inside the callback
fileprivate class EventSubscriptionWrapper {
var subscription: EventSubscription? = nil
}
}
用途:
entity.onAnchored(content) {
print("Entity is anchored")
}