Files
BABA_YAGA/Assets/Third Parties/Opsive/UltimateCharacterController/Demo/Scripts/DemoManager.cs
2026-06-09 09:18:17 +07:00

476 lines
22 KiB
C#

/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Demo
{
using Opsive.Shared.Events;
using Opsive.UltimateCharacterController.Character;
using Opsive.UltimateCharacterController.Demo.Objects;
using Opsive.UltimateCharacterController.Demo.UI;
using Opsive.UltimateCharacterController.Game;
using Opsive.UltimateCharacterController.Inventory;
using Opsive.UltimateCharacterController.Traits;
using Opsive.UltimateCharacterController.Utility;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// The DemoManager will control the objects in the demo scene as well as the text shown.
/// </summary>
public class DemoManager : MonoBehaviour
{
/// <summary>
/// Container for each zone within the demo scene.
/// </summary>
[System.Serializable]
public class DemoZone
{
[Tooltip("The header text.")]
[SerializeField] protected string m_Header;
[Tooltip("The description text.")]
[SerializeField] protected string m_Description;
[Tooltip("The text that appears beneath the header requiring action.")]
[SerializeField] protected string m_Action;
[Tooltip("The trigger that enables the header and description text.")]
[SerializeField] protected DemoZoneTrigger m_DemoZoneTrigger;
[Tooltip("The objects that the trigger should enable.")]
[SerializeField] protected MonoBehaviour[] m_EnableObjects;
[Tooltip("The objects that the trigger should activate/deactivate.")]
[SerializeField] protected GameObject[] m_ToggleObjects;
public string Header { get { return m_Header; } }
public string Description { get { return m_Description; } }
public string Action { get { return m_Action; } }
public DemoZoneTrigger DemoZoneTrigger { get { return m_DemoZoneTrigger; } }
public MonoBehaviour[] EnableObjects { get { return m_EnableObjects; } }
public GameObject[] ToggleObjects { get { return m_ToggleObjects; } }
private int m_Index;
public int Index { get { return m_Index; } }
/// <summary>
/// Initializes the zone.
/// </summary>
/// <param name="index">The index of the DemoZone.</param>
public void Initialize(int index)
{
m_Index = index;
// Assign the spawn point so the character will know where to spawn upon death.
var spawnPoints = m_DemoZoneTrigger.GetComponentsInChildren<SpawnPoint>();
for (int i = 0; i < spawnPoints.Length; ++i) {
spawnPoints[i].Grouping = index;
}
// The toggled objects should start disabled.
for (int i = 0; i < m_ToggleObjects.Length; ++i) {
m_ToggleObjects[i].SetActive(false);
}
}
}
[Tooltip("A reference to the character.")]
[SerializeField] protected GameObject m_Character;
[Tooltip("Is the character allowed to free roam the scene at the very start?")]
[SerializeField] protected bool m_FreeRoam;
[Tooltip("A reference used to determine the character's perspective selection at the start.")]
[SerializeField] protected GameObject m_PerspectiveSelection;
[Tooltip("A reference to the panel which shows the demo text.")]
[SerializeField] protected GameObject m_TextPanel;
[Tooltip("A reference to the Text component which shows the demo header text.")]
[SerializeField] protected Text m_Header;
[Tooltip("A reference to the Text component which shows the demo description text.")]
[SerializeField] protected Text m_Description;
[Tooltip("A reference to the Text component which shows the demo action text.")]
[SerializeField] protected Text m_Action;
[Tooltip("A reference to the GameObject which shows the next zone arrow.")]
[SerializeField] protected GameObject m_NextZoneArrow;
[Tooltip("A reference to the GameObject which shows the previous zone arrow.")]
[SerializeField] protected GameObject m_PreviousZoneArrow;
[Tooltip("A list of all of the zones within the scene.")]
[SerializeField] protected DemoZone[] m_DemoZones;
[Tooltip("Should the ItemIdentifiers be picked up when the character spawns within free roam mode?")]
[SerializeField] protected bool m_FreeRoamPickupItemDefinitions = true;
[Tooltip("An array of ItemIdentifiers to be picked up when free roaming.")]
[UnityEngine.Serialization.FormerlySerializedAs("m_FreeRoamItemTypeCounts")]
[UnityEngine.Serialization.FormerlySerializedAs("m_FreeRoamItemIdentifierAmounts")]
[SerializeField] protected ItemDefinitionAmount[] m_FreeRoamItemDefinitionAmounts;
[Tooltip("The title that should be displayed when the character is not in a zone.")]
[SerializeField] protected string m_NoZoneTitle;
[Tooltip("The description that should be displayed when the character is not in a zone.")]
[SerializeField] protected string m_NoZoneDescription;
[Tooltip("Is this manager part of an add-on?")]
[UnityEngine.Serialization.FormerlySerializedAs("m_AddonDemoManager")]
[SerializeField] protected bool m_AddOnDemoManager;
#if FIRST_PERSON_CONTROLLER && THIRD_PERSON_CONTROLLER
[Tooltip("Specifies the perspective that the character should start in if there is no perspective selection GameObject.")]
[SerializeField] protected bool m_DefaultFirstPersonStart = true;
#endif
public GameObject Character { get { return m_Character; } }
public bool FreeRoam { get { return m_FreeRoam; } set { m_FreeRoam = value; } }
public GameObject PerspectiveSelection { get { return m_PerspectiveSelection; } set { m_PerspectiveSelection = value; } }
public DemoZone[] DemoZones { get { return m_DemoZones; } }
private UltimateCharacterLocomotion m_CharacterLocomotion;
private Health m_CharacterHealth;
private Respawner m_CharacterRespawner;
private Dictionary<DemoZoneTrigger, DemoZone> m_DemoZoneTriggerDemoZoneMap = new Dictionary<DemoZoneTrigger, DemoZone>();
private List<int> m_ActiveZoneIndices = new List<int>();
private int m_LastZoneIndex = -1;
private List<Door> m_Doors = new List<Door>();
private int m_EnterFrame;
private bool m_FullAccess;
/// <summary>
/// Initialize the default values.
/// </summary>
protected virtual void Awake()
{
#if !FIRST_PERSON_CONTROLLER || !THIRD_PERSON_CONTROLLER
var demoZones = new List<DemoZone>(m_DemoZones);
for (int i = demoZones.Count - 1; i > -1; --i) {
// The demo zone may belong to the other perspective.
if (demoZones[i].DemoZoneTrigger == null) {
demoZones.RemoveAt(i);
}
}
m_DemoZones = demoZones.ToArray();
#endif
for (int i = 0; i < m_DemoZones.Length; ++i) {
if (m_DemoZones[i].DemoZoneTrigger == null) {
continue;
}
m_DemoZones[i].Initialize(i);
m_DemoZoneTriggerDemoZoneMap.Add(m_DemoZones[i].DemoZoneTrigger, m_DemoZones[i]);
}
// Enable the UI after the character has spawned.
if (m_TextPanel != null) {
m_TextPanel.SetActive(false);
}
if (m_PreviousZoneArrow != null) {
m_PreviousZoneArrow.SetActive(false);
}
if (m_NextZoneArrow != null) {
m_NextZoneArrow.SetActive(false);
}
if (m_Action != null) {
m_Action.enabled = false;
}
// The controller updates within Update. Limit the update rate.
Application.targetFrameRate = 60;
}
/// <summary>
/// Initializes the character.
/// </summary>
protected virtual void Start()
{
InitializeCharacter(m_Character, true, true);
}
/// <summary>
/// Initializes the Demo Manager with the specified character.
/// </summary>
/// <param name="character">The character that should be initialized/</param>
/// <param name="selectStartingPerspective">Should the starting perspective be selected?</param>
/// <param name="teleport">Should the character be teleported to the first demo zone?</param>
protected void InitializeCharacter(GameObject character, bool selectStartingPerspective, bool teleport)
{
m_Character = character;
if (m_Character == null) {
return;
}
m_CharacterLocomotion = m_Character.GetComponent<UltimateCharacterLocomotion>();
m_CharacterHealth = m_Character.GetComponent<Health>();
m_CharacterRespawner = m_Character.GetComponent<Respawner>();
// Disable the demo components if the character is null. This allows for free roaming within the demo scene.
if (m_FreeRoam) {
m_FullAccess = true;
if (m_PerspectiveSelection != null) {
m_PerspectiveSelection.SetActive(false);
}
var uiZones = GetComponentsInChildren<UIZone>();
for (int i = 0; i < uiZones.Length; ++i) {
uiZones[i].enabled = false;
}
// All of the doors should be opened with free roam.
for (int i = 0; i < m_Doors.Count; ++i) {
m_Doors[i].CloseOnTriggerExit = false;
m_Doors[i].OpenClose(true, true, false);
}
// The enable objects should be enabled.
for (int i = 0; i < m_DemoZones.Length; ++i) {
for (int j = 0; j < m_DemoZones[i].EnableObjects.Length; ++j) {
m_DemoZones[i].EnableObjects[j].enabled = true;
}
}
// The character needs to be assigned to the camera.
var camera = UnityEngineUtility.FindCamera(null);
var cameraController = camera.GetComponent<UltimateCharacterController.Camera.CameraController>();
cameraController.SetPerspective(m_CharacterLocomotion.FirstPersonPerspective, true);
cameraController.Character = m_Character;
// The character doesn't start out with any items.
if (m_FreeRoamItemDefinitionAmounts != null && m_FreeRoamPickupItemDefinitions) {
var inventory = m_Character.GetComponent<InventoryBase>();
if (inventory != null) {
for (int i = 0; i < m_FreeRoamItemDefinitionAmounts.Length; ++i) {
if (m_FreeRoamItemDefinitionAmounts[i].ItemDefinition == null) {
continue;
}
inventory.Pickup(m_FreeRoamItemDefinitionAmounts[i].ItemIdentifier, m_FreeRoamItemDefinitionAmounts[i].Amount, -1, true, false);
}
}
}
if (m_Character.activeInHierarchy) {
EventHandler.ExecuteEvent(m_Character, "OnCharacterSnapAnimator");
}
enabled = false;
return;
}
// The cursor needs to be visible.
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
if (!selectStartingPerspective) {
return;
}
#if FIRST_PERSON_CONTROLLER && THIRD_PERSON_CONTROLLER
// Show the perspective selection menu.
if (m_PerspectiveSelection != null) {
// The character should be disabled until the perspective is set.
m_CharacterLocomotion.SetActive(false, true);
m_PerspectiveSelection.SetActive(true);
} else {
SelectStartingPerspective(m_DefaultFirstPersonStart, teleport);
}
#elif FIRST_PERSON_CONTROLLER
SelectStartingPerspective(true, teleport);
#else
SelectStartingPerspective(false, teleport);
#endif
}
/// <summary>
/// Keep the mouse visible when the perspective screen is active.
/// </summary>
private void Update()
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
/// <summary>
/// Registers the door with the DemoManager.
/// </summary>
/// <param name="door">The door that should be registered.</param>
public void RegisterDoor(Door door)
{
m_Doors.Add(door);
}
/// <summary>
/// The character has entered a trigger zone.
/// </summary>
/// <param name="demoZoneTrigger">The trigger zone that the character entered.</param>
/// <param name="other">The GameObject that entered the trigger.</param>
public void EnteredTriggerZone(DemoZoneTrigger demoZoneTrigger, GameObject other)
{
var characterLocomotion = other.GetComponentInParent<UltimateCharacterLocomotion>();
if (characterLocomotion == null || characterLocomotion.gameObject != m_Character) {
return;
}
DemoZone demoZone;
if (!m_DemoZoneTriggerDemoZoneMap.TryGetValue(demoZoneTrigger, out demoZone)) {
return;
}
if (m_CharacterHealth != null && m_CharacterHealth.Value == 0) {
return;
}
ActivateDemoZone(demoZone, false);
}
/// <summary>
/// Activates the specified demo zone.
/// </summary>
/// <param name="demoZone">The demo zone to active.</param>
/// <param name="teleport">Should the character be teleported to the demo zone?</param>
private void ActivateDemoZone(DemoZone demoZone, bool teleport)
{
if (m_ActiveZoneIndices.Count == 0 || m_ActiveZoneIndices[m_ActiveZoneIndices.Count - 1] != demoZone.Index) {
m_ActiveZoneIndices.Add(demoZone.Index);
}
m_LastZoneIndex = demoZone.Index;
ShowText(demoZone.Header, demoZone.Description, demoZone.Action);
if (m_PreviousZoneArrow != null) {
m_PreviousZoneArrow.SetActive(demoZone.Index != 0);
}
if (m_NextZoneArrow != null) {
m_NextZoneArrow.SetActive(demoZone.Index != m_DemoZones.Length - 1);
}
m_EnterFrame = Time.frameCount;
for (int i = 0; i < demoZone.EnableObjects.Length; ++i) {
demoZone.EnableObjects[i].enabled = true;
}
for (int i = 0; i < demoZone.ToggleObjects.Length; ++i) {
demoZone.ToggleObjects[i].SetActive(true);
}
// When the character reaches the outside section all doors should be unlocked.
if (!m_AddOnDemoManager && !m_FullAccess && demoZone.Index >= m_DemoZones.Length - 6) {
for (int i = 0; i < m_Doors.Count; ++i) {
m_Doors[i].CloseOnTriggerExit = false;
m_Doors[i].OpenClose(true, true, false);
}
m_FullAccess = true;
}
if (teleport) {
var position = Vector3.zero;
var rotation = Quaternion.identity;
SpawnPointManager.GetPlacement(m_Character, demoZone.Index, ref position, ref rotation);
m_CharacterLocomotion.SetPositionAndRotation(position, rotation, true);
}
// Set the group after the state so the default state doesn't override the grouping value.
m_CharacterRespawner.Grouping = demoZone.Index;
}
/// <summary>
/// The character has exited a trigger zone.
/// </summary>
/// <param name="demoZoneTrigger">The trigger zone that the character exited.</param>
public void ExitedTriggerZone(DemoZoneTrigger demoZoneTrigger)
{
DemoZone demoZone;
if (!m_DemoZoneTriggerDemoZoneMap.TryGetValue(demoZoneTrigger, out demoZone)) {
return;
}
for (int i = 0; i < demoZone.ToggleObjects.Length; ++i) {
demoZone.ToggleObjects[i].SetActive(false);
}
m_ActiveZoneIndices.Remove(demoZone.Index);
// Show standard text if the demo zone isn't the last demo zone.
if (m_ActiveZoneIndices.Count == 0 && (m_AddOnDemoManager || demoZone.Index != m_DemoZones.Length - 1) && m_EnterFrame != Time.frameCount) {
ShowText(m_NoZoneTitle.Replace("{AssetName}", AssetInfo.Name), m_NoZoneDescription, string.Empty);
} else if (m_ActiveZoneIndices.Count > 0 && m_LastZoneIndex != m_ActiveZoneIndices[m_ActiveZoneIndices.Count - 1]) {
ActivateDemoZone(m_DemoZones[m_ActiveZoneIndices[m_ActiveZoneIndices.Count - 1]], false);
}
}
/// <summary>
/// Teleports the character to the next or pervious zone.
/// </summary>
/// <param name="next">Should the character be teleported to the next zone? If false the previous zone will be used.</param>
public void Teleport(bool next)
{
var targetIndex = Mathf.Clamp(m_LastZoneIndex + (next ? 1 : -1), 0, m_DemoZones.Length - 1);
if (m_ActiveZoneIndices.Count > 0 && targetIndex == m_ActiveZoneIndices[m_ActiveZoneIndices.Count - 1]) {
return;
}
ActivateDemoZone(m_DemoZones[targetIndex], true);
}
/// <summary>
/// Sets the starting perspective on the character.
/// </summary>
/// <param name="firstPersonPerspective">Should the character start in a first person perspective?</param>
public virtual void SelectStartingPerspective(bool firstPersonPerspective)
{
SelectStartingPerspective(firstPersonPerspective, true);
}
/// <summary>
/// Sets the starting perspective on the character.
/// </summary>
/// <param name="firstPersonPerspective">Should the character start in a first person perspective?</param>
/// <param name="teleport">Should the character be teleported to the demo zone?</param>
protected void SelectStartingPerspective(bool firstPersonPerspective, bool teleport)
{
// Set the starting position.
m_LastZoneIndex = -1;
ActivateDemoZone(m_DemoZones[0], teleport);
// The character should be activated after positioned so the fall surface impacts don't play.
m_CharacterLocomotion.SetActive(true, true);
// Set the perspective on the camera.
var camera = UnityEngineUtility.FindCamera(null);
var cameraController = camera.GetComponent<UltimateCharacterController.Camera.CameraController>();
// Ensure the camera starts with the correct view type.
cameraController.FirstPersonViewTypeFullName = GetViewTypeFullName(true);
cameraController.ThirdPersonViewTypeFullName = GetViewTypeFullName(false);
cameraController.SetPerspective(firstPersonPerspective, true);
cameraController.Character = m_Character;
// The cursor should be hidden to start the demo.
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
enabled = false;
// The character and camera are ready to go - disable the perspective selection panel.
if (m_PerspectiveSelection != null) {
m_PerspectiveSelection.SetActive(false);
}
}
/// <summary>
/// Returns the full name of the view type for the specified perspective.
/// </summary>
/// <param name="firstPersonPerspective">Should the first person perspective be returned?</param>
/// <returns>The full name of the view type for the specified perspective.</returns>
protected virtual string GetViewTypeFullName(bool firstPersonPerspective)
{
return firstPersonPerspective ? "Opsive.UltimateCharacterController.FirstPersonController.Camera.ViewTypes.Combat" :
"Opsive.UltimateCharacterController.ThirdPersonController.Camera.ViewTypes.Adventure";
}
/// <summary>
/// Shows the text in the UI with the specified header and description.
/// </summary>
/// <param name="header">The header that should be shown.</param>
/// <param name="description">The description that should be shown.</param>
/// <param name="action">The action that should be shown.</param>
private void ShowText(string header, string description, string action)
{
if (m_TextPanel == null) {
return;
}
if (string.IsNullOrEmpty(header)) {
m_TextPanel.SetActive(false);
return;
}
m_TextPanel.SetActive(true);
m_Header.text = "--- " + header + " ---";
m_Description.text = description.Replace("{AssetName}", AssetInfo.Name);
if (m_Action != null) {
m_Action.text = action;
m_Action.enabled = !string.IsNullOrEmpty(action);
}
}
}
}