First Project with Avatars
This tutorial will introduce how the Genies Avatar SDK can be used for basic gameplay.
It will cover these topics:
- Creating a UI for users to login to their Genies accounts.
- Once logged in, spawning the user's global Avatar.
- Connecting the Avatar to an external character controller package.
Open Unity Project
Follow the Getting Started tutorial for directions on how to create a Unity project with the Genies Avatar SDK installed.
Install the Character Controller
Add to My Assets
Unity offers a character controller package named Starter Assets - ThirdPerson. Visit the package's Asset Store page and click the blue Add to My Assets button.
Download and Install the Package
Once it has been added to your assets, open the Package Manager window in Unity.
Select the Packages: My Assets option in the top left dropdown. Find and select the Start Assets - ThirdPerson package. Click the Download button. After it is downloaded, click the Import button.
Upgrade Dependencies
There may be a popup that appears asking if you want to upgrade dependencies. Click the Install/Upgrade button.
Import the Assets
A popup window will appear with a list of all the assets. Make sure they are all selected and click the Import button.
Open the Playground Scene
Once imported, open the Assets > StarterAssets > ThirdPersonController > Scenes folder in the Project window. Double click the Playground scene to open it up.
Test the Scene
Enter Play mode. Use they keyboard (WASD) and mouse to move the model around.
Create a Login UI
Open the Simulator Window
Click the top-left dropdown menu in the Game window and select Simulator. Then select a mobile device.
Create a UI Canvas
In the Hierarchy window, right click and add a new UI Canvas object and name it Login Canvas
. In the Inspector window, set the UI Scale Mode property to Scale With Screen Size and set the Reference Resolution to X: 1080
and Y: 1920
.
Create UI
Add the following UI elements as children to the Login Canvas:
- (Optional) Panel object for a background
- Text object named
Status Text
- Button object named
Instant Login Button
- Button object named
Logout Button
- Input Field object named
Email Input Field
- Button object named
Submit Email Button
- Input Field object named
OTP Input Field
- Button object named
Submit OTP Button
- Button object named
Resend OTP Button
You may need to import the TextMeshPro packages if the text is not appearing. See the Common Issues page for a breakdown.
Create the Login Controller Script
Create a New Script
In the Hierarchy window, select the Login Canvas object. In the Inspector window, click the Add Component button near the bottom and then select the bottom option to New Script. Name the script LoginController
.
Add Code to the Script
Open the LoginController script.
Initial Code
Start by adding this initial code to create object references for the UI elements and the player:
using UnityEngine;
using Genies.Sdk.Avatar;
using TMPro;
using UnityEngine.UI;
public class LoginController : MonoBehaviour
{
[Header("UI References")]
[SerializeField] private TMP_Text statusText;
[SerializeField] private Button instantLoginButton;
[SerializeField] private Button logoutButton;
[SerializeField] private TMP_InputField emailInputField;
[SerializeField] private Button submitEmailButton;
[SerializeField] private TMP_InputField otpInputField;
[SerializeField] private Button submitOTPButton;
[SerializeField] private Button resendOTPButton;
[Header("Player Reference")]
[SerializeField] private GameObject playerRoot;
}
On Submit Email Function
Add this function to the LoginController
class:
// Triggered when the user clicks the "Submit Email" button
private async void OnSubmitEmail()
{
string email = emailInputField.text;
statusText.text = $"Email submitted: {email}";
(bool success, string errorMessage) = await GeniesSdkAvatar.StartLoginEmailOtpAsync(email);
if (success)
{
statusText.text = "OTP sent successfully. Please check your email.";
}
else
{
statusText.text = "Error sending OTP: " + errorMessage;
}
}
This function uses StartLoginEmailOtpAsync
to submit the inputted email and start the OTP login process. If it is a valid email, then the user should receive a email with an OTP verification code.
On Resend OTP Function
Add this function to the LoginController
class:
// Triggered when the user clicks the "Resend OTP" button
private async void OnResendOTP()
{
statusText.text = "Resending OTP...";
(bool success, string errorMessage) = await GeniesSdkAvatar.ResendEmailCodeAsync();
if (success)
{
statusText.text = "OTP resent successfully. Please check your email.";
}
else
{
statusText.text = "Error resending OTP: " + errorMessage;
}
}
This function uses ResendEmailCodeAsync
to resend a new OTP to the email that requested the initial OTP. It's good practice to have this functionality in case the first OTP failed to send or the user deleted the message by mistake.
On Submit OTP Function
Add this function to the LoginController
class:
// Triggered when the user clicks the "Submit OTP" button
private async void OnSubmitOTP()
{
string otpCode = otpInputField.text;
statusText.text = $"OTP submitted: {otpCode}";
(bool success, string errorMessage) = await GeniesSdkAvatar.SubmitEmailOtpCodeAsync(otpCode);
if (success)
{
statusText.text = "OTP verified successfully. User logged in.";
}
else
{
statusText.text = "Error verifying OTP: " + errorMessage;
}
}
This function uses SubmitEmailOtpCodeAsync
to submit the inputted OTP verification code. If the code is correct, then it will sign in the user to their Genies account and create autherization credential tokens so that user can instant login the next time.
On Instant Login Function
Add this function to the LoginController
class:
// Triggered when the user clicks the "Instant Login" button
private async void OnInstantLogin()
{
statusText.text = "Attempting instant login...";
await GeniesSdkAvatar.TryInstantLoginAsync();
}
This function uses TryInstantLoginAsync
to login using stored autherization credential tokens instead of doing the OTP workflow. This will only work if the user has signed in before with the OTP workflow.
On User Login Function
Add this function to the LoginController
class:
// Triggered when the user successfully logs in
private void OnUserLogin()
{
statusText.text = "User logged in.";
playerRoot.SetActive(true);
this.gameObject.SetActive(false);
}
This function activates the player root object and then deactivates the login UI object to hide it. The player root object needs to be deactivated at the start because it has code that hides the mouse and prevents interaction with the login UI.
On Logout Function
Add this function to the LoginController
class:
// Triggered when the user clicks the "Logout" button
private async void OnLogout()
{
statusText.text = "Logging out...";
await GeniesSdkAvatar.LogOutAsync();
statusText.text = "Logged out.";
}
This function uses LogOutAsync
to log out the user if they are logged in to their Genies account.
Start Function
Add this function to the LoginController
class:
private void Start()
{
statusText.text = "Ready";
// Button listeners
submitEmailButton.onClick.AddListener(OnSubmitEmail);
submitOTPButton.onClick.AddListener(OnSubmitOTP);
logoutButton.onClick.AddListener(OnLogout);
instantLoginButton.onClick.AddListener(OnInstantLogin);
resendOTPButton.onClick.AddListener(OnResendOTP);
// Event listeners
GeniesSdkAvatar.Events.UserLoggedIn += OnUserLogin;
}
This function will add listeners to all the button click events and also to the GeniesSdkAvatar.Events.UserLoggedIn
event which is fired when a user logs in.
On Destroy Function
Add this function to the LoginController
class:
private void OnDestroy()
{
// Remove button listeners
submitEmailButton.onClick.RemoveListener(OnSubmitEmail);
submitOTPButton.onClick.RemoveListener(OnSubmitOTP);
logoutButton.onClick.RemoveListener(OnLogout);
instantLoginButton.onClick.RemoveListener(OnInstantLogin);
resendOTPButton.onClick.RemoveListener(OnResendOTP);
// Clean up event listeners
GeniesSdkAvatar.Events.UserLoggedIn -= OnUserLogin;
}
This function will remove all button and event listeners when the script is destroyed. It is good coding practice to cleanup event listeners when destroying an object to avoid memory leak.
Add the UI References
Save the code and return to Unity. In the Hierarchy window, select the Login Canvas object. Drag and drop all the needed objects from the Hierarchy window into the reference input fields in the Inspector window.
Deactivate the Player Root
In the Hierarchy, select the PlayerArmature object. In the Inspector, deactivate the button at the top left.
The reason the player root needs to be deactivated at the start is because the player root has code that causes the mouse to not interact with UI which would make it impossible to login. The login code will activate the player root once the user is logged in.
Test the Code
Enter Play mode. You should be able to login which will hide the UI and show the player.
Load the Avatar and Control It
Remove the Default Animator
In the Hierarchy, select the PlayerArmature object. In the Inspector, click the button at the top right of the Animator component. Select Remove Component.
The reason the default Animator needs to be removed is because the Avatar will be loaded in with its own Animator. There will need to be some code added later on to connect the loaded Animator with the character controller logic.
Create a New Script
In the Hierarchy window, select the PlayerArmature object. In the Inspector window, click the Add Component button near the bottom and then select the bottom option to New Script. Name the script AvatarController
.
Add Code to the Script
Open the AvatarController script.
Add the following code to load the user Avatar:
using Genies.Sdk.Avatar;
using UnityEngine;
public class AvatarController : MonoBehaviour
{
[SerializeField] private string avatarName = "MyAvatar";
[SerializeField] private Transform parentTransform = null;
[SerializeField] private RuntimeAnimatorController animatorController = null;
private void Start()
{
if (GeniesSdkAvatar.IsLoggedIn)
{
SpawnUserAvatar();
}
else
{
GeniesSdkAvatar.Events.UserLoggedIn += SpawnUserAvatar;
}
}
private void OnDestroy() {
GeniesSdkAvatar.Events.UserLoggedIn -= SpawnUserAvatar;
}
async void SpawnUserAvatar()
{
ManagedAvatar userAvatar = await GeniesSdkAvatar.LoadUserAvatarAsync(avatarName, parentTransform, animatorController);
Debug.Log("User Avatar Loaded: " + userAvatar.Root.name);
}
}
This code will check if the user is logged in before calling the function that spawns the user's Avatar. It's good coding practice to clean up listener events to avoid memory leaks.
Add the Avatar References
Save the code and return to Unity. In the Hierarchy window, select the PlayerArmature object. Drag and drop all the needed objects from the Hierarchy window into the reference input fields in the Inspector window.
The StarterAssetsThirdPerson Animator Controller can be found in the Assets > StarterAssets > ThirdPersonController > Cahracter > Animations folder.
Delete the Default Geometry and Skeleton
In the Hierarchy, delete the child objects Geometry and Skeleton under the PlayerArmature parent object.
Open the Third Person Controller Script
In the Project window, open the Assets > StarterAssets > ThirdPersonController > Scripts folder. Double click the ThirdPersonController script to open it.
Edit the Code
Insert the following code to the Update
function:
private void Update()
{
_hasAnimator = TryGetComponent(out _animator);
if (_hasAnimator is false)
{
Animator anim = this.transform.GetComponentInChildren<Animator>();
if (anim != null)
{
_animator = anim;
_hasAnimator = true;
}
}
JumpAndGravity();
GroundedCheck();
Move();
}
This new code will find the Animator component in the loaded Avatar, which is a child of this script's GameObject, and connect it to the character controller logic.
Test the Code
Save the code and enter Play mode. The Avatar should load in and be controlled correctly.