Arkit with Multiple Users

ARKit with multiple users

Now, after releasing ARKit 2.0 at WWDC 2018, it's possible to make games for 2....6 users.

For this, you need to use ARWorldMap class. By saving world maps and using them to start new sessions, your iOS application can now add new Augmented Reality capabilities: multiuser and persistent AR experiences.

AR Multiuser experiences. Now you may create a shared frame of a reference by sending archived ARWorldMap objects to a nearby iPhone or iPad. With several devices simultaneously tracking the same world map, you may build an experience where all users (up to 6) can share and see the same virtual 3D content (use Pixar's USDZ file format for 3D in Xcode 10+ and iOS 12+).

session.getCurrentWorldMap { worldMap, error in 
guard let worldMap = worldMap else {
showAlert(error)
return
}
}

let configuration = ARWorldTrackingConfiguration()
configuration.initialWorldMap = worldMap
session.run(configuration)

AR Persistent experiences. If you save a world map and then your iOS application becomes inactive, you can easily restore it in the next launch of app and in the same physical environment. You can use ARAnchors from the resumed world map to place the same virtual 3D content (in USDZ or DAE format) at the same positions from the previous saved session.

AR Multi User Projectiles (Swift4)

If you are using the example provided by Apple, I was able to get your code work by doing the following.

Firstly when both devices where connected, I used the following function to send some test data:

/// 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)
}

With the cloudSession sendDataToUsers(_ data: Data) function being called in the 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 }
}

Then in our main ViewController handling the data like so:

//----------------------
// 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)")

}
}

}

Which yields something like this:

Sample Image

An example of sending multiple data types can be seen here: ARWorldMaps

Hope it points you in the right direction...

ARKit on different iPhones

Of course different iPhone models present different resolutions. There's a big difference between iPhone's screen size and viewport size. Look at this table. In some cases viewport size is 1/9 of screen size, sometimes – 1/4. Though, some models have identical screen size and viewport size.



Leave a reply



Submit