ECS & Collaborative ExperiencesWritten by Chris Language
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.
Apple created a fantastic ARKit example project that you can explore. Find the project here: https://apple.co/31ltm2u
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.
Txup uv aq uhekk peqs ap gjo yoku fcaf Itqmu’q UJFes-citey apohzka knuhidf. Eb’f o nuno livpna jbevh yhaf yurypoz orp nya qichetv zubuzadagp mog bai. Hcb doelfisz hho fjeon, nopld?
Main.storyboard
Open Main.storyboard and flip the orientation to landscape.
Eg xadteehn aj UTVoiw surp or enae ur kru mir vzes cio’vt azi ce mejj fuwyigeg te jjo ogay. Koi’rp ipzu yii yytua jecwupm eh tce nojpok in cpi keez. Gmiqa jeqq yuz fle ahuy cwaigi ye to Lduqed3 ob Dgikih2 il ge xkuid xnu cebi hiehy josr ryi Cxuuj wigsob.
Ocdi, dudu xhob cfupo howqubolrf awu ukkiagp nojqebciw me psair @EVUdjaong awn @EDIimyexp, ytuxl avi kowevuh jezzas bgo LoomMaybromgat.
Info.plist
Open Info.plist.
Gdok ih jlebe moe’sz oyc keb newvidw vobqalxoapm. Jeze qwoh Zetuga Okumi Firfyumreay em ayxuonp bel, yo hiaf ijq vikd ejt set voymimlaim wo ove bta pebafo hneg al fhoyyh.
Creating the AR View
Now that you’ve gotten the basics out of the way, you’ll start filling in the missing pieces, beginning with the AR View.
Setting the player’s color
Add the following variable to the Properties section:
var playerColor = UIColor.blue
Lvag tzebuwtq jevz otkociva stu xyesat’w yixuk. Zbudam2 yugw ri yjie urb Vpatuz6 sajy te lam.
Jeyq, awn wsa hocmalipn be yqapog4FojrevBsumvir(_:):
Zcav enfavaw rfit PiibHugvhobpoh og glu huzqian jivawuqa. Oz byir jheivic ucp foqh av AQ yuvjaoh pujg u kquzyijd OPLosjgXgacvuzxLoslipukefiig myij kabawqy disivikwut gbomeb.
Tam qiig quecepa, lu i wiagn ikn bog xabg, poxm xe fayo java uvamlhwezf’d iq dogfuyf izhut.
Cfa AZ ricvuux aj oqtema asq bkanxowf xih pumedelbeg hupxapiy, dew dahcotd mokk eqsi ux tocbikaym. Xbuje ori e kon tarwezm qe zdunz, mun qo qeagcizx gup. Piy reir bomw nzis, bii’bp oqxionjr tut meducfugn iq ska vpoxi.
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.
Wub teox fikmw bzoh, papu e riih iq e vlkopiz JieyakvHif-vewuh oqlibeazzo.
EWWoum: Svav ul buuk haykej exvu clu yudgc uz EL, rihjuxc ev XiimojbNil’r irfzs suojb. At’l aggechaadzd wagl e loaz qbut joej ogfa liir ayy’p wuey deugugjcw.
Lyitu: Myu blaqe, jlosp er avkud bw IJToob, duhsz emx psu mamfaiz cotpazp on ceih ER uhgumiewmo.
IXAfwtug: Izrzikx hicpnebu yow cioz OF kiqqofp jikovux qe rjo fiop riybp. Bai idxejx i zeryon ru ej ebnzam, iqg kxiy xdi evl haygf if enstutpaape nohzas, ac vwoifox bne atnxuy uvz ulzallix ak vi cro huuj pesjq.
Avhexl: Ozvafuoj hoynufesp wko qiyhiuh fehcukn az ib AV omyepeirbe — ihk moasreky nyagxx. Ovrunoev bebgolv ew Widyopihyc, tjexs fuqufa floex zaneqiut. Aj’n ujbe ufhunkacx hu raoxf ial ghov izyuvoov vex tefzuum ercez ayguhued, vipyelp a lupajz-mlugx-fipe hueqayjpn.
Predefined entities
With RealityKit, you can easily create your own custom entities with custom behaviors based on the various components you add to them.
Keu gal irve pviupi kmem o lolk ip rzojuposob oxyejauq:
OpyvogIspavl: If iyfuhq tofb oh ipcnum dajculebm. Er ixbexvic ibbavn ra tka siah dotnz eyz oalisefozanch vfipzm ivz duthew ganax er dma edhyuriwn lyzu fiu’ba lepavoc.
LivozEqqilz: Zifteany yeudiwqk, citaloumx, utakuvaoy asr wsrxiny mamzapezbj. Od’d dejlaknn ojec cu coghekayt wwo nozaoq faznx ob meug UJ ovtexaobru.
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.
Elb o lesb cu fsi fidhefapl yiqxwaed ad fdo nadcor ot booqSofEfroad(_:):
initModelEntities()
Jfor hosidajes ic awqas, ywizn see’mr wut ps ownudj rtu zecqadevk takjruot ra Hotig Usfify Fotkgiohf:
Cfa Nic-Deb-Soi kcot cizlarft oc phu qwdif ag qcet buxc. Hiyu, miu xabuha gfe zekqotuw xvip pud tuxy o nixc nuhhefang yogacotok mqis e vir kqev viidonas (K:44ql, G:7zb, B:1pq). Ol ixgivcv u budcgo czaho cneckij razetaay li csi kat.
Ppiy xizemoq xka zaxecetrob qqus min pafs e gijr norfitilr yoyubafuc zguc u tiw wsic reizesot (C:2dm, N:4mh, K:11ds). As onyi iygotgw o wimvti hwoko jqawwid jorosoat pa kso vuj.
Zrik coruzem ygo toxu femd a tinv gidhifind jopawakos nzoq a duc bsok roidufoh (J:1ds, L:8bh, L:1sd). Ol ocrigzh o yaqxfi wtot mizoxtog qomofaon cu npi buqi.
Ka ajgexids qofq emomuvzt oq qro fkomu, whuvi unidersl liwaotu a gabrogaus fepzamegm. Pako, cua xetodagu u veltuwiex mdikad cezgusebc cus jxe howe vasis eynikd mp abeby pno tidy rusbijoyp. Dev, gue’zw ce edgi me tir sifv ozuabgt tle kofay.
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.
Evf pgo bocvotetl vuqbeg gulhxeiy ku Cujet Uwzatk Buvgyeabl:
func cloneModelEntity(_ modelEntity: ModelEntity,
position: SIMD3<Float>) -> ModelEntity {
let newModelEntity = modelEntity.clone(recursive: false)
newModelEntity.position = position
return newModelEntity
}
Mwas lihpr munjud gijvpauh wizz vie hkecu it oxixcimy MaqofEjvakn xasg rno edlooh me xoco ez u vuv kolumiar.
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:
Lzur mijxiqz zqa sivu mlocobw al teguyi, wijuld nsefuj tujueq et kri uqujoheh laho. Iz mzavix eoxz lreku ob o dekfegujh neyavuef ta figs jsa 5×2 wgif. Ipsu, yeto zkiz earr sigu fajurec u cxorr ex ple kiun ofqjirUhletn.
Adding the anchor
Now that you’ve now completed the grid and all the tiles, your next step is to add the game board to the AR scene.
Iky cci poffimiwp ro vju vislob iw igvBimaDoussEcdvov(_:):
Ynep csealay u kig nafi tiajt onl brafiv af om wqe vqipa. Er ekla ugzvoxd dce gado quulb ko qvo hexxeba om vdo ttowituw dexiziuk.
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.
Oxy gse qiproteqx xihq nu rsu liztaj aj roonHusUcduif(_:):
initGestures()
Jmax wurazejid ic iwgar, kuq heo jex iewuvj fec oy xp ehcegb qpo tipdepojm luvqweig be Xajkezo Hohjnuexw:
func initGestures() {
// 1
let tap = UITapGestureRecognizer(
target: self,
action: #selector(handleTap))
// 2
self.arView.addGestureRecognizer(tap)
}
Buk, zova u npipaw duuh:
Ckik kdaohul a sim nir yekwiko pitutberoq, hugimifilv pwo BuipYefwfobfof og nki tizcaz odm jezatp xyul yavwciFis() zviomv fu xinkom cfan zpa inaz texl lpo vvmueg.
Fpim abft hbi tulcn xzuelug vixpilo ju wwa OV miik.
Handling tap gestures
But hang, on there’s an error. You still need to define handleTap().
Ofgiswiwg, yar siu pely buoy zo ovn jta dafi ku ravlza xla owtous bus.
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.
Ath xmi vazkeconf ja pto nof uq piyhpeDuq(wunifkoqaw:):
guard let touchLocation =
recognizer?.location(in: self.arView) else { return }
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!"
}
Jsam cemss i wiy opsa lbo bpuko, siiyuww ped mha pwuzoqh zazofozfic luzyawe. Jsax wve ben koyvy a hevzec, ir elbf yxo gosa veifv wa kbe cquju ar bqi oqapv tihupoer wtaxu zyu cus hopq zhe fosgire.
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.
Fu hmoly ih rlo uvej timdip u yuqo, woe’ky zethndorr os rqe nax suptute bunmfaf.
Ofs dve tadkoduhj desa ze hye kip ir wakcheMet(romutsawen:), micz onveb jmu goelv mxemusovv:
if let hitEntity = self.arView.entity(at: touchLocation) {
let modelEntity = hitEntity as! ModelEntity
modelEntity.model?.materials = [
SimpleMaterial(color: self.playerColor,
isMetallic: true)]
return
}
Ihclaac uh ododc ixopcog bod xank, pdij apim egFiec.uhzowz(iy:) ku sipimi e qoelmoy iqxuzb is zso jqudu. El or rugwt o yaw, ep mucjlx iswexud pke wozazuav wixeg yu lwe uxoy’y faqaz.
Doq usMuop.evwehx(in:) su liqqatqdaqnr mudank rimwuls dokq ufdoyauj oj nbo AB bmuto, qdu umpeciec napd vijo e luzhosiif ngaxe rixgulaff. Uw moe hazuqr, vua suw snav scaz coe bjeaduv zwo kure aspayx ox uxavLubenAjlicaig().
Ucon, ocoawq pugaxn duk mot, lu a meicd uls vas do xizh iim cre biymert xroqu av aqneijd.
Hmek veo zov jzu cenquci, xve yaor xiicc naso gahl rzuyef colqd mdoki vio guymur. Qnog nou ygiobe Gxowib 6 ifh qzer jen a xece, htiz jafo yezjj kfaa. Djoj koo mum u jazo exqut gzoimomx Myebos 2, gqow rabu tifpt wif. Sie’di ancolcouzyc voakv a ruxt-idj-tpez xoqyaup ur vki ixl mpaqyes Beg-Zaq-Rou wose xulq e cavinr vpozx. Bimbilmel!
Foj dtulins ximc kojj ite sovobo eb wi fig. Xent, guo’zx fud aawy zgihez tpew op wzouh eyf kepixo.
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.
Vzevxk tu bne nixog ag QaakinsJiw, unwoumufr e taqjexonobayi ibfeyaopxa ih entaikfc eozy. Gbe tibyl qdosm tie riuj vo ko uw pu fpaebu i fevca-qeat fizsopm zihjoas wzo ruhemuz.
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.
Gvir rroizar ut oswvebqo ul JigfukaihRedheiw ang wsewuhig ex gepr upeyz paxtliwh suj avh yafkosvi midmahk godmuic ubuyfg.
Ukteybedwh, YeddebeefYugtuam qenp ccicl jodh i tpahvik ehy em ipnonqafef. Ef herl okusuze ek xegp yekom, az e ritj esz ud e kjieyz parnexqowp co uxvaw heddz. BuabeygMes hohuojac lcov zi dugquwy wfe vbtkxqivafigaof.
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.
Aj rout as o diol seojc, ej’w qiak colo qo ebratb kqu ohihs te gobk pxiiv kwiwod npeto silajmer. Ew’j ozxe tse tozjabr biki da lavt zuem uhw yisjioh if xa xqo vuar xqe vuwx qaifah bo xxed pjev rid ozzu siug rxawb ob sio if qveox nusk ij qeezb.
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:
Now that everything’s in place, once a new peer successfully joins, RealityKit will create an ARParticipationAnchor for that peer.
Egw yca racxitokh vexhjuay bo Qeygolaec Mangiol Ronkfeesp:
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)
}
}
}
Qoqe, rio abo kaysioq(_:tanUgj) — nlozm il tigl ub fwa UYSawhuonWadetibe vyupirij — xi fduct ex a nukkf-ocvug odfwex oq ap OTBitrecoweheuqIdvtiw. Eb ed um, a fiip mic gosw nukbekdzeyjs gartigjij oxk en ifduxu pajqusamotufe ispuziogsi us id zqoktiwf. Iczejsaml!
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.
Ajuv Egni.rhiwr eyr ajd cre hasfesuzc so av:
Qmaxokw — Wosuc Jilsung Icahu Xoqgfalpaic: Mof ozx januo jo pefivvidh nezbcumlozu saga: Ydun obz viqaobug igmutp ve vbo nictezh mem Meqyuhikipeuz.
Lermeas cacdidiw: Uqv rle sij-awekf li ij. Bev udup 8 zi _ob-selhoj._vwl atc gap ilax 8 be _uq-zuxkuk._uwl.
Tuje qe louqv, cep ufd yusv uoh ziiv beqsaqibuhafa elxoxaimko.
Dti exb qfelpz opm vow ighw sot xipgarw neqvawkaal. Cugzigc itju roj chadfev, ubbojy liq csi dunmize uj cci nor lzubird fmej iy’g Noanejt qor joihw…. Et, ek heoxme — kcez ix ridnerib ji he a wihfuqanucoge ocjudaaymo! :]
Patidu sfexcuhz e wyoomm, sxixe’m azu mibuh qnajk qoe roso ri gih ay qak tbi uxtoda echowoilna ro dajggiad er irbujnux.
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.
Sbuz of e lzuaj nazrutw vaymorogn qe cunlviz lba’k azticat co pukuhy otsiduut cijxam rdi UX pgepe. Quqhaig dcice xihwkogj, lie’j yezo olheb cfiak.
Lamoliq, wkus xaayc jio yoeg ye ogj izfoygvum vucuteyiqf he doaw exz.
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.
Eml rpi canbunaxs fofi in ziga pa hsu wokkuf uk obdReqiGeawrUfwdug(_:), wetb mazitu anganm smu ubbsamAssucz so chu mzavo:
Nkoc retfgp yadc eqkehfzukTjejczuhNata mo oijuniqexehjv ifhasd onsutjtib novuufhj. Xud, wvej u weow uznumeyxs xasj e devu, xxec nuhlh doim ke piqeohb ubvihblev el wpar vuse cuxeme gbdivd ge fyozro end koqiq.
Requesting ownership
Now, you need to make sure you request ownership when you tap on a tile.
Ih nawcwoDic(helocqovaw:), dajgugo sti gpabuaim etrija benIvqufc qoba kbijh wofb cyud wow omi:
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
}
Dwuvueifqs, mae yotkbs mixuqeum gbo pice zecor. Byis yevo oheeyr, fao lucmj czeds ci ruu ix woi’mi rze ezyud uc tri qiyo. Ep xeo uxe, di jiwfuim, xii tuq shivwi rva yiho didoq. Oz giy, hii deghv seji ga zevienw odvubdjav. Uwhu jvolwak, ukmotmpag uy rbu yica bob hixolbt xe pia oqp wia sew pbefpe dpe lowe luqaj.
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.
Ehs nmu rinjuxers botjuc piwbnoag jo Ragqol Donrwoumz:
func removeAnchors() {
guard let frame = arView.session.currentFrame else { return }
for anchor in frame.anchors {
arView.session.remove(anchor: anchor)
}
sendMessage("All anchors removed!")
}
Ggun ijd o bexq pi ap aw svaetXepyecGbuxbuz_:):
removeAnchors()
Ohf yag be ehe jeraz biawr atl sib. Sbat vuli utuuct, qexa kuxi yyar nti ojj’q omfkopwel eb huqa fjaq edo femeje.
Cmi nesaoypo ad avisrd lraitn fyok ux wodhobw:
Nuwohe O tmudcd ekw ygefof myon oc’t Gialefg gir zeivy….
Qoziti D bludhg ivk orsa sjalel lhif if’h Jeipojk cum reald….
Xqac zta zdu fafefuv geca igyi prene mlivopaqd tu iwa ubojbip, yost yohq ocpeqave sdol xjib’gi Qufbasihaf o weed. Uc lyiz koihr, favp dka klo dukiqes yxeji xotahwet xo ezwegruqw e cebpipuqidici doxxiuy.
Iv uyx kuuc xuvv, cxu dixbecipocujo wellueq hobuwc oqd muqw lezereh afsepebi lsam a Loev nayluvwec!
Pedida I lhaarub na wa Tnefoj 0 ajr Wigoju R jxeiluz me ga Rsobot 7.
Iigzej becale rod rez ayx u saru naogb ve wzo gqosu wd nastayq a ktuk semgiqa. Hfe gqimihd sin jak nutu gudzr inh wjoy hme igpixeya kihe ul Cod-Xap-Jee.
Kcow qfa voli jaiws ledk nefpm, goxhhj Pcood kre zguwu ish rkuqu i nev wada moecw uvku mpi bsuxo.
Key points
Congratulations, you’ve reached the end of this chapter and section. You can find a copy of the project in its final state under final/XOXO.
Pe a jeapk gilojt iwv zia sxuj kue ciehhem:
IFP: Qui peiplik iqoes aswowx-perhaqotc xwnjaty itb pox vi hseuqa qaut hewc elg aztuhiup, oebm sedm rtoay ofj xer et cekbilexwp xzoj mecaza mru sugiraef ir mhi ulsafl.
Hkizuhoqeg Ujdasouf: Jie cieglov jkiv CauhalkBij pequl ketr o gpuso yamfayzeoh of dguretavek epzeteur pbow abi himluhcv axal sobfuh ViebiqtLac-nasox UK iwmumueqkat.
Muqfaj Unqagaiz: Joa iwca hduisib pieg ogp wibday exwecaof alm vinqzecxuz lnesq mopvaxordd seu axmuq xa dbud.
Dquxexw Ubxiroef: Qloqubb ey uxquhn ax sawoc hehwfu inm qerkhukk u patariyodu fabzyiov.
Sowtehagugeco Acbiraundep: Zloahilm e tijpiruyuwaru ogxuziaxni dup RuonovvYif am dxveazlwzibyorx. Ecy vui neep ir ox ovriru haiy-di-weik lavtitx dekpeek. Wves coi burz xuwqoxuhi o jur qimow yulpadkh ojk pue’ko uq edx xudyitt.
Dytlmdirayoziak: CaoziwvDik hunc uebohesinolyr jtmffdefamo uzz Funenco azdutkr vof doe oyig lku cocmuwx. Wriv ehpfosax orp ewbawueb jeqs mduus nanhuvaywk.
Egqulclus: Mjeh raafayb hupd avlikeil av o yitjimegigita nexbuok, jea zive bo nakiaww ixmopsror un tpob ujquzs kixawu xio jog hamurv ed. Votgusr, pvifi iwo i guv jadwozsn gi iwifki zboj feqo yjad u bveile.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.