Unity VR Sync – Avatar Selector and VRChat world creation

Introduction

This week we looked into rigging the Avatars in Unity with VR IK using the Final IK asset. The process was fairly simple, in terms of just assigning the script to the gameObject which also contained the Animator class. This process also involved setting some control objects which would handle any avatar on scene, as you can see below.

Control Cubes in motion.

Dynamic linking in Unity

The idea of learning this, however, was to be able to dynamically link everything on the scene. Why? When someone enters the game as an avatar, the game has to automatically set the controls for the player, as well as for any other player who is instantiated inside the game. When the game is networked, there is no fixed path or reference point to which scripts or objects can refer too. We have to create these BRIDGES. We create these BRIDGES in order to handle these dynamically linked avatar controls.

  • BRIDGE_ controls are created as a template for any VR headset. When any VR avatar is put into the scene, it will subscribe its body position to the controls. They connect when the avatar is instanciated.
  • These bridges allow us to control our bodies.
  • Below you can see a screenshot of the hierarchy. There are three main sections:
Hierarchy View
  • RED: The Avatars, parented under an empty gameObject.
  • YELLOW: The BRIDGES, which connect to parts of the avatar Rig through the VRIK script. (I attached an image below)
  • BLUE: The dynamically linked controls. The bridges will look for these in order to attach to their positions. There is a BRIDGE for each of the three VR_BodyParts.
VRIK settings.

Character Selection

In order to make the option for the user to change avatar through keyboard input, I created 2 scripts: AvatarSelector, which is attached to the AvatarParent I showed in an earlier image. The second script is AvatarListener, which is attached to each avatar(Alvaro, Satomi_1, and Satomi_2), and it’s parameter can be set to Avatar1, Avatar2, OR Avatar3, which correspond to the ENUM keynames defined in the AvatarSelector script. This means that if you press key 1 in your keyboard, you shall see Alvaro. If you press 2, you shall see Satori_1, and if you press 3 you shall see Satori_2( blue and with a party hat.) You can see the video below, as well as each of the scripts.

AvatarSelector.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.InputSystem; 
using System;

public enum AVATAR
{
    avatar1,        //0
    avatar2,
    avatar3
}


public class AvatarSelector : MonoBehaviour
{
    // This will be our array of avatars on scene
    AvatarListener[] avatars;

    // This will be our keyboard input
    Keyboard keyboard;

    // The current Avatar is the onw which will we will see and control. By default it is set to avatar1.
    private AVATAR currentAvatar = AVATAR.avatar1;

    // This will display our current character on screen
    public TextMeshProUGUI avatarText;
    void Start()
    {
        // Set our current Keyboard as an input device.
        keyboard = Keyboard.current; 
        if(keyboard != null)
        {
            Debug.Log("Keyboard found!");
        }
        else
        {
            Debug.Log("Keyboard not found :(");
        }

        // Find all of the gameObject with the AvatarListener component attached. Our avatars.
        avatars = FindObjectsOfType<AvatarListener>();

    }

    private void SetAvatarText()
    {
        avatarText.text = ("You are now " + currentAvatar.ToString());

    }

    public void FixedUpdate()
    {
        // Remember which avatar is active at each frame.
        AVATAR lastAvatar = currentAvatar;

        // If you hit the "1" key in the keyboard, select avatar1
        if (keyboard.digit1Key.ReadValue() != 0)
        {
            // Set the current Avatar to avatar1
            currentAvatar = AVATAR.avatar1;
        }
        // If you hit the "1" key in the keyboard, select avatar1
        if (keyboard.digit2Key.ReadValue() != 0)
        {
            // Set the current Avatar to avatar1
            currentAvatar = AVATAR.avatar2;
        }
        // If you hit the "1" key in the keyboard, select avatar1
        if (keyboard.digit3Key.ReadValue() != 0)
        {
            // Set the current Avatar to avatar1
            currentAvatar = AVATAR.avatar3;
        }

        // Loop through each of our avatars.
        foreach ( var avatar in avatars)
        {
            // If the avatar is not the one we selected, set it unactive.
            if(avatar._avatar != currentAvatar )
            {
                avatar.gameObject.SetActive(false);
            }
            // If the avatar is the one we selected, set it active.
            else
            {
                avatar.gameObject.SetActive(true);
            }
        }

        // Print out everytime you change an avatar
        if(currentAvatar != lastAvatar)
        {

            SetAvatarText();
        }

    }

}

AvatarListener.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*
 Attach this to each Avatar. The Avatar Selector class will lokk for this script.
*/

public class AvatarListener : MonoBehaviour
{
    //Select the avatar
    public AVATAR _avatar;

}