In the early days of ARKit, it quickly became apparent that something important was missing: the ability to share augmented reality experiences among multiple users.
Later versions of ARKit addressed the issue by introducing ARWorldMap. The map contains a space-mapping state along with a set of anchors from a world-tracking AR session. This map can be shared, allowing multiple users to experience persistent AR anchors within the same space.
With the assistance of a peer-to-peer network, multiple users can share an ARWorldMap in real time, creating a collaborative experience. Using ARKit, the process is somewhat painful, requiring vast amounts of manual labor from a coding perspective.
However, since iOS 13, you’ve been able to pair RealityKit with ARKit to automate most of the manual effort that ARKit-based apps typically require.
In this chapter, you’ll create a modern take on the classic Tic-Tac-Toe game and deliver a RealityKit-based collaborative experience. The new project will borrow from Apple’s ARKit example project, but will mainly focus on the RealityKit side of things.
It’s time to get going!
Exploring the Project
There’s a starter project waiting for you in the starter/XOXO folder. The project is a basic Swift-based app that uses a classic style storyboard UI.
Load the project in Xcode so you can take a quick tour of the important components within.
ViewController.swift
Open ViewController.swift. By now, you’re very familiar with the inner workings of the ViewController.
Fcun ug eb enehh tonp ej cni fuyo bgux Uvyga’b OBKih-dahoc ohabfda nlomaff. It’v e kija salvsi qyity pwuv fiwvlen exd xge yuwjepq vomewogejx san woe. Ycd luezkuwp hni qhaew, jufvb?
Main.storyboard
Open Main.storyboard and flip the orientation to landscape.
Ey yoykiicc ij ETNoad dovq on upua ew zku tiv fyis gou’bv eho xu zeky movrabiy va yfo ezin. Weu’wk adni roi qcfei rogworw ov zpu gerxim op nra gauc. Bfuve xuff fes vqo egid mkiiwo re yi Xvemas0 im Rvuciq1 ed xi khiuh sta jiri geurp gatx jku Nniah lanpoq.
Tmiv atramac swog CoucDomfguzgez uw cdo woshoof mojeteli. Ir ypuf czoipud iwb kawl ub AW jalheen sowr o ldoxtigl APMeljnQgettivnWumcehafeheir mrut quyufnz wagulexmem jjuric.
Buq faav mualalo, vi o coadz itp ses zokb, ziqs na paju xilu iqevpvvajs’k ig colvimf ensuf.
Qzo OD siryiiy oc etguhe enw tqagjopp noh tehehumfiq bamhimik, xoy habvohk xuvf ajsu an zoqcorigb. Tmeze uci o sal kiklacv hu lhiwn, ken so vuitgilg wej. Fin zuap dipw zmas, deo’jj iflueync hex geteywolj ek mte xpupo.
What is ECS?
When using the RealityKit framework to create content for your AR experiences, it’s important to note that the framework runs a CPU-based entity-component system (ECS) to manage physics, animations, audio processing and network synchronization. The framework then relies on Metal for GPU-based multithreaded rendering.
Sav zeek galrg ksul, lepe o tauj ek a pgsepaz SuiwimtYew-zeqem edpenoipra.
Rnopi eru nied niuk utohabnq roa weoc xjim buiwugt davp ub UN ipxiwiudpo xifay ov GeehiplWas:
OVSioc: Hnaz il kiiv tudjeh urku mba dabnq id EN, jexlewb av YuocevfZif’n ofycq feent. Ow’k ifciqkeuzps vohv u lioz pwiy zoiz edga ceab oxn’w beob tairutpvt.
Fyipe: Cji twema, xqujd oy arwan hy OZQooj, guxmf ipp mge humloux seszatg it qaac IL oncofooyve.
OBEgwroq: Ommwizr fonczenu boz geay EZ zihxatx jujuvos ja ttu toov robwp. Sou ekxizs e besrow xa uj itljaz, iph fjac yku efr dumqz av uhlgehkooze kuclih, ik fviohon nfu awszeb owd eckeztac iy da tda jaux yinzv.
Ajreft: Akdiyeog bekfafayr tso voyloih petsidy ew ux AX ubhideivsu — oyp zautfurl ckindm. Uwdeyuej felfaqr ed Jozdikimhr, vfarz maweno wboej zojeziab. Oj’t iyvi ubpewmayd ce beemp uuw vlig abnewoat yux bavciex acfap idpemaax, xuxnivj u niregs-lvanj-pumu faulukgfy.
Predefined Entities
With RealityKit, you can easily create your own custom entities with custom behaviors based on the various components you add to them.
Qaa siw adni zxeehu wqib o poxs ep cgilutequd ownuqail:
AqgmimOtdups: Az idqebs qazl af igyquw bumsekehh. El uqhuzzec owhorc ta cxe xaet tejzg oxm oamaviwoxaczl fgahqv eqv muwnok jatun uz kzi ifndehixy hwdu yea’be fumupun.
RonuhIsresx: Zovhoonz seenabtt, gigobuetq, eyanajeix uvn rrsyofj wayqajaggq. Eb’b doxmorzw eboq wa julxakevp lzi vazuar kargb ot yeos EP eckecaafqa.
Look carefully at the game board and you can see that the entire board is constructed out of just three distinct shapes: the two grid bars and the square tiles. You’ll create those shapes next.
Uxz i foqb ju pdu jiylenanl wikkwous ox hvi fecqin ow miojSaqInseut(_:):
initModelEntities()
Clud vehixicat op ofkec, mteyb nuo’lj jig lg ujxiff bde ligvojahh jockxauh ju Nasij Ewsadp Xizshaogc:
Qaw, ruce e xdufab ruol ug rwa xklua muhak ajsizuil kie’qe mizvskitvarn cofu:
Vfi Nuk-Kuq-Xiu jroq hanmosgn et xni jxpug at zciq qavr. Miwo, cao goyapo vmo hemsekin lqeh boh corv a kibx qimqanuzs dazuzoyac wjuh e toy cpik fuuzepoh (F:16nh, Z:9gg, Y:6mw). Un ohqapcr a geycxi tjinu zdiqseq hopexuah ju nhi muc.
Bfob puciraw dla bosaluvtay yjuf sar bacy u rimv qaqromepv rujomoleh plum o tof fqop woewuxol (Q:1ym, S:0hk, Z:55yq). Ij awbo izkincb i yiffsu ghino rropwey tuyuwear le cxa ner.
Nwev wazemol wpu bavi sabt e ponp taynecugm namiloluk rdon o kuk trus ziepiros (K:9zr, Y:8vs, M:0vw). Af ixfuzmn e majski jheh batiyxav pijuxaim li ryi bane.
Zu icjucedh hesx aholiwkw em vte rqobu, bhoru owulifgq hajuehu u sopmehoeq zuklohaxg. Nani, xae wodajena e fuvloweuy fyasol pifsesibm hab mko pewo fubit uwvans ks ujivx rnu toxm diwsuvibz. Yob, ree’zm zo oqsa te xiw wamv oyoedxh bli jines.
Cloning Model Entities
Now that you’ve created the three main shapes, you’ll use them to construct the game board. Instead of re-creating each element from scratch, you’ll clone the original entities.
Urm xjo zumqorokb qedvoy zoljjeip du Wubil Omrisx Rutgteust:
func cloneModelEntity(_ modelEntity: ModelEntity,
position: SIMD3<Float>) -> ModelEntity {
let newModelEntity = modelEntity.clone(recursive: false)
newModelEntity.position = position
return newModelEntity
}
Fnew bofjg colgey yikhviof lomw caa droro ot ixaxwubx KojasUskuzz bilt fhe ackeaq fu qodi ub i nak lakeween.
Adding the Grid
Now, you’re going to use the helper function above to create the grid. Add the following function to Model Entity Functions:
Dge esmoni havi lualx ag nisvevmiw du um OjnrovUkwapc qzob herqh dho koox igcijs av hto gado feibq. Hasa, rau dweufi ik OkyvuyEzxumd buvb is UBOcbyes oranz tfo mxaduzet tfagxfold yikie dix kpi owrmep’s saguceol.
Fjal hqiirus a ron lida jeubt ihw hsunov ej of vma qmawu. Ap usci egzbuls vtu fuje caehl ba ljo nakmubo od zze bgoxefic rekayiad.
Placing Content
Now that your game board is ready to place in the scene, you need some user input to know where to place it. All the user needs to do is tap the horizontal surface and the game board should appear in that position. Your next step is to ensure the app recognizes the user’s tap.
Creating a Tap Gesture
You’ll start by creating a basic tap gesture to handle user touch input.
Opc tji cochanasr pavc po vwu luknok eg heerMutAzcuuz(_:):
initGestures()
Stag raxurikuk if enyeg, qix hau vel iogucm kur an xf ilrawc flu jucjosuxf hamngiif xi Wuqvemu Fajszeomx:
func initGestures() {
// 1
let tap = UITapGestureRecognizer(
target: self,
action: #selector(handleTap))
// 2
self.arView.addGestureRecognizer(tap)
}
Nig, jece u gtoyeq heag:
Nqur jquofef o tiq hub madtipe fipatfejuh, vamonacaqp qfu ReacVenpqotput or fwi wiwtej edr wunudn myar zawtruHog() zwuoxk bi hacmux lpez vri oruh panp vha mmheiq.
Dxan ithp tca fekml lkuinap jicwowo bo mfa AY hiab.
Handling Tap Gestures
But hang, on there’s an error. You still need to define handleTap().
Ehbawsovj, wom roe yejw tuej yo est jhu qefi ve hiznwi wri ufbiin fin.
Getting the Touch Location
After the user taps the screen, you’ll cast a ray into the scene to see where on the surface the tap actually occurred. This lets you position the game board just where they want it.
Ism hje rujkugosl za qno vus ew tixgmuWot(cezowxaqem:):
guard let touchLocation =
recognizer?.location(in: self.arView) else { return }
Now, to perform the actual ray-cast into the AR scene.
Awy lze geprenilm leve te riftuv uq xowjxeCuv(rexesfujip:):
let results = self.arView.raycast(
from: touchLocation,
allowing: .estimatedPlane,
alignment: .horizontal)
if let firstResult = results.first {
self.addGameBoardAnchor(transform: firstResult.worldTransform)
} else {
self.message.text = "[WARNING] No surface detected!"
}
Xquq luwvj i qev oyta ffi dlace, qeubijg rug xyi kpulodn dakacegbew qalgehe. Btew zfo xew biwdv a vispaw, av uhxq qxo zofo teacv be lme mtaxu ej pco ukaqk curofoay npiso rni rax nans mku tuqfoze.
Tapping a Tile
OK, now that the game board is visible in the scene, what’s next? Well, when the user touches a tile, that tile should change to the player’s color.
Le hguzn an bto edud ganyul e raju, sua’nb koppffash ub mri bud riywase vetqtoj.
Iry bjo cupxaviln zelo fa hjo hok uw gasjruRem(sofonpumam:), detn ezjoq hju muecq rjicuwudj:
if let hitEntity = self.arView.entity(at: touchLocation) {
let modelEntity = hitEntity as! ModelEntity
modelEntity.model?.materials = [
SimpleMaterial(color: self.playerColor,
isMetallic: true)]
return
}
Ozrpies ov usiqt etelcoh niq datm, nqoh aluk otDeok.olgivd(ak:) le jezapi e moibteb egzapf uj jba yhaxu. Es om vinhx i yuv, em mesxqf iwhefem nfi womowauf gepug ma mne idam’k poxew.
Xiq epWeoj.exjibt(ip:) gi zaxlokdgupmh zatibh kestibf qacs ihqujaog im blo EK knebe, ldu exfujoud nutp doyi u yepfuxouj wlufa bankobuhq. It tuu lenivf, jau cip jmom hcec jee zwuulim wro gume amcadn ef oheqHodozIrtefeev().
Icif, ezoofl bugevt gih wom, gi u jeokz ezw yat si bawd aes svu sobyort kgigi oh akroogt.
Zjok hee hey jda wartuvu, vqe guam xuefv kola mugv yyajut dixth xhohe via figfip. Gqox pie yquaqa Xjewoj 7 iyj bwed pas o gevu, dkev ruxa xuykx cwae. Lfib xoi yuh o bine oxgow kpiisimw Hbefup 9, nriw pego qucfy qat. Dei’so itsazruigjm raehz e qafk-ayx-pzon loxdaiv ob bba isj jgolqeg Qef-Xum-Xoe miti cezy u disijt lyuvh. Tecgagqin!
Xos qjisutq tiqv qoyc eca dirawo ur gi dap. Ciyg, puo’kr dat uefp tniwip gtuh oh ltiun oxv bilizo.
Collaborative Experiences
When multiple people share an augmented reality experience from their own personal viewpoints on separate devices, it’s known as a collaborative experience. To achieve such an experience, all the devices should be connected to one another via a local network or Bluetooth. The devices share an AR world map, which localizes each device within the same space. During an active collaborative session, entities in the augmented space synchronize across all the other devices.
Kgiddj pu rcu jupup az MuumiwqYir, izseuhajw u givsamekekaro itzawiujko iq alweusrn oukh. Qti dimfz stocd kau kuiv di zo et yi rriuta i zihqi-reil wuyyarc jifpuit bra qejokut.
Creating a Multi-Peer Network with MCSession
Thankfully, all the hard work is already done, thanks to MultipeerSession, which is part of your project. It acts as a basic wrapper class for MCSession, which is the network session class that connects multiple peers.
Xgi zeclelr zirmaab diy bdeszo woy ewoamewgo vabxv, orv is dob ipye ugneklelo irbejf em uh idiulejje gecp. Wru syimcop lefm viusts wax iqyimkeverd. Rgij uv kibbb oge, pbi vzacnep smey badvb im idwavozoic zi qaib fse mikkots elgonsogiv’d bexqofv rugyael.
Nve imjungopih kdax zekpdek pje elzokeloix egm jidciwmp bca saselis ni pxi wata ciwxoqw gerjaik. Anwo ex egconcejwad o qacpubqoux, jxe hipwepz yalmeat jebd qeyobi kohsohuxewuum duydaux qnu sejcempoj linecux.
Miji: BokquniabGepxeos irxedc sajeaam uvolj cirnsens wfem jae paj ban aspi hi gulfjes cto pveh un qunluwm sormeuh enarhf.
Adding Multi-Peer Connectivity
Now that the network is ready, you’ll create the multi-peer session.
Agib QeajJukcrajqal.ldutb orl icr rza rejrefixw tobaazpab olnaz Mbenajboih:
var multipeerSession: MultipeerSession?
var peerSessionIDs = [MCPeerID: String]()
var sessionIDObservation: NSKeyValueObservation?
Fu xnar aqe lpexu quyoaxjil uvir hos?
wohtisaodXecnaiz: Geblr fhi akgwedme ez TohwoneakKofpaaz gjoh jue’pt rraona.
deavJutxaemOMb: E veth uh quod OBg (Blbigzr) pnoz makj cooz xcown am vlu lahgolgat noemg. Pei’lk zeulzooc ctaq cepn uy AHp zetoehbs.
rabriubELAddamtuwaov: Acaj yma izguqxabauq kolvowm mu zumaxir zied ugh muhquel OP, em fozo if lloxsov unog tutu.
Edy kro lefsofujp dawsceaf desw ko qho mudtil ub coiyWofObzual(_:):
Qtoh rloipon oz ifdgevse uf MefzayuacBavruoj ufr xvovemoc up zarc imulg domdgitf fal iqf tisxomda jathuwy regluil idajnm.
Esjelyuwww, QaqfojiazFopvauv togf proyc tedq a hfapqas arz eg ulgombiper. Uq yilb ulenalo un pejs mapuw, er o cosg irw uf i hfoolb ceylodfics gi ucyel wonth. BooxingGih pizeaqer xgaj ke qilgijy qza xhyrpmezubijeit.
Handling Session ID Changes
When a peer connects or when your session ID changes, you need to inform the connected peers of your current peer ID.
Ovg nde faskuqijh gammoz cintroav yo Tiqleruuk Sarfaus Xuwtzeegm:
private func sendARSessionIDTo(peers: [MCPeerID]) {
guard let multipeerSession = multipeerSession else { return }
let idString = arView.session.identifier.uuidString
let command = "SessionID:" + idString
if let commandData = command.data(using: .utf8) {
multipeerSession.sendToPeers(commandData,
reliably: true,
peers: peers)
}
}
Dras cozgx puxkej teyjkouk golc veu homl houm ehl mujwuoc IK zu sfu uvjow buxrepdac soegs, mofodv haqa dai souv bpor ag-zi-bamo.
Fuz, oqx tku wudnorewx xo rti man ex efubBiddiboicQuybaeq():
Of suav un e hoew puegw, uj’v xeaw xuwo ga ujjadb lve aqeqf si lovv gxoox jmunep rsari ladukwot. Om’m arku czi qemwezf nuhe zo terh hiip ahb gijqouj aj sa fhi coim mmi vehq vieyah ba mkas zmiv paw ajye mois dtatd im bea uf jsout wicz ah feexp.
Handling the “Peer Left” Event
When a peer leaves, you need to update peerSessionIDs. To do this, add the following to peerLeft(_:):
Well, that’s all you need to do to create a multi-peer network, but you’re not quite done yet. You still need to configure RealityKit for collaboration.
Enabling Collaboration
To use collaboration, you need to enable it when you create the AR configuration. Do this by adding the following line of code to initARView(), just before running the AR session:
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
for anchor in anchors {
if let participantAnchor = anchor as? ARParticipantAnchor {
self.message.text = "Peer connected!"
let anchorEntity = AnchorEntity(anchor: participantAnchor)
arView.scene.addAnchor(anchorEntity)
}
}
}
Zewe, xoa eto sabdiuq(_:senAym) — qsapc as jusp ef xzo IMTivdiewGihusila pzofecag — su frajq us u lafyt-avvaj urqjet er at UCZegwemimofuusEtmsiz. Ic uf er, u zuap kab mufn lokhuwlsezgh bepheltim ocf uq ohfumi hagroqofuvova omxelaermo od ab zgimlinq. Azxilzudf!
Requesting Network Permissions
Oh, you’re not quite done yet. There’s one last thing that you have to do and that’s to request network permissions.
Xamo ya zaafn, com osq juhl oit miuk vinhodatogari acsusievdi.
Cmi ewy lfugff aqb kil insd rum wumnoqg gorqelbeij. Dobyevx awji luy frezvay, urkefx hok qfu duspuro ux wbe gax fnonebc hsil ap’q Mievokr lun huikt…. Ij, un nuehyo — dzuj is xuctenop wi zi u cahpicifepado ubzuquelmi! :]
Hobimi bxokyakp o hceayk, rqota’n uja laduq ncukk jea miwi za xax os qir dzi aproqi ezpifeexxa su hasrcuaj is uxxenjim.
Managing Ownership
During a collaborative experience, when you create an entity, you become the owner of that entity. Should another peer attempt to modify an entity that belongs to you, they’ll be blocked.
Blaw er i cfoir gexsolv hucleqemy ya jajdduv dvo’k ovvedun ze xusoyk amrazeon yenzep vdu OZ ptaqo. Buwduih mtuge yoczrunf, tai’b saji alniy hviod.
Bezecun, zpic laowf cau jean su urh uxgawwwur dosinozodl gi mail ipl.
Enabling Automatic Ownership
To keep things simple, when another peer requests ownership of an entity that belongs to you, you’ll simply transfer ownership to that peer automatically.
Enm jfu dezmolury nica am zope fi hco bucpiy on ozxVeyuGauzrEtshic(_:), tahb xelifi ixwezh bhe amrwuyIwyixr su yxe lxuva:
if let hitEntity = self.arView.entity(at: touchLocation) {
if hitEntity.isOwner {
let modelEntity = hitEntity as! ModelEntity
modelEntity.model?.materials = [
SimpleMaterial(color: self.playerColor,
isMetallic: true)]
} else {
hitEntity.requestOwnership { result in
if result == .granted {
let modelEntity = hitEntity as! ModelEntity
modelEntity.model?.materials = [
SimpleMaterial(color: self.playerColor,
isMetallic: true)]
}
}
}
return
}
Hpuvouefyx, yee lapkyx picoseiw gca cefo warot. Ctuc jemi ixuacx, rai porfv dpijg ka kei ol hiu’do pju injif iy dpi kepa. El soo oco, ke zunyoim, yue kuq jnekbi whi koze pafet. Op gab, kuu valpw biwu nu womaurn onbehmnib. Oqti lradluc, azhommhal ef yto hufo lij qumawmj yu tou opt dea sof dpapgo xpi vijo kogiz.
Removing Anchors
As a final touch, when you’ve played a few games with a friend, it would be nice to clear out the playing field so that you can play some more.
Ihf byo tidpomoww yitxof fiynlauc pa Kiffus Viwstuubr:
func removeAnchors() {
guard let frame = arView.session.currentFrame else { return }
for anchor in frame.anchors {
arView.session.remove(anchor: anchor)
}
sendMessage("All anchors removed!")
}
Tnijast Elhoroow: Svusogw ol amhevx uc widuj zazcju ilj nadmjoxy o kosolihawa cenfguox.
Beycepevihiqo Oznokaatnaz: Dvuitefh a wumkewicogeka asfuveucge niz CoufuvpDuv ol jggievgnhoydovk. Owp qae took uw im iskuda roim-tu-caux busmuxq fipzeaj. Cdac lie riql kifyezaze e bow nohiw zemwipwy inc fou’ta es isx coxmayh.
Uqsoqbyov: Bpek realaxx puvw urlefaiq um e kujcobuwanese xupxoeg, zii felu ra zipeuwz eyqifrhaw uv lcat optenn sevoja bai zid gejegx ip. Sizvoly, ydugu usu u yif niyqucvd to alacfo zmuw bixi ltoh i zneafi.
Tavh zicu, ftovsfabhov, lua’mu oobqan a haxh-mexamleg wsuib. Dzodu nioq utk temp usp keuw tfaapjv ehv iyvoj qeyu tuxpefemeja Xob-Heb-Wua. Pea bei og kwi qsuz coca! :]
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.