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.
  • Downloading and using the sample scenes.
  • Connecting the Avatar to a character controller.
  • Creating an NPC with a locally stored Avatar definition.
  • Giving the NPC a custom animation to wave at the player.

Setup the Scene

The first step is to create a scene for the player to move around in and have a camera following it.

Open Unity Project

Follow the Getting Started tutorial for directions on how to create a Unity project with the Genies Avatar SDK installed.

Open a New Scene

In the Project window, find a folder named Scenes and double click the SampleScene asset to open it.

Sample Scene

tip

This is scene is included by default when creating a new Unity project. If for some reason your Unity project does not have this folder or asset, then create a new folder named Scenes and create a new scene named SampleScene.

Add the Ground Plane

Create a ground object using the Create > 3D Object > Plane option in the Hierarchy window.

Ground Plane

Add a Free Look Camera

Create a camera using the Create > Cinemachine > Targeted Cameras > FreeLook Camera option in the Hierarchy window. Make sure to disable it in the Inspector window.

Free Look

info

A FreeLook Camera is useful tool from the Cinemachine package for creating third person cameras that follow the player.

Create a Login UI

The next step is to create a login screen UI for the user to login to their Genies account.

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, select the Create > UI > Canvas option 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
  • Button object named Signup 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

Now the login screen needs logic so it calls the API methods to login the user to their Genies account.

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:

using UnityEngine;
using Genies.Sdk;
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;
[SerializeField] private Button signupButton;
}

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 AvatarSdk.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 AvatarSdk.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 AvatarSdk.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...";
var result = await AvatarSdk.TryInstantLoginAsync();
if (result.isLoggedIn)
{
statusText.text = "Instant login successful.";
}
else
{
statusText.text = "Instant login failed.";
}
}
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.";
this.gameObject.SetActive(false);
}
info

This function deactivates the login UI object to hide it once the user is logged in.

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 AvatarSdk.LogOutAsync();
statusText.text = "Logged out.";
}
info

This function uses LogOutAsync to log out the user if they are logged in to their Genies account.

On Signup Function

Add this function to the LoginController class:

    // Triggered when the user clicks the "Sign Up" button
public void OnSignUp()
{
Application.OpenURL(AvatarSdk.UrlGeniesHubSignUp);
}
info

This function uses the UrlGeniesHubSignUp parameter to open the Developer Portal signup website on the user's browser.

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);
signupButton.onClick.AddListener(OnSignUp);

// Event listeners
AvatarSdk.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);
signupButton.onClick.RemoveListener(OnSignUp);

// Clean up event listeners
AvatarSdk.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

Test the Code

Enter Play mode. You should be able to login which will hide the login UI.

Test Code

Add the Character Controller

The next step is to spawn the user Avatar and control it like a player. This will use the Avatar Starter sample scene for its useful prefabs and scripts.

Import the Avatar Starter Sample Scene

From the top menu, select Windows > Package Manager window. Then select Packages: In Project on the top left dropdown and also select the Genies Avatar SDK package on the left side menu.

Click the Samples tab and click the Import button next to the Samples Starter Scene sample.

Avatar Samples

Add the Character Controller Prefab

In the Project window, open the Assets > Samples > Genies Avatar SDK > X.Y.Z > Sample Starter Scene > Avatar Starter > Prefabs folder. Drag the AvatarSpawn prefab into the scene.

Spawn Prefab

info

This character controller prefab has script components that will control the Avatar by moving and jumping using user input.

Enable Touch Controls

In the Hierarchy, select the AvatarSpawn object. In the Inspector, make sure the Enable Touch Controls property is enabled.

Enable Touch

info

This property allows mobile devices to use UI joystick and buttons for input.

Add the Load My Avatar Script

Create a new empty Game Object named LoadMyAvatar. In the Project window, open the Assets > Samples > Genies Avatar SDK > X.Y.Z > Sample Starter Scene > Avatar Starter > Scripts folder. Drag the LoadMyAvatar script into the LoadMyAvatar component list.

Load Avatar

info

This script will load the Avatar once the user is logged in. It will also connect the animations and input systems to the character controller prefab scripts.

Add the Event System Prefab

