♪ [MUSIC] ♪ [NARRATOR] Welcome to Unite Now, where we bring Unity to you,
wherever you are. [CHEMA] Hello everyone,
and welcome to Unite Now. I'm Chema Damak,
a Technical Evangelist at Unity and in this session I'll be covering
how to use the Device Simulator to improve your user interface
and make it more adaptive with the least iteration possible.
So let's get started. Preparing and integrating
a user interface especially for mobile games
is getting very difficult to test on all devices that are
released to market each year. We are witnessing
a big boom in technology and thus different new
resolutions to be aware of. So if you are creating
a game for mobile and trying to have
the perfect user experience, you need to think about
the testing phase and spend a lot of time building
on various devices. At least that's what we needed
to do before Device Simulator. Today I'll show you
how this feature works. To do so we are going to adjust a simple user interface
for a fictional game. This will be composed
of five different parts: the player information, the buttons, the inventory, the friends list,
and the score of the game. We will be using some cool UI from the Ultimate Clean GUI
from the Asset Store applied to a Scene from
the Karting Microgame project available on Unity Learning. Let's move to the Editor. We've already prepared the Scene,
and as you can see the user interface
is exactly the same as the one we have in our mockup. Only problem here is that once
we change the resolution, we notice that for some
the UI does not follow. In addition to the resolution
provided by the Game window, we can manually add our own. As you can see,
these values do not work either. And if you have a larger pool
of target devices you might need to add
more values to the list. Or better yet, you can test it
with the Device Simulator. The Device Simulator package
is available in the Package Manager. If you are working
on Unity 2019.3 or above go to the Package Manager, be sure that you are enabling the preview package
and download your package. Once this is done you can find
your Simulator in the Window menu under General
or directly from the Game window. And that's it. Better than building to see how it looks like on a device, just go to the list
of provided devices and switch to your target device. This will not only allow you
to check your resolution, but also, if you need specification
about the phone or tablet itself you can find in the Device
Specification section some information about
the operating system, the CPU and the GPU. Let's have a quick glance
at the other things the Device Simulator
will help us do. To test our UI
when the device is flipped we can use the rotate button
in the Simulator view and choose the rotation
direction to check our UI on. If your game, like ours, will only be played
on a particular orientation, you can specify that directly
in the Screen Settings and uncheck the orientation
that will not be used. The Screen Settings from
the Device Simulator will not tamper
with your Project Settings. If you want to have them reflected
in your final build please make sure to apply them
in the Project Settings window. Finally the last section
of the Device Simulator lists all the Application Settings
you might need to test without having to switch them
on your device. These custom values will be
returned by the application API. For example,
if your game is intended to be displayed
with different languages depending on the system language of the operating system
it's running on, you might want to check
all the languages implemented and how they react
with the UI you have created. Also, if your game is depending
on internet reachability and have different states
between local area network, carrier data network or no network, you can easily simulate these states directly from the Editor. A button is also available
to simulate a Low Memory event. This event normally occurs when an iOS or Android
device notifies you of low memory while the app
is running in the foreground. Now it can be reproduced
by a simple click on this button. If you need a custom device
or one that has not been added yet, you can easily create your own. Just go to the
Package Manager > Device Simulator package and you'll find the link
to the documentation to help you create your own device. You can either create it
with extended information or just the basic ones. Then to add it to the list,
open the Preferences and go to Device Simulator, and link the folder
in the Device Directory field. Now let's go back to our UI. We've requested more information
about the UI from the artist so we could have it
following the screen. They asked to have the avatar
being in the top-left of the screen the score in the top-right, having the button
in the left-center and the inventory
and the friends list taking a percentage
of the height and the width. We'll do just that. Before starting, we'll check
the scaling method that we are using for this Canvas. It will re-scale with the screen
with the Reference Resolution that we have set to 1920 by 1080. Second, we're moving
to the player panel. We will need to anchor it
to the top-left of the screen. And because we want the calculation
of the distance to be made from the pivot
of the top-left of the panel to the anchor on the top-left
of the parent, we will change the pivot
to "0" on X, and "1" on Y. This can be made either in the
RectTransform or in the Scene directly. It will ensure us that whatever
the resolution is, this panel will always be anchored
in the top-left with a distance of -30 pixels from
the top, and 30 from the left. So let's test this. Now to the buttons.
We'll need them to be positioned compared to the center-left anchor,
so we'll do the same. We change the pivot
and the anchoring, and then we put the new values. Awesome. Now the Friends List. The artist specifically
needed the list to cover a certain percentage
of the screen. To change the anchoring
we could do it on the Scene, drag them
and reading the percentage, or for more accurate values, we can do so in
RectTransform Component. Know that these values
need to go from zero to one. Zero on X being the first point
in the left of the screen, and 1 being the last point
in the far right. That means that to follow the logic
of the artist we need this panel to end at "0.98" on the X, and start 30% before that, so "0.68". Same for the height.
Starting from zero on the Y, ending with "0.85".
Let's test this now. And it's working just fine. Same thing is done to the Inventory, and even though we don't see
any difference for this one, we know that the artist's wishes
have been respected. Final element, the Score. Let's anchor it to the top-right. Now that all our elements
are properly anchored, let's test them
with the Device Simulator. It works perfectly resolution wise, but on this device, the buttons
are not properly displayed. Smartphones and tablets
nowadays present a new challenge for everyone:
the notches and edges. That means that if we want to have
an element of the UI in one of the corners
or in one of the borders, we need to take in consideration
the safe area. Let's get some help
from the Device Simulator. To visualize the safe area,
we click on the button Safe Area and a border will be drawn
in the screen. You can change the color
and the width of the border in the preferences
under Device Simulator. To see this change, we need to
reload the Device Simulator window. Okay. Now that we can see it, we can take care of the issue
with one of two methods, either we decide
that nothing exists outside of the safe area, so we uncheck the Full Screen Toggle and nothing is rendered
outside of the borders, not even the game itself. To see that reflected
in the final build, make sure to change it
in the player preferences. Or the second method
would be to create a script that renders our UI just inside
the safe area zone. The script you create
can be as simple or as complex as your UI is. For this project we will use
the second method and create our own script,
the safe area setter. Let's create an empty panel that
will act as the Panel Safe Area, and create and add our script to it. We will use the Screen API
and get the safe area of the screen in pixels
with Screen.safeArea Once we have this Rect
we'll convert it into values that can be assigned
to our panel's anchor. To do so we get the position
of the safeArea as the minimum anchor, and the position plus the size
as the maximum anchor. Then we normalize these values
following the Canvas resolution. Let's assign these new values
to our panel. Finally we make sure the script
is updating the anchors each time the rotation or the resolution
is changing during runtime. And of course, since we are using a Canvas to normalize
our anchor's values, we need to assign this Canvas. Now let's test this. When we enter Play Mode we will see that the panel that we have will re-scale
to follow the Safe Area. Whenever we change
the resolution or the rotation the panel will follow. Now that you are satisfied
with the result, we can drag and drop
the other panels we have as child
to the Safe Area panel. These elements will now adjust
to the panel the same way they were adjusting
to the Canvas. Let's try this again in Play Mode and test on other devices. With the Safe Area now secured, we can move
to the other parts of the UI, such as the Friends List. To test this feature
we need to manually fill it or create a script to do so. Let's look for the Prefab
that you need to use, drag and drop it, duplicate it, and click on the Play button
to preview it. Scroll down. Yes, it's working. Now before building,
we will need to remove all the added Prefabs
and the testing scripts. This makes the testing phase
very long and strenuous, especially if you have
multiple scenarios and need to test your user
experience for different cases. As an example, let's take a look
at what our artist requested. For the Friends List, they want
to implement different states, one where the list is filled, one where the list doesn't have
any connected friends and another one where the player
doesn't have any internet connection. In order to test this scenario
in the most accurate way possible, we are using the
Random User Generator API to get real-life information
for a list of users. As for the script to show
this information on screen, it's the same that will be used
in the final build, and no additional scripting
has been added. So let's figure out how to use
Device Simulator in this case. Of course for network reachability
it's easy to reproduce. We already have it
in the Application Settings. For the other cases, we are going
to create our own scenario to extend the Device Simulator window. To do so we are going
to use the UI Toolkit framework, download the UI Builder package, create a UI for our specific scenario with a title and a integer field, and create a script to extend
the Device Simulator. Let's start with the UI Builder. To download the package,
we open the Package Manager and make sure we are still enabling
the preview packages. The UI Toolkit,
formerly known as UI Element, is a built-in verified package that is already available
in our project. Now let's open
the UI Builder window and create a user interface
to extend the Editor. We save our UI document
as a UXML file that we will store in the resources
so it's easier to access. We need a title, and an input field. Let's change their names
so we could access them via code. And choose the levels
that will be displayed. Now that you have all we need, we will create the script
that will extend it, and call it
"DeviceSimulatorExtension". First thing we're going
to do is implement the IDeviceSimulatorExtension
interface. For that we need to be using
the name space Unity.DeviceSimulator Also, and because this interface
and the script are using the UI Element's name spaces, we are going to go ahead
and add them. Then because it's an interface, we need to also implement
the extended title variable that returns the title
of the extension and the OnExtendDeviceSimulator
method that takes as parameter the initial visual elemental
our UI will be added to. If we go back to our
Device Simulator window and wait for the script to recompile, we'll see that a foldout
has been added to this view with the name
that we assigned to it. Now let's populate this foldout. What we're going to do without
going too much into details is getting the UI from the UXML file we've created earlier
as a visual tree asset and clone it to the visual element
we created. We then look for the integer field
we will get the friends count from, then we search in our Scene
for the Friends List script that takes care of the logic
behind the Friends List. Once we have all
the components needed, we'll just do a callback
to the integer field and state that each time this value
changes the list is repopulated. Finally we initialize this field and then add the UI
to the initial visual element. Now let's go back to the Editor, after the script recompile, we'll see that our UI appears under the foldout we've created and is ready to use. Let's test it
and enter the Player Mode. Open the Friends List,
change the friends count value, and see it reflected
in the Friends List. With this extension we can check
the Friends List feature and how it reacts with the UI, how the Prefab displays the names, and if there's anything to change. We can also check the pooling system that we've implemented
and if it works. Let's change the values. With this test, we can also check
some errors like missing characters, and we can investigate them without
having to run it on a device. In this case, we will change
the Prefab we're using to populate
the list to another one that supports these characters. This works perfectly. I've created two other scenarios
for this project to show you how you could leverage
this feature even more. One of the scenarios is very helpful if you are looking to test
a Game Over pop-up. When you test a game,
one of the screens you need to check is the Game Over, and in this case some of the games
are very complicated to lose, others are way easier, but whenever
you need to test the screen after integration,
and if it has animations or sounds or maybe different layouts
for high score or not high score it becomes very time consuming. So I created this simple extension
to help trigger the Game Over either with or without a high score
to check this integration. Let's open it in the UI Builder
and see what we are working with. It has a toggle for the high score
and a button to open the window. Of course it has the title to show
what we are testing exactly. Now let's try it. We will add it to the Device Simulator
extension script the same way that we did
with the other UI. We clone the visual tree of the new
UXML on a new VisualElement, then we add the method that will
trigger the GameOver event with a boolean
for the high score state. This method will be called
each time the button is clicked and the high score will be updated each time the toggle is checked
or unchecked. Last thing, as we did previously,
we add this visual element to the foldout
and once this is done, we go back to our Device Simulator
and we'll see the new section appearing after recompile. Now let's test it,
and try the Game Over when there is a high score. And when there's not one. Yeah, working quite good. The other scenario uses a slide
to test the inventory system. When we add it,
we can check the particle system that is triggered only once
when an item is revealed without having to reveal the item
manually from the Hierarchy. I now have done all the tests
that I wanted to do without even having to build
or play the game. This does not mean
that I will not do it on a real device at one point, but that already cut
the number of iterations I'll have to go through
by half at least. And that's how Device Simulator
can make your life easier. You can test your game and UI
on realistic game views and check resolutions, safe areas
and try the rotation. You can also simulate
the language change, the internet connection drops,
and low memory events. You can even add your own
custom device to work on. And of course, you can create
your own scenarios and extend the Device Simulator
window to make your own tests. Thank you so much
for following this session. Hope to see you very soon
in the next one. Bye! ♪ [MUSIC] ♪