AR Многопользовательские снаряды (Swift4) - PullRequest
0 голосов
/ 26 сентября 2018

В настоящее время я успешно делюсь картой мира AR по сеансу множителя и пытаюсь отправить отдельные данные снаряда (положение, угол, скорость, тип) по сети от обоих одноранговых клиентов, но я не совсем уверенкак сериализовать эти типы данных SCNVector3, используя Codable, и при этом я не могу найти какую-либо документацию по этому поводу ... именно поэтому я здесь.Мне нужно иметь возможность разархивировать данные следующим методом

let shouldSend = MovementData(velocity: data.velocity, angular: Float(data.angular), position: posStr , x:Float(data.x), y:Float(data.y),z:Float(data.z),type:"Striker")
        guard let sendData = try? NSKeyedArchiver.archivedData(withRootObject: shouldSend, requiringSecureCoding: true)
            else { fatalError("can't encode movementData") }

Я пытаюсь использовать следующее для кодирования данных

import SceneKit

open class MovementData: NSObject, NSSecureCoding {
   public static var supportsSecureCoding: Bool = true

var velocity = CGPoint.zero,
angular = Float(0),
position:String = "SCNVector3(x:0,y:0.5,z:0)",
orientation = SCNVector3(),
x = Float(0),
y = Float(0),
z = Float(0),
type = String()

enum Key:String {
    case velocity = "velocity"
    case angular = "angular"
    case position = "position"
    case x = "x"
    case y = "y"
    case z = "z"
    case type = "type"
}

public func encode(with aCoder: NSCoder) {
    aCoder.encode(velocity as CGPoint, forKey: Key.velocity.rawValue)
    aCoder.encode(angular as Float, forKey: Key.angular.rawValue)
     aCoder.encode(position as String, forKey: Key.position.rawValue)
    aCoder.encode(x as Float, forKey: Key.x.rawValue)
     aCoder.encode(y as Float, forKey: Key.y.rawValue)
     aCoder.encode(z as Float, forKey: Key.z.rawValue)
    aCoder.encode(type as String, forKey: Key.type.rawValue)


}

public convenience required init?(coder aDecoder: NSCoder) {
    let _velocity = aDecoder.decodeCGPoint(forKey: Key.velocity.rawValue)
    let _angular = aDecoder.decodeFloat(forKey: Key.angular.rawValue)
    let _position = aDecoder.decodeObject(forKey: Key.position.rawValue)
       let _x = aDecoder.decodeFloat(forKey: Key.x.rawValue)
        let _y = aDecoder.decodeFloat(forKey: Key.y.rawValue)
        let _z = aDecoder.decodeFloat(forKey: Key.z.rawValue)
    let _type = aDecoder.decodeObject(forKey: Key.type.rawValue)



    self.init(velocity: _velocity, angular: _angular, position: _position as! String, x:_x,y:_y,z:_z,type:_type as! String)
}

init(velocity: CGPoint, angular: Float, position:String, x:Float,y:Float,z:Float,type:String) {
    self.velocity = velocity
    self.angular = angular
    self.position = position
    self.x = x
    self.y = y
    self.z = z
    self.type = type
}}

Я использую следующую модель

//
//  Striker.swift


import Foundation
import SceneKit

public struct StrikerData: CustomStringConvertible {
      var velocity = CGPoint.zero,
 angular = CGFloat(0),
  //    position = SCNVector3()
x = CGFloat(0),
y = CGFloat(0.5),
z = CGFloat(0)
mutating func reset() {
    velocity = CGPoint.zero
    angular = 0
    x = 0
    y = 0.5
    z = 0
}

   public var description: String { 
      return "StrikerData(velocity: \(velocity), angular: \(angular),         position:  SCNVector3(x:\(x),y:\(y),z:\(z)))"
    }
}
struct Striker {
    var name: String
    var x: String
    var y: String
    var z: String
    var orientation: String
    var velocity:String
    init(name: String, orientation: String, velocity: String,x: String,     y: String,z:String) {
         self.name = name
        self.orientation = orientation
       self.velocity = velocity
       self.x = x
        self.y = y
        self.z = z
    }
  }



