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