Last active
June 25, 2025 21:43
-
-
Save leeprobert/815c7d2be754ae6384ce5c351041fd7e to your computer and use it in GitHub Desktop.
Unity C# class for loading a Ready Player Me model (avatar) and then setting the relevant Third Person Controller components (like the Starter Assets package that uses a Cinemachine Virtual Camera). This script based on the work of Sarge (RPM) and his tool for converting an older RPM model in the Editor. See this repos: https://github.com/srcnal…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Cinemachine; | |
using ReadyPlayerMe.Core; | |
using StarterAssets; | |
using UnityEngine; | |
using UnityEngine.InputSystem; | |
namespace com.wealdcreative.unity.readyplayerme { | |
public class RPMAvatarLoader : MonoBehaviour | |
{ | |
private const string CAMERA_TARGET_OBJECT_NAME = "CameraTarget"; | |
private readonly Vector3 avatarPositionOffset = new Vector3(0, -0.08f, 0); | |
[SerializeField][Tooltip("RPM avatar URL or shortcode to load")] | |
private string avatarUrl; | |
private GameObject avatar; | |
private AvatarObjectLoader avatarObjectLoader; | |
[SerializeField][Tooltip("If true it will try to load avatar from avatarUrl on start")] | |
private bool loadOnStart = true; | |
public event Action OnLoadComplete; | |
[Header("Asset References")] | |
[SerializeField][Tooltip("Animator to use on loaded avatar")] private GameObject _previewAvatar; | |
[SerializeField][Tooltip("Animator to use on loaded avatar")] private RuntimeAnimatorController _animatorController; | |
[SerializeField][Tooltip("Input Actions to control the avatar")] private InputActionAsset _inputActionAsset; | |
[SerializeField][Tooltip("Sound effect for landing")] private AudioClip _landingAudioClip; | |
[SerializeField][Tooltip("Footstep sound effects")] private AudioClip[] _footstepAudioClips; | |
private void Start() | |
{ | |
avatarObjectLoader = new AvatarObjectLoader(); | |
avatarObjectLoader.OnCompleted += OnLoadCompleted; | |
avatarObjectLoader.OnFailed += OnLoadFailed; | |
if (_previewAvatar != null) | |
{ | |
SetupAvatar(_previewAvatar); | |
} | |
if (loadOnStart) | |
{ | |
LoadAvatar(avatarUrl); | |
} | |
} | |
private void OnLoadFailed(object sender, FailureEventArgs args) | |
{ | |
OnLoadComplete?.Invoke(); | |
} | |
private void OnLoadCompleted(object sender, CompletionEventArgs args) | |
{ | |
if (_previewAvatar != null) | |
{ | |
Destroy(_previewAvatar); | |
_previewAvatar = null; | |
} | |
SetupAvatar(args.Avatar); | |
OnLoadComplete?.Invoke(); | |
} | |
private void SetupAvatar(GameObject targetAvatar) | |
{ | |
if (avatar != null) | |
{ | |
Destroy(avatar); | |
} | |
avatar = targetAvatar; | |
// Re-parent and reset transforms | |
avatar.transform.parent = transform; | |
avatar.transform.localPosition = avatarPositionOffset; | |
avatar.transform.localRotation = Quaternion.Euler(0, 0, 0); | |
avatar.tag = "Player"; | |
// Create camera follow target | |
GameObject cameraTarget = new GameObject(CAMERA_TARGET_OBJECT_NAME); | |
cameraTarget.transform.parent = avatar.transform; | |
cameraTarget.transform.localPosition = new Vector3(0, 1.5f, 0); | |
cameraTarget.tag = "CinemachineTarget"; | |
// Set the animator controller and disable root motion | |
Animator animator = avatar.GetComponent<Animator>(); | |
animator.runtimeAnimatorController = _animatorController; | |
animator.applyRootMotion = false; | |
// Add tp controller and set values | |
ThirdPersonController tpsController = avatar.AddComponent<StarterAssets.ThirdPersonController>(); | |
tpsController.GroundedOffset = 0.1f; | |
tpsController.GroundLayers = 1; | |
tpsController.JumpTimeout = 0.5f; | |
tpsController.CinemachineCameraTarget = cameraTarget; | |
tpsController.LandingAudioClip = _landingAudioClip; | |
tpsController.FootstepAudioClips = _footstepAudioClips; | |
// Add character controller and set size | |
CharacterController characterController = avatar.GetComponent<CharacterController>(); | |
characterController.center = new Vector3(0, 1, 0); | |
characterController.radius = 0.3f; | |
characterController.height = 1.9f; | |
// Add components with default values | |
avatar.AddComponent<BasicRigidBodyPush>(); | |
avatar.AddComponent<StarterAssetsInputs>(); | |
// Add player input and set actions asset | |
PlayerInput playerInput = avatar.GetComponent<PlayerInput>(); | |
playerInput.actions = _inputActionAsset; | |
playerInput.defaultActionMap = "Player"; | |
playerInput.ActivateInput(); | |
var camera = UnityEngine.Object.FindFirstObjectByType<CinemachineVirtualCamera>(); | |
if (camera) | |
{ | |
camera.Follow = cameraTarget.transform; | |
camera.LookAt = cameraTarget.transform; | |
} | |
} | |
public void LoadAvatar(string url) | |
{ | |
//remove any leading or trailing spaces | |
avatarUrl = url.Trim(' '); | |
avatarObjectLoader.LoadAvatar(avatarUrl); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment