Race_fields/Assets/_Project/Scripts/GameFlow/SelectionMenuController.cs

385 lines
13 KiB
C#

using System;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace RaceFields.GameFlow
{
public sealed class SelectionMenuController : MonoBehaviour
{
[Serializable]
public sealed class VehicleOption
{
public string displayName = "BUGGY";
public GameObject prefab;
public Vector3 previewRotation = new Vector3(0f, 145f, 0f);
public float previewScale = 1f;
}
[Serializable]
public sealed class MapOption
{
public string displayName = "DEMO SCENE";
public string sceneName = "Demo Scene";
public SelectedGameMode mode = SelectedGameMode.Arena;
}
private enum Step
{
Vehicle,
Mode,
Map
}
[Header("Content")]
[SerializeField] private VehicleOption[] vehicles;
[SerializeField] private MapOption[] maps;
[Header("Preview")]
[SerializeField] private Transform previewRoot;
[SerializeField] private float previewRotationSpeed = 28f;
[Header("Panels")]
[SerializeField] private GameObject vehiclePanel;
[SerializeField] private GameObject modePanel;
[SerializeField] private GameObject mapPanel;
[Header("Texts")]
[SerializeField] private Text titleText;
[SerializeField] private Text subtitleText;
[SerializeField] private Text vehicleNameText;
[SerializeField] private Text vehicleClassText;
[SerializeField] private Text[] modeLabels;
[SerializeField] private Text[] mapLabels;
private readonly string[] modeNames =
{
"\u0410\u0420\u0415\u041d\u0410",
"\u0413\u041e\u041d\u041a\u0410 \u041d\u0410 \u0412\u042b\u0416\u0418\u0412\u0410\u041d\u0418\u0415"
};
private Step currentStep;
private GameObject currentPreview;
private int vehicleIndex;
private int modeIndex;
private int mapIndex;
private void Awake()
{
if (previewRoot == null)
previewRoot = transform;
}
private void Start()
{
currentStep = Step.Vehicle;
ShowVehicle(vehicleIndex);
RefreshUi();
}
private void Update()
{
RotatePreview();
HandleKeyboard();
}
public void SelectPrevious()
{
MoveSelection(-1);
}
public void SelectNext()
{
MoveSelection(1);
}
public void Confirm()
{
if (currentStep == Step.Vehicle)
{
currentStep = Step.Mode;
RefreshUi();
return;
}
if (currentStep == Step.Mode)
{
mapIndex = 0;
currentStep = Step.Map;
RefreshUi();
return;
}
StartSelectedMap();
}
public void ChooseArena()
{
modeIndex = 0;
mapIndex = 0;
currentStep = Step.Map;
RefreshUi();
}
public void ChooseSurvivalRace()
{
modeIndex = 1;
mapIndex = 0;
currentStep = Step.Map;
RefreshUi();
}
public void ChooseCurrentMap()
{
StartSelectedMap();
}
public void Back()
{
if (currentStep == Step.Map)
currentStep = Step.Mode;
else if (currentStep == Step.Mode)
currentStep = Step.Vehicle;
RefreshUi();
}
private void HandleKeyboard()
{
#if ENABLE_INPUT_SYSTEM
Keyboard keyboard = Keyboard.current;
if (keyboard == null)
return;
if (keyboard.leftArrowKey.wasPressedThisFrame || keyboard.aKey.wasPressedThisFrame)
SelectPrevious();
else if (keyboard.rightArrowKey.wasPressedThisFrame || keyboard.dKey.wasPressedThisFrame)
SelectNext();
if (keyboard.spaceKey.wasPressedThisFrame || keyboard.enterKey.wasPressedThisFrame || keyboard.numpadEnterKey.wasPressedThisFrame)
Confirm();
else if (keyboard.escapeKey.wasPressedThisFrame || keyboard.backspaceKey.wasPressedThisFrame)
Back();
#endif
}
private void MoveSelection(int direction)
{
if (currentStep == Step.Vehicle)
{
int length = vehicles != null ? vehicles.Length : 0;
vehicleIndex = Wrap(vehicleIndex + direction, length);
ShowVehicle(vehicleIndex);
}
else if (currentStep == Step.Mode)
{
modeIndex = Wrap(modeIndex + direction, modeNames.Length);
}
else if (currentStep == Step.Map)
{
int length = GetMapCountForCurrentMode();
mapIndex = Wrap(mapIndex + direction, length);
}
RefreshUi();
}
private void ShowVehicle(int index)
{
if (currentPreview != null)
Destroy(currentPreview);
if (vehicles == null || vehicles.Length == 0 || vehicles[index] == null || vehicles[index].prefab == null)
return;
VehicleOption option = vehicles[index];
currentPreview = Instantiate(option.prefab, previewRoot.position, Quaternion.Euler(option.previewRotation), previewRoot);
currentPreview.name = "Runtime Preview - " + option.displayName;
currentPreview.transform.localPosition = Vector3.zero;
currentPreview.transform.localRotation = Quaternion.Euler(option.previewRotation);
currentPreview.transform.localScale = Vector3.one * Mathf.Max(0.01f, option.previewScale);
DisableGameplayComponents(currentPreview);
}
private void DisableGameplayComponents(GameObject root)
{
Rigidbody[] bodies = root.GetComponentsInChildren<Rigidbody>(true);
for (int i = 0; i < bodies.Length; i++)
{
bodies[i].isKinematic = true;
bodies[i].useGravity = false;
}
Collider[] colliders = root.GetComponentsInChildren<Collider>(true);
for (int i = 0; i < colliders.Length; i++)
colliders[i].enabled = false;
MonoBehaviour[] behaviours = root.GetComponentsInChildren<MonoBehaviour>(true);
for (int i = 0; i < behaviours.Length; i++)
behaviours[i].enabled = false;
AudioSource[] audioSources = root.GetComponentsInChildren<AudioSource>(true);
for (int i = 0; i < audioSources.Length; i++)
{
audioSources[i].Stop();
audioSources[i].enabled = false;
}
ParticleSystem[] particles = root.GetComponentsInChildren<ParticleSystem>(true);
for (int i = 0; i < particles.Length; i++)
particles[i].Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
TrailRenderer[] trails = root.GetComponentsInChildren<TrailRenderer>(true);
for (int i = 0; i < trails.Length; i++)
trails[i].emitting = false;
}
private void RotatePreview()
{
if (currentPreview == null)
return;
currentPreview.transform.Rotate(Vector3.up, previewRotationSpeed * Time.deltaTime, Space.World);
}
private void RefreshUi()
{
if (vehiclePanel != null)
vehiclePanel.SetActive(currentStep == Step.Vehicle);
if (modePanel != null)
modePanel.SetActive(currentStep == Step.Mode);
if (mapPanel != null)
mapPanel.SetActive(currentStep == Step.Map);
if (titleText != null)
titleText.text = GetTitle();
if (subtitleText != null)
subtitleText.text = GetSubtitle();
if (vehicleNameText != null)
vehicleNameText.text = GetVehicleName();
if (vehicleClassText != null)
vehicleClassText.text = (vehicleIndex + 1) + " / " + Mathf.Max(vehicles != null ? vehicles.Length : 0, 1);
RefreshModeLabels();
RefreshMapLabels();
}
private void RefreshModeLabels()
{
if (modeLabels == null)
return;
for (int i = 0; i < modeLabels.Length && i < modeNames.Length; i++)
{
if (modeLabels[i] != null)
modeLabels[i].text = i == modeIndex ? "< " + modeNames[i] + " >" : modeNames[i];
}
}
private void RefreshMapLabels()
{
if (mapLabels == null)
return;
MapOption selectedMap = GetMapForCurrentMode(mapIndex);
for (int i = 0; i < mapLabels.Length; i++)
{
if (mapLabels[i] != null)
mapLabels[i].text = selectedMap != null ? selectedMap.displayName : "NO MAPS";
}
}
private string GetTitle()
{
if (currentStep == Step.Mode)
return "\u0420\u0415\u0416\u0418\u041c \u0418\u0413\u0420\u042b";
if (currentStep == Step.Map)
return "\u0412\u042b\u0411\u041e\u0420 \u041a\u0410\u0420\u0422\u042b";
return "\u0412\u042b\u0411\u041e\u0420 \u041c\u0410\u0428\u0418\u041d\u042b";
}
private string GetSubtitle()
{
if (currentStep == Step.Vehicle)
return "\u0421\u0422\u0420\u0415\u041b\u041a\u0418 / A-D - \u041c\u0410\u0428\u0418\u041d\u0410 SPACE - \u0412\u042b\u0411\u0420\u0410\u0422\u042c";
if (currentStep == Step.Mode)
return "\u0421\u0422\u0420\u0415\u041b\u041a\u0418 / A-D - \u0420\u0415\u0416\u0418\u041c SPACE - \u0414\u0410\u041b\u042c\u0428\u0415 ESC - \u041d\u0410\u0417\u0410\u0414";
return "\u0421\u0422\u0420\u0415\u041b\u041a\u0418 / A-D - \u041a\u0410\u0420\u0422\u0410 SPACE - \u0421\u0422\u0410\u0420\u0422 ESC - \u041d\u0410\u0417\u0410\u0414";
}
private string GetVehicleName()
{
if (vehicles == null || vehicles.Length == 0 || vehicles[vehicleIndex] == null)
return "NO VEHICLE";
return string.IsNullOrWhiteSpace(vehicles[vehicleIndex].displayName) ? vehicles[vehicleIndex].prefab.name : vehicles[vehicleIndex].displayName;
}
private void StartSelectedMap()
{
MapOption selectedMap = GetMapForCurrentMode(mapIndex);
if (selectedMap == null || string.IsNullOrWhiteSpace(selectedMap.sceneName))
{
Debug.LogWarning("No map is configured for selected game mode.");
return;
}
SelectedGameMode selectedMode = modeIndex == 0 ? SelectedGameMode.Arena : SelectedGameMode.SurvivalRace;
GameObject selectedPrefab = vehicles != null && vehicles.Length > 0 ? vehicles[vehicleIndex].prefab : null;
GameSelectionState.SetSelection(selectedPrefab, GetVehicleName(), selectedMode, selectedMap.sceneName);
SceneManager.LoadScene(selectedMap.sceneName);
}
private MapOption GetMapForCurrentMode(int filteredIndex)
{
if (maps == null || maps.Length == 0)
return null;
SelectedGameMode selectedMode = modeIndex == 0 ? SelectedGameMode.Arena : SelectedGameMode.SurvivalRace;
int currentFilteredIndex = 0;
for (int i = 0; i < maps.Length; i++)
{
if (maps[i] == null || maps[i].mode != selectedMode)
continue;
if (currentFilteredIndex == filteredIndex)
return maps[i];
currentFilteredIndex++;
}
return null;
}
private int GetMapCountForCurrentMode()
{
if (maps == null || maps.Length == 0)
return 0;
SelectedGameMode selectedMode = modeIndex == 0 ? SelectedGameMode.Arena : SelectedGameMode.SurvivalRace;
int count = 0;
for (int i = 0; i < maps.Length; i++)
{
if (maps[i] != null && maps[i].mode == selectedMode)
count++;
}
return count;
}
private static int Wrap(int value, int length)
{
if (length <= 0)
return 0;
value %= length;
return value < 0 ? value + length : value;
}
}
}