//MARK: - OpponentStriker
open class OpponentStriker:  SCNView {
var striker: Striker!

var trackingHandler: ((StrikerData) -> ())?
var beginHandler: (() -> Void)?
var stopHandler: (() -> Void)?
var substrate: OpponentStriker!

private var tracking = false
private(set) var data = StrikerData()






required public init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

@objc func listen() {

    if tracking {
        trackingHandler?(data)
    }
}

//MARK: - Overrides
    open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    if let touch = touches.first{

        let striker = touch.location(in: self)
        tracking = true
        beginHandler?()

    }
}

    open override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    for touch: AnyObject in touches {
        let location = touch.location(in: self)

        guard tracking else {
            return
        }

      print("Stopped here FIX THIS!!!!!!")
    }
}

    open override func touchesEnded(_ touches: Set<UITouch>, with event:     UIEvent?) {
    resetStriker()
}

    open override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    resetStriker()
}

// CustomStringConvertible protocol
open override var description: String {
    return "OpponentStriker(data: \(data))"
    print("OpponentStriker(data:\(data)")
}

// private methods
private func resetStriker() {
    tracking = false
        let moveToBack = SCNAction.move(to: SCNVector3(x:0, y:1, z: 0),     duration: TimeInterval(0.1))
    moveToBack.timingMode = .easeOut

    data.reset()
    stopHandler?();
    }
}

typealias ? = OpponentStriker

Затем я отправляю данные из контроллера представления при создании узла со следующей функцией:

    guard let tableScene = SCNScene(named: "art.scnassets/table.scn"),
        let strikerOne = tableScene.rootNode.childNode(withName:    "Striker", recursively: false)
        //            let strikerTwo =      tableScene.rootNode.childNode(withName: "Striker", recursively: false)
        else { print("Cant find strikerOne node")
            return
    }
    let nodeScale = SCNVector3(0.5,1.25,0.75)
    strikerOne.position = SCNVector3(x,y,z)
    strikerOne.physicsBody = SCNPhysicsBody(type: .kinematic , shape:    SCNPhysicsShape(node: strikerOne,  options:  [SCNPhysicsShape.Option.scale:   nodeScale]))
        strikerOne.physicsBody?.categoryBitMask =    BitMaskCategory.striker.rawValue
        strikerOne.physicsBody?.contactTestBitMask = BitMaskCategory.puck.rawValue
        self.strikerOneNode = strikerOne
        if player == "One" {
        self.selectedNode = strikerOne
        }

        self.sceneView.scene.rootNode.addChildNode(strikerOne)
    let mat4 = strikerOne.transform

    let f4x4 = simd_float4x4(mat4)

    let anchor = ARAnchor(name: "StrikerOne", transform: f4x4 )
    sceneView.session.add(anchor: anchor)

        // Send the anchor info to peers, so they can place the same    content.
        guard let data = try?    NSKeyedArchiver.archivedData(withRootObject: anchor, requiringSecureCoding: true)
            else { fatalError("can't encode anchor") }
        self.multipeerSession.sendToAllPeers(data)
        print("Striker One added")
        //        guard let data = try? NSKeyedArchiver.archivedData(withRootObject: strikerOne, requiringSecureCoding: true)
      self.sceneView.scene.rootNode.addChildNode(strikerTwo)

и пытаюсь десериализовать данные, используя NSKeyedUnarchiver

 if let movement = try  NSKeyedUnarchiver.unarchivedObject(ofClass:MovementData.self, from: data) {
                     print(" movement DATA FROM \(peer) with data  \(data)")
                    print(movement)


        }

Я получаю сообщение об ошибке:

can't decode data recieved from <MCPeerID: 0x28294b120 DisplayName = Montreaux’s iPhone>

Я использую следующую демонстрационную версию / файл Readme для создания многопользовательского режима AR:

Демо-версия Apple ARKit MultiUser

Вся помощь очень ценится,

Приветствия!

1 Ответ

0 голосов
/ 26 сентября 2018

Если вы используете пример, предоставленный Apple, я смог заставить ваш код работать следующим образом:

Во-первых, когда оба устройства были подключены, я использовал следующую функцию для отправки некоторых тестовых данных:

