// Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Meta.XR.Movement.Samples
{
///
/// Menu that allows spawning characters.
///
public class MovementCharacterSpawnMenu : MonoBehaviour
{
///
/// The reference OVRBody to get the joint set from.
///
[SerializeField]
protected OVRBody _ovrBody;
///
/// Stylized character prefab to spawn.
///
[SerializeField]
protected GameObject _stylizedCharacterToSpawn;
///
/// High fidelity character prefab to spawn.
///
[SerializeField]
protected GameObject _highFidelityCharacterToSpawn;
///
/// Parent to spawn under.
///
[SerializeField]
protected Transform _spawnParent;
///
/// Offset per spawn.
///
[SerializeField]
protected Vector3 _spawnOffset = new Vector3(-1.0f, 0.0f, 0.0f);
[SerializeField, InspectorButton("AddStylizedCharacter")]
private bool _spawnStylizedButton;
[SerializeField, InspectorButton("AddHighFidelityCharacter")]
private bool _spawnHighFidelityButton;
private class SpawnMetadata
{
///
/// Constructor for spawned character.
///
/// Is parented character.
/// Spawned GameObject.
public SpawnMetadata(bool isParentedCharacter,
GameObject spawnedObject)
{
IsParentedCharacter = isParentedCharacter;
SpawnedObject = spawnedObject;
}
///
/// Is parented to a transform or not.
///
public readonly bool IsParentedCharacter;
///
/// Actual spawned character.
///
public readonly GameObject SpawnedObject;
}
private const int _characterSpawnLimit = 20;
private Vector3 _currentSpawnOffset, _currentSpawnOffsetNotParented;
private readonly List _charactersSpawned = new();
///
/// Move the non-mirrored characters out further so that they don't intersect any menus to the
/// right of the user.
///
private const float _nonMirroredOffsetMultiplier = 1.5f;
private void Awake()
{
Assert.IsNotNull(_stylizedCharacterToSpawn);
_currentSpawnOffset = _spawnOffset;
_currentSpawnOffsetNotParented = -_spawnOffset * _nonMirroredOffsetMultiplier;
}
///
/// Add a stylized character to the scene.
///
public void AddStylizedCharacter()
{
if (_charactersSpawned.Count >= _characterSpawnLimit)
{
Debug.LogWarning("Reached the limit of characters to spawn.");
return;
}
var newCharacter = Instantiate(_stylizedCharacterToSpawn);
newCharacter.SetActive(true);
newCharacter.GetComponent().ProvidedSkeletonType = _ovrBody.ProvidedSkeletonType;
AdjustSpawnedCharacterTransform(newCharacter, true, _currentSpawnOffset);
_currentSpawnOffset += _spawnOffset;
_charactersSpawned.Add(new SpawnMetadata(true, newCharacter));
}
///
/// Add a high fidelity character to the scene.
///
public void AddHighFidelityCharacter()
{
if (_charactersSpawned.Count >= _characterSpawnLimit)
{
Debug.LogWarning("Reached the limit of characters to spawn.");
return;
}
var newCharacter = Instantiate(_highFidelityCharacterToSpawn);
newCharacter.SetActive(true);
newCharacter.GetComponent().ProvidedSkeletonType = _ovrBody.ProvidedSkeletonType;
AdjustSpawnedCharacterTransform(newCharacter, true, _currentSpawnOffset);
_currentSpawnOffset += _spawnOffset;
_charactersSpawned.Add(new SpawnMetadata(true, newCharacter));
}
///
/// Removes last character spawned.
///
public void RemoveLastCharacter()
{
if (_charactersSpawned.Count == 0)
{
return;
}
var lastCharacterIndex = _charactersSpawned.Count - 1;
var isLastCharacterParented = _charactersSpawned[lastCharacterIndex].IsParentedCharacter;
Destroy(_charactersSpawned[lastCharacterIndex].SpawnedObject);
_charactersSpawned.RemoveAt(lastCharacterIndex);
if (isLastCharacterParented)
{
_currentSpawnOffset -= _spawnOffset;
}
else
{
_currentSpawnOffsetNotParented += _spawnOffset * _nonMirroredOffsetMultiplier;
}
}
private void AdjustSpawnedCharacterTransform(GameObject newCharacter, bool reparent, Vector3 offsetToUse)
{
var characterTransform = newCharacter.transform;
if (_spawnParent != null && reparent)
{
characterTransform.SetParent(_spawnParent, false);
}
characterTransform.localPosition = offsetToUse;
}
}
}