In the Project window, open the Assets > Samples > Genies Avatar SDK > X.Y.Z > Sample Starter Scene > Common > Prefabs > MobileControls folder. Drag and drop the UI_EventSystem prefab into the scene.

Event System

tip

If there is already an EventSystem game object in the Hierarchy, make sure to delete that one. This is usually added by default when creating a UI Canvas object. This prefab has defined properties to work with the character controller system.

Add the Touch Controls UI

In the Project window, open the Assets > Samples > Genies Avatar SDK > X.Y.Z > Sample Starter Scene > Common > Prefabs > MobileControls folder. Drag and drop the GeniesTouchZones prefab into the scene.

Touch UI

info

This prefab has the UI for the move joystick, run button, and jump button.

Find the Avatar Animator

In the Project window, open the Packages > Genies Avatar SDK > Internal > Avatars > Runtime > Assets > CommonAnimations folder. Inside is the GeniesAvatarAnimator asset needed in the next step.

Avatar Animator

info

This folder contains a default animator and animation assets used by the Avatar Starter sample scene to animate the Avatar as it moves and jumps.

Add the Component Properties

In the Hierarchy window, select the LoadMyAvatar object and open the Inspector window.

Add the following properties to the LoadMyAvatar component:

  • Add the UI_EventSystem object to the Input System UI Input Module property.
  • Add the AvatarSpawn object to the Loaded Controller property.
  • Add the GeniesAvatarAnimator asset to the Optional Controller property.
  • Add the FreeLook Camera object to the Cinemachine Free Look Settings property.
  • Add the FreeLook Camera object to the Input Axis Controller property.

Add Properties

Test the Character Controller

Enter Play mode. After logging in, the Avatar should spawn and be controllable with the touch controls.

Test Game

tip

You may need to calibrate the FreeLook Camera properties for more optimal camera movement.

Add an NPC Avatar

The next step is to create an NPC Avatar. This will use the Using Multiple Avatars sample scene to create a locally stored Avatar definition. This definition can then be used to load an Avatar to represent the NPC.

Open the Sample Scene

In the Project window, open the Assets > Samples > Genies Avatar SDK > X.Y.Z > Sample Starter Scenes > Using Multiple Avatars > Scenes folder. Double click the MultipleAvatars scene to open it.

Open Sample

Play the Scene

Once the scene is opened, press the Play button. Once you are logged in, make sure the default values for the UIs are set to:

  • Save Locally & Exit Editor
  • Test-Profile

Then click one of the Avatars to open the Avatar Editor.

Play Sample

info

These two UIs are setting the Avatar Editor to save a locally stored Avatar definition under the name Test-Profile once the user clicks the Save button.

Save a Definition

Use the Avatar Editor to create the look of the NPC. Once finished, click the Save button to locally store the definition you created. Exit Play mode.

Save Definition

Find the Test Profile Definition

In the Project window, open the Assets > Genies > AvatarEditor > Profiles > Resources folder. Make sure the Test-Profile definition is successfully created.

Test Profile

Create NPC Spawn Object

Open the SampleScene in the Assets > Scenes folder. Create a new empty Game Object named NpcSpawn.

NPC Spawn

Create NPC Controller Script

In the Hierarchy window, select the NpcSpawn 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 NpcController.

Then set the Position property to Z: 3 and Rotation property to Y: 180.

NPC Controller

Add Code to the Script

Open the NpcController script and add this code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Genies.Sdk;

public class NpcController : MonoBehaviour
{
void Start()
{
if (!AvatarSdk.IsLoggedIn)
{
AvatarSdk.Events.UserLoggedIn += LoadNpc;
return;
}

LoadNpc();
}

private void OnDestroy()
{
AvatarSdk.Events.UserLoggedIn -= LoadNpc;
}

private async void LoadNpc()
{
var npcAvatar = await AvatarSdk.LoadFromLocalAvatarDefinitionAsync("Test-Profile");
npcAvatar.Root.transform.parent = this.transform;
npcAvatar.Root.transform.position = this.transform.position;
npcAvatar.Root.transform.rotation = this.transform.rotation;
}
}
info

The AvatarSdk.LoadFromLocalAvatarDefinitionAsync("Test-Profile"); method is loading the locally stored Avatar definition that you created earlier.

Test the Code