/// Send A Movement Data Object To Our Peers
@IBAction func pressToSend(){

    let shouldSend = MovementData(velocity: CGPoint.zero,
                                  angular: Float(10),
                                  position: "StackOverflow",
                                  x:Float(10), y:Float(20),z:Float(30),
                                  type:"BlackMirrorz")

    guard let sendData = try? NSKeyedArchiver.archivedData(withRootObject: shouldSend, requiringSecureCoding: true) else { fatalError("can't encode movementData") }

    cloudSession.sendDataToUsers(sendData)
}

При вызове функции cloudSession sendDataToUsers(_ data: Data) в Multipeer Class:

//--------------------------
// MARK: - MCSessionDelegate
//--------------------------

extension ARCloudShare: MCSessionDelegate {

    func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { }

    func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
        receivedDataHandler(data, peerID)
    }

    func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {
        fatalError("This Service Does Not Send Or Receive Streams")
    }

    func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {
        fatalError("This Service Does Not Send Or Receive Resources")
    }

    func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {
        fatalError("This Service Does Not Send Or Receive Resources")
    }

}

//---------------------------------------
// MARK: - MCNearbyServiceBrowserDelegate
//---------------------------------------

extension ARCloudShare: MCNearbyServiceBrowserDelegate {

    public func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String: String]?) {

        //Invite A New User To The Session
        browser.invitePeer(peerID, to: session, withContext: nil, timeout: 10)
    }

    public func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) { }

}

//------------------------------------------
// MARK: - MCNearbyServiceAdvertiserDelegate
//------------------------------------------

extension ARCloudShare: MCNearbyServiceAdvertiserDelegate {

    //----------------------------------------------------------
    // MARK: - Allows The User To Accept The Invitation To Share
    //----------------------------------------------------------

    func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {

        //Allow The User To Accept The Invitation & Join The Twunkl Session
        invitationHandler(true, self.session)
    }

}

class ARCloudShare: NSObject{

    static let serviceType = "arcloud-share"

    let myPeerID = MCPeerID(displayName: UIDevice.current.name)
    var session: MCSession!
    var serviceAdvertiser: MCNearbyServiceAdvertiser!
    var serviceBrowser: MCNearbyServiceBrowser!
    let receivedDataHandler: (Data, MCPeerID) -> Void

    //-----------------------
    // MARK: - Initialization
    //-----------------------

    init(receivedDataHandler: @escaping (Data, MCPeerID) -> Void ) {

        self.receivedDataHandler = receivedDataHandler

        super.init()

        session = MCSession(peer: myPeerID, securityIdentity: nil, encryptionPreference: .required)
        session.delegate = self

        serviceAdvertiser = MCNearbyServiceAdvertiser(peer: myPeerID, discoveryInfo: nil, serviceType: ARCloudShare.serviceType)
        serviceAdvertiser.delegate = self
        serviceAdvertiser.startAdvertisingPeer()

        serviceBrowser = MCNearbyServiceBrowser(peer: myPeerID, serviceType: ARCloudShare.serviceType)
        serviceBrowser.delegate = self
        serviceBrowser.startBrowsingForPeers()
    }

    //---------------------
    // MARK: - Data Sending
    //---------------------

    func sendDataToUsers(_ data: Data) {
        do {
            try session.send(data, toPeers: session.connectedPeers, with: .reliable)
        } catch {
            print("Error Sending Data To Users: \(error.localizedDescription)")
        }
    }

    //----------------------
    // MARK: - Peer Tracking
    //----------------------

    var connectedPeers: [MCPeerID] { return session.connectedPeers }
}

Затем в нашем главном ViewController обработка данных выполняется следующим образом:

//----------------------
// MARK: - Data Handling
//----------------------

/// Handles The Data Received From Our ARMultipeer Session
///
/// - Parameters:
///   - data: Data
///   - peer: MCPeerID
func receivedData(_ data: Data, from peer: MCPeerID) {

    if let unarchivedData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data){

    if unarchivedData is MovementData, let data = unarchivedData as? MovementData{

            print(data.orientation)
            print(data.position)

        }

        else {
            print("Unknown Data Recieved From = \(peer)")

        }
    }

}

Что приводит к примерно так:

enter image description here

Пример отправки нескольких типов данных можно увидеть здесь: ARWorldMaps

Надеюсь, это направит вас в правильном направлении ...

...