网站建设制作培训,网页超链接到别的网站404,金融网站模版,非洲用什么网站做采购协作 Session 可以很方便地实现多用户之间的AR体验实时共享#xff0c;但开发者需要自行负责并确保AR场景的完整性#xff0c;自行负责虚拟物体的创建与销毁。为简化同步操作#xff0c;RealityKit 内建了同步机制#xff0c;RealityKit 同步机制基于 Multipeer Connectivi… 协作 Session 可以很方便地实现多用户之间的AR体验实时共享但开发者需要自行负责并确保AR场景的完整性自行负责虚拟物体的创建与销毁。为简化同步操作RealityKit 内建了同步机制RealityKit 同步机制基于 Multipeer Connectivity当设置好 MultipeerConnectivityService 属性之后RealityKit 会自动在参与者之间同步实体对象Entity。
RealityKit 同步服务机制 在 RealityKit 中组成场景Scene的基本元素是实体Entity实体由其所挂载的组件Component定义外观和行为RealityKit 所有的实体类都继承自 Entity 基类如 AnchorEntity、ModelEntityEntity 基类包含了两个组件Transtorm 组件和 Synchronization 组件Transform 组件用于空间定位而Synchronization 组件用于同步。因此RealityKit 中所有的实体对象都默认带有 Synchronization 组件即都可以通过网络进行同步这也是 RealityKit 同步的技术基础。 虽然 RealityKit 网络数据传输仍然依赖于 Multipeer Connectivity但相对于协作 Session在 RealityKit中开发人员不再需要自行处理数据的发送与接收处理工作RealityKit 会自动进行相关操作从而大大简化了开发流程。 使用 RealityKit 的同步服务功能只需要两步操作1使用 Multipeer Connectivity 设置好 MCSession并生成一个 MultipeerConnectivityService 对象。2 将生成的 MultipeerConnectivityService 对象赋给 ARView.scene 的 synchronizationService 属性场景中所有的实体对象都将继承该值。在进行这两步操作之后后续的所有同步操作完全由 RealityKit 自动处理使用 RealtiyKit 同步服务的完整代码如下所示稍后我们会对代码进行详细解析。
//
// SyncARSession.swift
// ARKitDeamo
//
// Created by zhaoquan du on 2024/2/28.
//import SwiftUI
import ARKit
import RealityKit
import MultipeerConnectivitystruct SyncARSession: View {static var arView: ARView!static var multipeerSession: MultipeerSession?var body: some View {SnycARSessionContent().onDisappear {SyncARSession.arView.session.delegate nilSyncARSession.arView.session.pause()SyncARSession.arView nilSyncARSession.multipeerSession?.endConnect()SyncARSession.multipeerSession nilprint(SyncARSession onDisappear)}.edgesIgnoringSafeArea(.all).navigationTitle(ARSession同步)}}struct SnycARSessionContent: UIViewRepresentable {func makeUIView(context: Context) - some ARView {let arView ARView(frame: .zero)arView.automaticallyConfigureSession falselet config ARWorldTrackingConfiguration()config.planeDetection .horizontalconfig.isCollaborationEnabled truearView.session.run(config,options: [.resetTracking,.removeExistingAnchors])arView.session.delegate context.coordinatorSyncARSession.arView arViewcontext.coordinator.createPlane()context.coordinator.addGesture()return arView}func updateUIView(_ uiView: UIViewType, context: Context) {}func makeCoordinator() - Coordinator {Coordinator()}class Coordinator: NSObject,ARSessionDelegate {var arView: ARView? {return SyncARSession.arView}var multipeerSession: MultipeerSession? {return SyncARSession.multipeerSession}var planeEntity : ModelEntity? nilvar raycastResult : ARRaycastResult?func createPlane(){SyncARSession.multipeerSession MultipeerSession(serviceType: sync-session,receivedDataHandler: receiveData(data:from:), peerJoinedHandler: peerJoined(_:), peerLeftHandler: peerLeft(_:), peerDiscoveredHandler: peerDiscovered(_:))let planeMesh MeshResource.generatePlane(width: 0.15, depth: 0.15)let planeMaterial SimpleMaterial(color:.white,isMetallic: false)planeEntity ModelEntity(mesh: planeMesh, materials: [planeMaterial])let planeAnchor AnchorEntity(plane: .horizontal)planeAnchor.synchronization nildo {let planeMesh MeshResource.generatePlane(width: 0.15, depth: 0.15)var planeMaterial SimpleMaterial(color: SimpleMaterial.Color.red, isMetallic: false)planeMaterial.color try SimpleMaterial.BaseColor(tint:UIColor.yellow.withAlphaComponent(0.9999), texture: MaterialParameters.Texture(TextureResource.load(named: AR_Placement_Indicator)))planeEntity ModelEntity(mesh: planeMesh, materials: [planeMaterial])planeAnchor.addChild(planeEntity!)arView?.scene.addAnchor(planeAnchor)arView?.scene.synchronizationService multipeerSession?.syncService} catch let error {print(加载文件失败:\(error))}}func randomColor() - UIColor{return UIColor(red:CGFloat(arc4random()%256)/255.0,green:CGFloat(arc4random()%256)/255.0,blue: CGFloat(arc4random()%256)/255.0,alpha: 1.0 )}func addGesture(){let tap UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))arView?.addGestureRecognizer(tap)}objc func handleTap(_ sender: UITapGestureRecognizer? nil) {guard let raycastResult raycastResult else {print(还未检测到平面)return}let box ModelEntity(mesh: MeshResource.generateBox(size: 0.1), materials: [SimpleMaterial.init(color: randomColor(), isMetallic: false)])box.position [0,0.05,0]let anchorEntity AnchorEntity(raycastResult: raycastResult)anchorEntity.addChild(box)arView?.scene.addAnchor(anchorEntity)}//ARSessionDelegatefunc session(_ session: ARSession, didUpdate frame: ARFrame) {guard let arView arView, let result arView.raycast(from: arView.center, allowing: .estimatedPlane, alignment: .horizontal).first else{return}raycastResult resultplaneEntity?.setTransformMatrix(result.worldTransform, relativeTo: nil)}func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {}func receiveData(data:Data,from peer: MCPeerID){}func peerDiscovered(_ peer: MCPeerID) - Bool {guard let multipeerSession multipeerSession else {return false}if multipeerSession.connectedPeers.count 3 {return false}else{return true}}func peerJoined(_ peer: MCPeerID) {}func peerLeft(_ peer: MCPeerID) {}}
}#Preview {SyncARSession()
} 代码比上一节代码中的代码要清爽很多一方面是RealityKit 的同步服务机制简化了人工干预代码详细功能如下 1 初始化 Multipeer Session 类设置 RealityKit 的同步服务。 2进行平面检测在检测到可用平面时实例化一个指示图标用于指示放置位置。 3添加屏幕单击手势在平面可用时单击屏幕会在指示图标位置放置一个颜色随机的立方体。 在第1项功能中即 createPlane方法中的代码除了在检测到平面时创建一个指示图标还初始化了
MultipeerSession 类传入的是一个用于区分网络服务的 serviceName然后使用语句 self.scene. synchronizationService multipeerSession.syncService设置 RealityKit 的同步服务功能。 在第3项功能中即 handTap方法中代码我们直接在指示图标位置生成了一个颜色随机的立方体。 需要注意的是这里并没有生成 ARAnchor 对象因为在 RealityKit 中所有的 AnchorEntity 类都会自动进行同步。 在两台设备 A和B上同时运行本案例确保两台设备连接到同一个WiFi网络或者都打开蓝牙在A设备检测到的平面上单击添加立方体在A、B 连接顺畅的情况下可以看到B设备也会同步出现该立方体并且立方体所在物理世界中的位置与A设备中的一致同理在B设备检测到的平面上单击添加立方体A设备也会同步出现该立方体并且立方体所在物理世界中的位置与B设备中的一致效果如图所示。
使用 RealityKit 同步服务注意事项 Reality Kit 同步服务让 AR体验实时共享变得前所未有地方便虚拟元素可以实时地共享到所有参与方而这主要归功于 Synchronization组件该组件的主要功能就是通过网络在不同设备间实时同步实体对象其主要属性有俩个。identifierisOwner每一个实体对象在网络中的唯一标识符布尔值用于标识本设备是否拥有该实体对象的所有权。ownershipTransferMode所有权转移类型为 SynchronizationComponent. OwnershipTransferMode 枚举值该枚举共有两个值autoAccept 为自动授受所有权转移manual需要使用者进行所有权转移授权。 可以看出Synchronization 组件可以对实体对象进行非常严格的所有权控制防止不经授权对其他设备生成的实体对象进行操作。所有权是 Synchronization 组件中重要的概念每一个创建实体对象的ARSession 拥有对该实体对象的所有权只有实体对象的所有者才有权修改该实体对象如修改尺寸、修改材质、旋转、移动等修改结果尔后会同步到所有参与者。非实体对象所有者可以修改其本机场景中的实体对象但无法同步到其他参与者如果需要同步修改结果可以向实体对象所有者申请授权得到授权后就可以成为该实体对象的所有者修改结果可以同步到所有的参与者如图8–16所示。 RealityKit 这么处理的原因是为了防止未授权用户擅自修改其他参与方场景中的虚拟元素影响其他人的使用体验保证共享场景中的虚拟元素放置都符合预期。在图8-16中假设用户①与用户②已经通过 RealityKit 的同步服务进行了同步①号立方体由用户①创建②号立方体由用户②创建这时用户①与用户②都可以看到这两个立方体此时用户①可以对①号立方体进行任何修改修改结果会实时地同步到用户②用户①也可以对②号立方体进行修改但修改结果并不会被同步。 如果用户①希望能修改②号立方体并且同步到用户②那么用户①可以申请所有权在用户②同意授权后用户①对②号立方体所作的修改就能够同步到用户②。需要注意的是这时②号立方体的所有权已经转移到用户①如果用户②要对②号立方体进行修改操作用户②也需要向用户①申请授权即一个实体对象的所有者在同一时刻只有一个。进行实体对象操控授权的典型代码如下所示。
public extension HasSynchronization {func EntityManipulation() {if isOwner {//拥有某个实体的所有权可以进行处理} else {requestOwnership { failure inif failure .granted {//没有某个实体的所有权进行所有权申请得到授权后可以进行处理}}}}/// Execute the escaping completion if you are the entity owner, once you receive ownership/// or call result failure if ownership cannot be granted to the caller./// - Parameter completion: completion of type Result, success once ownership granted, failure if not grantedfunc runWithOwnership(completion: escaping (ResultHasSynchronization, Error) - Void) {if self.isOwner {// If caller is already the ownercompletion(.success(self))} else {self.requestOwnership { (result) inif result .granted {completion(.success(self))} else {completion(.failure(result .timedOut ?MHelperErrors.timedOut :MHelperErrors.failure))}}}}} 在代码中对实体对象进行操作时首先检查是否拥有该实体对象的所有权如果有则进行操作如果没有则向实体对象所有者申请授权如果授权申请通过则可以进行相应操作。 实体对象所有者可以设置实体授权模式RealityKit 支持两种实体授权模式其值由SynchronizationComponent. OwnershipTransferMode 枚举定义该枚举共有两个枚举值autoAccept自动授权和 manual手动授权。默认授权模式为 autoAccept即实体对象所有权会自动授权给任何参与者对该实体的所有权申请。设置为 manual 时当有参与者申请所有权时会触发 SynchronizationEvents.OwnershipRequest 事件我们需要在该事件的accept回调方法中对授权进行自定义处理代码实例如下。 //收到实体权限变更请求let subscribe arView.scene.subscribe(to: SynchronizationEvents.OwnershipRequest.self, { event inif (Int(event.entity.name) ?? 0) % 2 0 {//不允许变更权限print(------------不允许变更权限)return}else{//接受变更权限print(------------允许变更权限)event.accept()}})subscribes.append(subscribe)//监听添加的实体Entitylet sub arView.scene.subscribe(to: SceneEvents.DidAddEntity.self, {[weak self] event inself?.didAddEntitiy(entity: event.entity)})subscribes.append(sub)func didAddEntitiy(entity: Entity) {if let entity entity as? HasCollision {self.arView?.installGestures(.all, for: entity).forEach({ entityGestureRecognizer inentityGestureRecognizer.addTarget(self, action: #selector(handleModelGesture))})}}objc func handleModelGesture(_ sender: Any) {if let ges sender as? EntityGestureRecognizer {//获取权限ges.entity?.runWithOwnership(completion: { res inif case .success(_) res {}else {print(--------获取所有者权限失败)}})}}
public extension HasSynchronization {/// Execute the escaping completion if you are the entity owner, once you receive ownership/// or call result failure if ownership cannot be granted to the caller./// - Parameter completion: completion of type Result, success once ownership granted, failure if not grantedfunc runWithOwnership(completion: escaping (ResultHasSynchronization, Error) - Void) {if self.isOwner {// If caller is already the ownercompletion(.success(self))} else {self.requestOwnership { (result) inif result .granted {completion(.success(self))} else {completion(.failure(result .timedOut ?MHelperErrors.timedOut :MHelperErrors.failure))}}}}} 在一些场合下我们可能不想某些实体对象或者某些操作被共享这时候可以将该实体对象的同步组件设置为nil设置为 nil 的实体对象及其子对象将不会被共享。
具体代码地址GitHub - duzhaoquan/ARkitDemo