Save the code and enter Play mode. The NPC Avatar should load in front of the player Avatar.

Test NPC

Add a Wave Animation

The next step is to add a custom animation to the NPC so it waves when the player gets near it.

Download a Wave Animation

Download a wave animation that is formatted for humanoid models. A recommended site is Mixamo by Adobe that offers a wide variety of animations for humanoids for free.

Download Wave

Create an Animator Controller

In the Project window, right click and select Create > Animator Controller. Name it NpcAnimator.

Add Idle Animation

Open the Animator window by double clicking the NpcAnimator asset. In the Project window, search for idle in the search bar in the Packages directory. Drag the Idle animation into the Animator window.

Add Idle

info

This is a default idle animation provided by the Avatar SDK. It's deeply nested in the Packages folders so its easier to search its name.

Apply Humanoid Rig to Wave Animation

Drag and drop the downloaded wave animation asset into the Project window. Select the wave animation asset and open the Inspector window. In the Rig tab, set the Animation Type property to Humanoid. Then click the Apply button.

Humanoid Rig

tip

The Genies Avatar will only work with animations that are rigged for humanoid models.

Add the Wave Animation

Open the Animator window by double clicking the NpcAnimator asset. Drag and drop the wave animation asset into the Animator window. Rename the animation state to Wave. Then add transitions to and from the idle animation state.

Add Wave

Add a Waving Trigger

In the Animator window, click the Parameters tab at the top left and click the plus sign. Create a new Trigger parameter named Waving.

Add Trigger

Trigger the Wave Animation Transition

In the Animator window, select the transition going from the idle state to the wave state. In the Inspector window, make sure the Has Exit Time property is disabled and then add the Waving trigger to the Conditions list.

Trigger Transition

Add the Player Tag

Select the AvatarSpawn object and open the Inspector window. Set the Tag property to Player.

Player Tag

info

The player needs a tag so the NPC can determine if it did enter its collider and wave when the player is near.

Update the Code

Open the NpcController script and update the code to this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Genies.Sdk;

public class NpcController : MonoBehaviour
{

[SerializeField] private RuntimeAnimatorController npcAnimatorController;
private Animator npcAnimator;
private GameObject player;
private float rotationSpeed = 5f;

void Start()
{
if (!AvatarSdk.IsLoggedIn)
{
AvatarSdk.Events.UserLoggedIn += LoadNpc;
return;
}

LoadNpc();
}

private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
npcAnimator.SetTrigger("Waving");
player = other.gameObject;
}
}

private bool IsWaving()
{
AnimatorStateInfo stateInfo = npcAnimator.GetCurrentAnimatorStateInfo(0);
return stateInfo.IsName("Wave");
}

void Update()
{
if(player != null && IsWaving())
{
RotateToPlayer();
}
}

private void RotateToPlayer()
{
Vector3 direction = player.transform.position - transform.position;
direction.y = 0f; // prevent tilting

if (direction.sqrMagnitude < 0.001f) return;

Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
rotationSpeed * Time.deltaTime
);
}

private void OnDestroy()
{
AvatarSdk.Events.UserLoggedIn -= LoadNpc;
}

private async void LoadNpc()
{
var npcAvatar = await AvatarSdk.LoadFromLocalAvatarDefinitionAsync("Test-Profile");
npcAvatar.Root.transform.parent = this.transform;
npcAvatar.Root.transform.position = this.transform.position;
npcAvatar.Root.transform.rotation = this.transform.rotation;
npcAvatar.SetAnimatorController(npcAnimatorController);
npcAnimator = npcAvatar.Root.GetComponent<Animator>();
}
}
info

This new code will set the Animator Controller to the NPC. It will also rotate to face the player and trigger the wave animation if the player collides with the NPC's collider.

Add the Collider

Select the NpcSpawn object and open the Inspector window. Add a Sphere Collider component and set the Radius property to 1.5. Then set the Npc Animator property to the newly created Npc Animator Animator Controller.

Add Collider

Test the Project

Enter Play mode. The NPC should trigger the wave animation and rotate to the player when the player gets close enough.

Test Wave

Conclusion

This concludes the first project tutorial. Some next steps could be incorporating the Avatar Editor and custom UGC wearable gifting.