Skip to main content

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.

Asset Store

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.

Package Manager

Upgrade Dependencies

There may be a popup that appears asking if you want to upgrade dependencies. Click the Install/Upgrade button.

Update Dependencies

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.

Install Package

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.

Open Scene

Test the Scene

Enter Play mode. Use they keyboard (WASD) and mouse to move the model around.

Test Playground

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.

Simulator Window

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.

Login Canvas

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

Login UI

tip

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.

Create Script

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;
}
}
info

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;
}
}
info

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;
}
}
info

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();
}
info

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);
}
info

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.";
}
info

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;
}
info

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;
}
info

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.

Add References

Deactivate the Player Root

In the Hierarchy, select the PlayerArmature object. In the Inspector, deactivate the button at the top left.

Deactivate Player

info

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.

Remove Animator

info

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.

Create Controller

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);
}
}
info

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.

Avatar References

tip

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.

Delete Default

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.

Third Controller

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();
}
info

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.

Test Project