[ Music ]
[ Applause ]
I'm Ross. I'm an engineer on the RealityKit team at Apple and welcome to Building Apps with RealityKit.
So this session is intended as a follow up.
So you might want to check out Intro to RealityKit and Reality Composer Session.
Today we're going into the applied usage of RealityKit.
And we'll walk you through building an augmented reality app and how you can leverage many of the frameworks key features.
Before we dive in let's do a quick recap of what RealityKit is.
So RealityKit is Apple's new framework for building AR Apps.
Trying to make it as simple and intuitive as possible for any developer.
It's been built from the ground up to work with AR.
And it allows you to seamlessly blend rendered content with a real world environment.
Its Swift API is simple yet powerful and lets you do a lot with only a few lines of code.
All right, let's build an app.
The app we're going to be building today is called "Memory Cards."
And as the name cleverly implies, it's a card matching game.
To play you tap on a card to select it and its image is revealed.
Select another card to reveal it and if the pair is a match the cards are removed.
If they don't match both cards are hidden and you're free to select another pair.
This simple game will allow us to highlight and discuss a lot of useful RealityKit features.
So we're going to build Memory Cards in four stages.
First we're going to put together a prototype that will place our content in AR and allow for some simple interaction.
Next we'll add some polish with some fancy art assets, improve performance and AR rendering tricks.
Then we'll leverage the entity component system to track custom state information.
And finally we'll show you how to use RealityKit's built in network support by adding multiplayer to our game.
Let's get started with our prototype.
So as mentioned in the intro session there are four objects that you'll need to use in every RealityKit app an ARView, the Scene, Anchors, and Entities.
ARView is your window into the world of AR.
And in your entry point for RealityKit.
It's a view and it goes into your apps View hierarchy.
The scene holds up with a virtual content that makes up your app and is owned by ARView.
In RealityKit, Anchors describe how objects relate to the real world.
And you'll need them to be able to place your virtual content.
To place an anchor you add it to the scene and when the appropriate target is found the anchor is automatically placed in the world.
For Memory Cards we'll use an anchor to place our game board on a horizontal surface.
And entities are used to represent your virtual content and are the building blocks that make up your scene.
In Memory Cards each of the cards will be represented by an entity.
Since your game has 16 cards we'll need 16 entities.
So now that we've covered the important elements that we'll need in our app let's talk about how we'll create our anchor so we can place our virtual game board in the real world.
RealityKit anchoring is built on top of and integrated with ARkit.
But then you leverage it's full feature set.
To define Anchor content you create an anchor entity.
To clear the type of anchoring you'd like to use and then add the anchor entity to your scene.
Once the appropriate target is detected by ARkit, the anchor entity will automatically track it and making your virtual content feel like it's been placed in the real world as your device moves.
Also as mentioned in the intro session, RealityKit supports all of the anchor types available with ARkit, allowing me to anchor things like planes, faces, images, objects, and new for ARkit 3, body anchors.
For Memory Cards we just need a single anchor to place our game board.
We like to place it on a horizontal surface with enough space to fit all the cards, about 20 centimeters squared.
And that anchor will represent our game board in the world and we'll place our content around it.
Let's write some code to do this.
So here we have the view controller for our Memory Cards prototype.
We have an ARView in place in the View hierarchy and we're ready to find our anchor and create out virtual content, which we'll be doing in the viewDidLoad method.
To define our anchor we create an anchor entity and using this convenience initializer we can specify the type of plane we'd like it to anchor to.
In this case the horizontal plane.
And we can optionally pass in the minimum area we require, 20 centimeters squared.
RealityKit's units are in meters so I get 20 centimeters by 20 centimeters, we pass in 0.2 by 0.2.
Then we just add our anchor to the scene and as soon as RealityKit finds a horizontal plane of at least 20 centimeters squared, any virtual content we attach to this anchor will appear in AR.
Now that we have our anchor set up let's add some virtual content.
First we need to load in the model.
RealityKit natively supports assets in USDZ in the new Reality File formats.
Loading can be done synchronously or asynchronously.
For now we'll start with synchronous loading but we'll touch more on this later.
When you load a USDZ or reality file asset, RealityKit automatically imports its entity hierarchy, and meshes used by the asset, its materials, and any animations it may have as well.
Let's load in our basic card assets.
To load a model you just need to go entities load model method with the name of the asset.
There's not need to specify the file extension if the asset is in your app bundle.
If RealityKit is able to load the asset, it will automatically create a ready to use entity for you to use however you want.
Here we're loading on the eight base card models and storing them in an array.
So Memory Card has 16 total cards.
There are eight card types, each with a different image.
And then there are two instances of each type to create our eight matching pairs.
So we've got our eight type models loaded, but how do we get the other eight?
Now we could call entities load model method again, but any setup we perform on our cards will need to be done for each of these as well.
RealityKit provides and easier solution with entity cloning.
To create a clone you call entities clone method.
And cloning creates an identical copy of the original entity.
It references all the same assets used by the original, and cloning can also be done recursively, which will clone all of entities children as well, which really comes in handy when you're composing complex scenes with deep hierarchies.
It's also important to note that clone entities are copies of the originals, not instances.
So if you make a change to the original entity, say by removing a child, that change will not be reflective in any of its all ready existing clones.
Let's use cloning to create our card pairs.
So here we're cloning the two instances of each card type we need and storing them in a separate array, which we'll use to build our game board.
We could use the original card templates and then just clone one new instance, but by keeping the card template separate we can easily refer back to them to recreate the game board later without having to load them all again from scratch.
Now that you have all 16 cards ready to go we need to place them in our game board.
The anchor will be at the center of the play area and we'll arrange the cards in a four-by-four grid around them.
Then all we need to do is add each card to the anchor and they'll be displayed in our ARC.
So to do this we calculate the position of each card using its index and the array and then set it on it's position property.
This will position it relative to its parent, the anchor, when we add each card to it.
And that's all we need to do to get our cards laid out and rendering in AR.
So we got our cards placing in a surface in AR, but we can't do anything with them yet, so let's add some interaction.
We want the cards to flip when we tap on them.
But to be able to do that we need to translate that tap on our devices screen into the world of AR so we can figure out what we're actually tapping on.
And RealityKit provides a solution to this with hit testing.
Hit testing works by turning a 2D point that was tapped on your device's screen into a ray in our virtual scene.
That ray is then cast into the scene and RealityKit finds all of the objects that are intersected by the ray.
Any entities that were intersected by the ray are returned and you now know what objects lie under the tap.
ARView provides the methods for hit testing.
Entity at point returns the entity closest to the camera for the given point and entities at point returns all the entities that are intersected by the ray caster that given point.
And we can easily integrate this into our Memory Cards app.
Here we have a method responding to a tap gesture recognizer.
We get the tap location in the ARView and then pass it into entity at, which we're using because we only want the entity closest to our device.
If there's an entity under that tap location, dis call, we'll return it, and we could then perform our interaction on it.
However, there's still one more thing we need to do for hit testing to work.
For entities to be hit testable they need a collision type shape.
Collision shape is simplified geometry, typically a box.
They're easy to find and they allow for efficient intersection and collision calculations.
And importantly without a collision shape, entities are not hit testable.
So let's add them to our cards.
So this is the same code we saw before for leading our models and creating our card templates.
We'll make a small change here to add entities generate collision shapes method.
This will automatically generate simple box collision shapes for an entity using the entities visual bounds.
As you can see, much like with cloning, this can be performed recursively creating collision shapes for all the children of an entity as well.
And speaking of cloning, collision shapes are included in the data that gets copied when an entity is cloned.
Since we're all ready cloning the card templates to build our game board, they'll be automatically included with these collision shapes as well.
Now that we can figure out what entities we're interacting with, let's add an animation that will play when we tap on a card.
RealityKit supports two kinds of animation.
The first is transform animation.
It let's you animate the position, rotation, and scale of entities in code.
And the second is asset animation, which plays the animations that are baked into your assets that are loaded in from USDZs or reality files.
And RealityKit also provides completion handlers for use with both kinds of animation, letting you know when the animation finishes.
Once our card assets don't have a baked in animation, so to flip them we're going to use a transform animation on our card entities.
Transform animations can make use of a variety of timing functions that can control the speed of which the animation is played.
There's linear, which plays the animation at a constant rate with instantaneous acceleration and deceleration.
There's the ease in, which gradually ramps up the animation speed over its duration.
Ease out is the opposite of ease in, which slows the animation down until its complete.
And the ease in and ease out combines both, ramping the animations to be up until the halfway point and then slowing it down until its complete.
And there's also a cubic Bezier option for customization of your timing function.
Let's create an animation for our card flip.
So we're going to start by copying our card's current transform.
This ensures that we preserve the current scale and translation of the entity, which right here we don't want to change.
Next we'll set the transform to a 180-degree rotation around the x-axis using a quaternion.
This will flip the card so its image is facing up.
Now we can start the animation itself.
Transform animation is applied using entities "move to" method.
RealityKit will smooth the animated entity between its current transform and the one you provide in this method.
Using the requested timing function, in this case "ease in and out" over the given length of time.
This gives us the animation we need to flip the card face up.
And entities move method return in animation with playback controller that allows you to pause, resume, or stop the animation, or to receive notification when the animation completes like we're doing here.
Then to flip the card back to being face down, we just need to make one small tweak setting the rotation of our transform back to zero.
When we call move this transform, the card will flip back to being face down.
So now we can show the app with interaction enabled, using hit testing to detect what objects the user is selecting and transform animation to hide the cards.
Now that we've got the basics working, let's polish things up a bit with some detail work.
We've got our simple card models loaded and placed in AR, but 2D images on cards is not very exciting.
Thankfully our art team has also created a bunch of high quality models, so let's add them into the mix to help our app be a bit more three-dimensional.
We could load these new assets the same way we did before with the synchronous entity.load method.
However, these models are a lot more detailed than the simple card assets.
And the larger assets will take longer to load.
Loading them will be quick, but the app will be blocked while they're loading.
And if you're loading a lot of assets that can add up to noticeable delays where nothing is happening.
Is there anything we can do to make this better?
Thankfully as we mentioned it earlier, RealityKit offers both synchronous and asynchronous loading.
We can load models asynchronously by calling entities load model async method.
With asynchronous loading, assets are loaded in the background and this unlocks the app and allows it to continue uninterrupted, which makes your app responsive and allows ARkit to continue observing the world.
When asynchronous loading is complete you'll receive a call back and then it can use your assets just as you would with synchronous loading.
And additionally you can also combine load requests and execute them simultaneously and receive notification when all assets have finished loading.
So you don't need to load your content piecemeal.
Let's write some code to load our new models asynchronously.
So we'll start by loading a single model asynchronously.
Entities load model async method takes on the name of the asset just like its synchronous counterpart.
It returns a load request to receive notification when the model has finished loading and can be used.
We call the sync method with a closure that will be executed when the asset is ready.
The load request uses API introduced with a new Swift framework Combine.
So I recommend checking out the session Introducing Combine and Advances in Foundation.
And that's all you need to do to be able to load your content asynchronously.
No you could load all of your assets individually like this, but you can also combine multiple load requests into one.
We can do this by simply appending another load request on to the first one.
We then call collect to combine our to load requests.
And then we call sink.
And our closure will be executed when both models have finished loading, packaged into the models of array parameter for easy processing.
And we can extend this as far as we want.
Here we're collecting all eight of our detailed card models into a single load request.
Our sink closure will be executed when all eight have finished loading.
Combine and load requests makes managing your content much easier.
Let's show how synchronous loading compares with asynchronous loading.
On the left our app is using synchronous loading and on the right asynchronous.
As soon as the synchronous app starts loading everything comes to a stop as the app is blocked until loading is complete.
By contrast the asynchronous app continues to be responsive allowing it to respond to user input, update the camera, and you continue to observe the world in front of the camera.
Loading takes about the same amount of time for both, however the device using async loading is able to place its content first because its able to continue observing the world during loading.
While the device using asynchronous loading is not making it a much better fit for use with AR.
So now we have out detailed models loading in without freezing the app.
However an astute observer may notice that we can see the new models while the cards are faced down.
We could simply hide the models while the cards are face down, but nothing would stop the user from bending over and looking at the underside of the cards and seeing which ones match up without having to flip them.
So RealityKit provides a super useful way to fix this, occlusion materials.
Occlusion materials are invisible, but when applied to geometry in a scene they hide virtual content behind them revealing the video pass through.
Here you can see we've added some occlusion geometry to our scene, along with some additional highlighting to show the shape of an otherwise invisible object.
As in animates up and down parts of the robot it intersects are hidden and lets the real world behind it show through.
Occlusion materials are great for simulating real world objects that would block virtual objects from being seen like a table or wall.
So let's make use of it in our app.
Let's start by adding an occlusion plane under our game board.
We'll create a plane mesh half a meter wide and half a meter deep.
We'll then create our occlusion material and then create a model entity using it and our plane mesh.
We'll position it slightly below the game board so that it doesn't intersect the bottom of our models.
And then we just add it to our anchor to place the occlusion plane in our scene.
Here we can see the occlusion plane in action.
At first glance this seems to do a great job of solving the problem, however when we start to move the device down we can see the edge of the occlusion plane and see our virtual content rendering inside the table.
So this plane works great when we're above the game board but we need occlusion that will work for an angle in this case.
Solution is using occlusion box instead of an occlusion plane.
So we'll generate a box, make use of the same occlusion material, and then create a model entity using the box in place of the plane.
Generated geometric objects have their origins in the center so we need to bump it down by half a size and then just a little bit more so that the top of the box will rest just below the bottom of the game board.
And then once again we just add it to our anchor to place the occlusion box in our scene.
Initially it looks the same as the occlusion plane until we start to move our device down and we can see, or rather can't see our virtual content.
Our occlusion box is preventing content from rendering inside the table simulating the real world object in our virtual scene.
So now we have an app where we can place our virtual cards in the real world and interact with them.
We're loading assets asynchronously and we're simulating our virtual content being hidden by real world objects using occlusion geometry.
Now I'd like to invite my colleague Courtland up to talk about tracking game state.
[ Applause ]
Thank you Ross.
Hello. I'm Courtland, an engineer on the RealityKit team at Apple.
We've just seen how to prototype a functioning AR game, add interaction, and integrate final artwork.
Now I'd like to show you how to best track your state with custom components and entities and finally how to add mutli-player.
Let's start with tracking state.
As we covered in the intro session, RealityKit uses the entity component design pattern to build objects within the virtual world.
An entity itself is comprised of pieces called "components."
These components define specific behaviors and data that can be added to individual entities.
Using entities and components allows for reuse of code and is flexible to use.
Let's take a look at how we can apply components.
We're using RealityKit's model entity to represent our cards.
It provides us with a set of components, which are useful for representing most common virtual objects.
We've made use of the model component for visual appearance and the collision component for hit testing.
Model entity also contains a physics component, allowing the entity to move and interact with other objects in a physically realistic way, though we haven't made use of it here.
RealityKit enable you to customize an entity.
By using an entity component design you can include the behaviors you want, exclude the ones you don't need, and add new behaviors of your own.
Let's customize this entity for our cards.
Model entity has most of what we need.
We'll remove physics since we're not using it.
We want to store whether the card is hidden or revealed and the kind of card which we'll use to determine if two cards are matching.
To do this we need to create a new card component type with these properties, which we'll add to the entity.
So what exactly is a component?
A RealityKit component is a Swift struct that contains your properties.
It conforms to the component protocol, which allows us to attach it to an entity.
It's also a good idea to conform to Codable, as we'll see in the multi-player segment.
So let's make one.
We start by declaring our struct called CardComponent.
We adopt the component in Codable protocols.
Next we'll add our two properties a boolean called "revealed" to represent whether the card's contents are hidden or revealed and a string "kind," which we can use to match the two cards.
That's all for the type.
We'll start with one of the card models Ross loaded earlier.
For demonstration purposes I'll show how to remove a physics body component.
The model entity provides a physics body property, which makes this easy.
We just assign nil and we're done.
Now we'll load the card component by assigning to the components array indexed by the type of component.
This will add the component, if it doesn't all ready exist, on the entity.
Changing the kind property is similar.
Indexed into the entities components array, and assigned to the kind property.
Since not every entity contains a card component, the accesser returns an optional value.
For common configuration of components used throughout your game, we can take it a step further and create our own custom entity.
RealityKit comes with a number of entity configurations such as directional light and model entity.
And you can make your own very easily.
We're using cards everywhere in our game, so this makes an ideal candidate to turn into an entity.
We'll get compile times, static typing and code completion for these objects.
This is also where we can add methods to encapsulate functionality.
This is especially useful when you're changing multiple components at the same time such as setting the state of the card to revealed at the same time as flipping it over.
Creating new entity is just a few steps.
First we needed a new class to represent our entity.
Then we added any RealityKit components we need.
Next we'll add any custom components.
We solved the physics body property on model entity earlier, which provide a convenient syntax for accessing the physical body component.
We'll do the same for our card.
Last we extend it with methods reveal and hide.
So we create our card entity class derive from entity.
Next include the RealityKit components.
We're adding the HasModel and HasCollision protocols.
These protocols give us access to the model and collision components via properties and any methods that they provide such as generate collision shapes.
Last we add a card property, which returns a CardComponent.
Since all cards need a card component we'll make it non-optional.
The getter will retrieve our CardComponent from the components array.
We use the nil coalescing operator to return a default value if we didn't all ready have a card.
The setter copies new value into the components array.
Now that we have our class we extend it with methods.
I've included the reveal method to show us how easy it is to coordinate multiple updates.
We first update the card's state to indicate that it's revealed.
We'll do this immediately rather than at the end of the animation in case you tap it again while it's still animating.
Next we use the same code that Ross showed us earlier to animate the card flipping over.
And that's it.
Hide method would do the opposite.
Set reveal to false and turn the card to be facing down.
Let's see what this is like to use now.
We'll go back to the onTap handler.
We call the same ARView entity at method, but this time cast its result to a card entity.
Though we only have cards in our game, your app might have other types of entities.
Casting to card entity lets us perform actions specific to the card.
With a new card entity and component, we can just ask the card if it's revealed.
If it is currently revealed we'll hide it.
Otherwise we'll reveal it.
With hide and reveal encapsulated as methods on the entity, we don't need to be concerned with the specifics of hiding and revealing cards here.
We've cleaned things up considerably by adding a custom component in entity and made one important functional change here.
We now know that a card is revealed and can tap on it again to hide it in case it wasn't the card you remembered.
Now that we've modeled our state as components and entities, it's time to take the game to the next level.
What's something easy we can add?
Just add multiplayer right?
AR games are fun and playing with friends makes them even more fun.
Multiplayer can turn a simple card matching game into a real competition, however making a multiplayer AR game comes with a few additional challenges that we don't encounter with non-AR games.
When they place virtual objects in the real world, we want them to be in the same location for everyone playing the game.
And because everyone is in the same place, we want updates between devices to be fast, to maintain the shared illusion of reality.
To enable this we built RealityKit from the ground up to support multiplayer AR.
Combined with ARKit's Collaborative Session, also introduced this year, this gives you the tools you need to add multi users support to your AR experiences.
So let's take a look at what multiplayer means for our game.
Both players will play simultaneously and can turn over cards at any time.
Everyone can see the reveal of cards.
So you can gain an advantage if you're paying attention to what the other player is doing.
We'll also add a small white circle to indicate to the player which card they turned over.
Let's take a look at multiplayer in RealityKit.
RealityKit provides automatic scene synchronization.
Changes made automatically update on all devices.
Leverages, device discovery, and connection for multi peer connectivity.
This makes it simple to find and connect to nearby devices without maintaining servers or even connecting to the same Wi-Fi network.
It provides an easy to use ownership model allowing you to control which peers are allowed to change which entities.
And provides low latency even on busy Wi-Fi networks, which is essential for convincing AR experiences.
Let's go through the steps for adopting multiplayer for our game.
First we need to designate a host.
We'll place the game board.
We'll ask the user to select this at the game's main menu.
Then we connect the sessions so that they can communicate with each other.
We'll also enable ARkit's Collaborative Session, which enables our peers to create a shared map of the environment.
And we'll create a synchronized anchor, which attaches our game board to a specific world location and coordinated between peers.
And last, we use ownership to make changes to the game board such as flipping over the cards and removing them.
We've asked the user to choose "host" or "join" from the main menu.
Now we need to establish a connection.
We use the multi peer connectivity framework to get connected.
We won't go into all of the detail of multi peer connectivity here, but if you want to know more check out the 2014 session, Cross Platform Nearby Networking.
First create an MCPeerID and MCSession.
Be sure to enable encryption as it's required for use with RealityKit.
We'll check the user's role before proceeding.
If they choose host we'll advertise the session using MCNearbyServiceAdvertiser.
This broadcast that we have an available session.
The client creates an MCNearbyServiceBrowser to start looking for sessions.
And now that we have an MCSession we need to instruct RealityKit to use it for synchronization.
We do this by creating a multi peer connectivity service, which is a RealityKit class that wraps the MCSession and makes it usable for scene synchronization.
We'll create this and assign it to the synchronization service property on the scene.
Now we have two devices connected and synching their scene.
However, they don't yet know where they are in the physical world relative to each other.
Let's take advantage of the collaborative session introduced in ARKit 3, which is natively supported by RealityKit.
Collaborative session lets us build the world more quickly and lets multiple users share the same in world experience.
Since we want collaborative mapping we need to turn it on.
We'll do this at the end of viewDidLoad.
We create a new world tracking configuration.
Set is collaboration enabled to true, instruct AR Session to run our configuration.
Now that we have collaborative session enabled we can create a synchronized anchor.
Previously we created an anchor entity requesting any horizontal plane that's at least 20 centimeters on a side.
With multiple players we want to make sure the board is in a good place for two players to walk around.
To ensure this we'll ask the host to place the board.
And since we want the same world position on both peers, we'll ask ARkit to synchronize this anchor.
How do we do this?
On the host, we'll use the same onTap handler as before.
We want to pick a spot that's good for everybody.
So we'll raycast into the real world.
This is similar to the hit test Ross showed us earlier, however this one is run against an estimation of the real world rather than virtual objects.
Our game board needs to go on a flat surface, so we're asking for a horizontal plane.
If it finds a horizontal plane we'll take the world transform and use it to create an AR anchor.
This is an ARkit anchor, which we add to the AR session provided by the view.
And this creates a synchronized anchor whose real world position will be coordinated by ARkit.
Using that AR anchor we'll create a RealityKit anchor entity.
This is our bridge between ARkit and RealityKit and allows us to attach our card.
And then we have the game board, as Ross showed us earlier.
Only the host needs to build the randomized board.
Everyone else receives it automatically via network synchronization.
The models we loaded aren't synchronized as part of the scene because that would be loads of data.
Remember when Ross showed us how to load the card templates?
Make sure we still do this on all appearance.
And that's enough to get our game running.
On the left we can see that the host has placed the board and the client can see it.
It's in the same real world location.
Watch as the host flips over one of the objects and then another.
OK, not a match.
Notice how it's automatically reflected on the client and how smooth the animation is.
No network program here required.
Now watch as the client flips over one of the cards.
it flips over in their screen but not on the hosts.
To explain what's happening I need to introduce you to ownership.
What is ownership?
It's the right to modify an entity.
In a shared session an entity has one owner at a time, which defaults to whomever created the entity.
In our case that's the host.
Ownership is transferable, which is how you can allow other players to make changes.
And ownership transfer is configurable so you can decide which entities can be transferred and at what times.
To illustrate ownership let's look at what's happening in our game.
The host creates the cards, which means it owns them.
And the cards automatically show up on the client.
When the owner of an entity makes the change, such as flipping it over, this change is sent to the client and reflected in their scene.
Since RealityKit has been built from the ground up for multiplayer, we're only synchronizing the instruction to animate, rather than to transform every frame.
This is why it looks so smooth on the client.
Now the client flips over our card.
Since the client does not own the entity, the change will not get sent to peers.
While it's still allowed to make the changes locally, they're likely to get overwritten the next time the owner sends an update.
This presents a dilemma because we do want to make changes to the card.
So let's go back a step before the client had turned over the robot.
So I mentioned earlier, ownership can be transferred.
Any peer can request ownership of an entity.
Let's have the client request ownership of a yellow robot before making its changes.
It sends a request to the host asking for ownership.
The entities owner decides whether to allow the transfer.
It may decline if a different peer requested ownership or if the object's transfer mode was changed.
Since from the default state the host accepts the request and ownership is transferred to the client.
The client is now free to make changes to the card.
We can reveal the card and the changes will be reflected on the host.
And we've made this simple to use.
The client starts by requesting ownership of the entity.
If we all ready own the object that's fine.
The request will be granted without doing any additional work.
When the ownership request returns we'll be informed whether the ownership was granted.
If it was, we'll call reveal on the card to flip it over.
However, if the request was denied, for example someone else turned it over, we'll give the user the opportunity to select a different card.
And that's all we need to flip over the card and see it on both peers.
But we want to go one step further.
While the card is revealed, we want to keep ownership of it.
We want to deny any requests to take ownership while we have it revealed.
We'll go back to our reveal method.
Here's where we previously set the revealed property to true.
Because we adopted the codable protocol on card component, this automatically updates on other peers.
There's nothing else to do.
And after we change the cards revealed property to true, set the ownership transfer mode to manual.
This automatically denies any requests to transfer the entity.
When we flip the object back to hidden we'll want to start accepting requests for ownership.
This is what the hide method might look like.
After changing revealed to false, we changed the ownership transfer mode to auto accept.
This will instruct RealityKit to accept transfer requests for this entity automatically.
One thing that's worth pointing out the host isn't special when it comes to ownership because it placed the board, it initially owned all the cards, but otherwise behaves like any other peer.
Once an object is transferred, if the host wants to turn it over it needs to request it too.
You can use the ownership transfer mode to change this behavior to suit your app.
And there's one last detail for our app.
When you play with two or more players you'll notice that the board can get a little confusing.
It could be hard to know what piece you've selected and which ones were selected by others.
We'd like to add a transparent disk to display our selection.
As we've seen, everything gets shared.
So that will show for everyone and defeat its purpose.
Fortunately, RealityKit supports local-only entities.
This is ideal for displaying selection indicators or hidden information like your hand in poker.
And we do this by removing the synchronization component and otherwise handling the entity like any other.
If the entity has children, they will be unshared as well.
This can be useful to make an entire tree of entities local only.
While I'm not showing it here, I've created a selection entity class that represents our selection.
It's initializer adds a white slightly transparent disk.
Let's take a look at adding it to our reveal method from earlier.
We create the entity and position it slightly above the card.
Then we assign nil to the synchronization component.
This instructs RealityKit that we do not want to share this entity.
And we add the child normally to enter the hierarchy.
Now that we've added it in reveal, we need to remove it when we hide the card.
Here's the hide method from earlier.
After changing other properties, we'll iterate over the children with the for loop.
We can use the where clause to look for entities of type selection entity.
Once we find it we'll remove it from its parent.
Since we know there can only be one selection entity, we'll break out of the loop.
And that's it.
We're handling ownership correctly and adding or removing a local entity for selection.
Our game now works correctly across two devices.
And while we've demonstrated using two devices for simplicity, we can actually support more devices without any additional code.
Let's look back at what we've learned.
Today we've shown you how RealityKit makes building AR Apps easy and fast.
We've covered how to place content with anchoring, load assets both synchronous and asynchronously, integrate interaction in hit testing, create custom components in entities, and how to add multiplayer to augmented reality experiences.
I hope that we've given you a better understanding of what RealityKit is and what it provides.
We can't wait to see what you do with it.
For more information and access to this session video check out this session webpage.
Also check out Introducing RealityKit and Reality Composer for more information on RealityKit and don't miss Introducing ARkit 3 to learn more about collaborative session and other new features.
And be sure to catch us in the labs today at noon and tomorrow at 3:00 PM.
Thanks everyone and please enjoy the rest of the conference.
[ Applause ]