655 lines
22 KiB
C#
655 lines
22 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.UI;
|
|
|
|
public class controllerSettings : MonoBehaviour
|
|
{
|
|
[Header("Inspector")]
|
|
public Button[] keyButtons;
|
|
public Button secondary_track3Button;
|
|
public Text errorText;
|
|
|
|
[Header("Message Colors")]
|
|
public Color messageErrorColor = Color.red;
|
|
public Color messageSuccessColor = Color.green;
|
|
|
|
[Header("Inspector")]
|
|
public Slider noteSpeedMultipler_slider;
|
|
public Text noteSpeedMultipler_valueText;
|
|
public Button noteSpeed_DecreaseButton;
|
|
public Button noteSpeed_IncreaseButton;
|
|
public Button noteSpeed_ResetButton;
|
|
|
|
[Header("call dt prefab")]
|
|
public GameObject delayTapper_prefab;
|
|
public GameObject dt_to_put;
|
|
public Button launch_delayTapper;
|
|
public Text currentDelayText;
|
|
|
|
private readonly string[] colors = { "red", "green", "yellow", "purple", "blue" };
|
|
private readonly string[] bindingDisplayNames = { "轨道1键位", "轨道2键位", "轨道3键位", "轨道4键位", "轨道5键位", "轨道3键位2" };
|
|
|
|
private const int SecondaryTrack3Slot = 5;
|
|
private const float NoteSpeedMin = 1f;
|
|
private const float NoteSpeedMax = 3.25f;
|
|
private const string NoteSpeedPrefKey = "noteSpeedMultiplier";
|
|
private const string NoteSpeedDefaultVersionKey = "noteSpeedMultiplierDefaultVersion";
|
|
private const int NoteSpeedDefaultVersion = 2;
|
|
private const float NoteSpeedDefault = 2.0f;
|
|
private const float ContinuousInitialDelay = 0.5f;
|
|
private const float ContinuousRepeatRate = 0.05f;
|
|
private const float StepAmount = 0.01f;
|
|
|
|
private bool isRebinding;
|
|
private int rebindingIndex = -1;
|
|
private KeyCode previousKey = KeyCode.None;
|
|
private bool previousKeyValid;
|
|
private Coroutine blinkCoroutine;
|
|
private Coroutine messageCoroutine;
|
|
private Coroutine continuousChangeCoroutine;
|
|
|
|
// Input System interactive rebind: a throwaway InputAction listens for the next keyboard
|
|
// press and reports the bound control's path, which we convert back to KeyCode so the
|
|
// existing KeyCode-based storage and conflict-swap logic stay unchanged.
|
|
private InputAction rebindAction;
|
|
private InputActionRebindingExtensions.RebindingOperation rebindOperation;
|
|
|
|
private void Start()
|
|
{
|
|
RefreshButtonLabels();
|
|
ClearMessage();
|
|
SetupKeyButtonListeners();
|
|
SetupNoteSpeedControls();
|
|
SetupDelayTapper();
|
|
RefreshDelayText();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (keyButtons != null)
|
|
{
|
|
foreach (Button button in keyButtons)
|
|
{
|
|
if (button != null) button.onClick.RemoveAllListeners();
|
|
}
|
|
}
|
|
|
|
if (secondary_track3Button != null) secondary_track3Button.onClick.RemoveAllListeners();
|
|
if (noteSpeedMultipler_slider != null) noteSpeedMultipler_slider.onValueChanged.RemoveListener(OnNoteSpeedSliderChanged);
|
|
if (noteSpeed_DecreaseButton != null) noteSpeed_DecreaseButton.onClick.RemoveAllListeners();
|
|
if (noteSpeed_IncreaseButton != null) noteSpeed_IncreaseButton.onClick.RemoveAllListeners();
|
|
if (noteSpeed_ResetButton != null) noteSpeed_ResetButton.onClick.RemoveAllListeners();
|
|
if (launch_delayTapper != null) launch_delayTapper.onClick.RemoveAllListeners();
|
|
|
|
CleanupRebindOperation();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
// Clicking away from the key buttons cancels an in-progress rebind. Key capture itself
|
|
// is handled by the Input System RebindingOperation started in OnKeyButtonClicked.
|
|
if (isRebinding && Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame && !IsPointerOverAnyKeyButton())
|
|
{
|
|
CancelRebind();
|
|
}
|
|
}
|
|
|
|
public void RefreshDelayText()
|
|
{
|
|
if (currentDelayText == null) return;
|
|
|
|
const string delayPrefsKey = "UserGlobalDelaySeconds";
|
|
if (PlayerPrefs.HasKey(delayPrefsKey))
|
|
{
|
|
float savedDelaySeconds = PlayerPrefs.GetFloat(delayPrefsKey, 0f);
|
|
int ms = Mathf.RoundToInt(savedDelaySeconds * 1000f);
|
|
currentDelayText.text = $"当前偏移:{(ms >= 0 ? "+" : "")}{ms}ms";
|
|
}
|
|
else
|
|
{
|
|
currentDelayText.text = "未设定偏移数值";
|
|
}
|
|
}
|
|
|
|
public void OnLaunchDelayTapperClicked()
|
|
{
|
|
if (delayTapper_prefab == null || dt_to_put == null)
|
|
{
|
|
Debug.LogWarning("[controllerSettings] Prefab or target container is missing!");
|
|
return;
|
|
}
|
|
|
|
GameObject instantiated = Instantiate(delayTapper_prefab, dt_to_put.transform);
|
|
Debug.Log($"[controllerSettings] Instantiated delay tapper prefab under {dt_to_put.name}");
|
|
|
|
delayTapperPrefab dtp = instantiated.GetComponent<delayTapperPrefab>();
|
|
if (dtp != null)
|
|
{
|
|
dtp.mainSettings = this;
|
|
}
|
|
}
|
|
|
|
private void SetupKeyButtonListeners()
|
|
{
|
|
if (keyButtons != null)
|
|
{
|
|
for (int i = 0; i < keyButtons.Length && i < colors.Length; i++)
|
|
{
|
|
int idx = i;
|
|
if (keyButtons[i] == null) continue;
|
|
keyButtons[i].onClick.RemoveAllListeners();
|
|
keyButtons[i].onClick.AddListener(() => OnKeyButtonClicked(idx));
|
|
}
|
|
}
|
|
|
|
if (secondary_track3Button != null)
|
|
{
|
|
secondary_track3Button.onClick.RemoveAllListeners();
|
|
secondary_track3Button.onClick.AddListener(() => OnKeyButtonClicked(SecondaryTrack3Slot));
|
|
}
|
|
}
|
|
|
|
private void SetupNoteSpeedControls()
|
|
{
|
|
if (noteSpeedMultipler_slider != null)
|
|
{
|
|
noteSpeedMultipler_slider.minValue = NoteSpeedMin;
|
|
noteSpeedMultipler_slider.maxValue = NoteSpeedMax;
|
|
EnsureDefaultNoteSpeedPreference();
|
|
|
|
float saved = Mathf.Clamp(PlayerPrefs.GetFloat(NoteSpeedPrefKey, NoteSpeedDefault), NoteSpeedMin, NoteSpeedMax);
|
|
noteSpeedMultipler_slider.value = saved;
|
|
UpdateNoteSpeedText(saved);
|
|
|
|
noteSpeedMultipler_slider.onValueChanged.RemoveAllListeners();
|
|
noteSpeedMultipler_slider.onValueChanged.AddListener(OnNoteSpeedSliderChanged);
|
|
|
|
EventTrigger trigger = noteSpeedMultipler_slider.gameObject.GetComponent<EventTrigger>();
|
|
if (trigger == null) trigger = noteSpeedMultipler_slider.gameObject.AddComponent<EventTrigger>();
|
|
trigger.triggers.RemoveAll(e => e.eventID == EventTriggerType.PointerUp);
|
|
EventTrigger.Entry entry = new EventTrigger.Entry { eventID = EventTriggerType.PointerUp };
|
|
entry.callback.AddListener(_ => OnNoteSpeedSliderPointerUp());
|
|
trigger.triggers.Add(entry);
|
|
}
|
|
|
|
if (noteSpeed_DecreaseButton != null)
|
|
{
|
|
noteSpeed_DecreaseButton.onClick.RemoveAllListeners();
|
|
noteSpeed_DecreaseButton.onClick.AddListener(() => ChangeNoteSpeedBy(-StepAmount));
|
|
AddButtonContinuousEvents(noteSpeed_DecreaseButton, -StepAmount);
|
|
}
|
|
|
|
if (noteSpeed_IncreaseButton != null)
|
|
{
|
|
noteSpeed_IncreaseButton.onClick.RemoveAllListeners();
|
|
noteSpeed_IncreaseButton.onClick.AddListener(() => ChangeNoteSpeedBy(StepAmount));
|
|
AddButtonContinuousEvents(noteSpeed_IncreaseButton, StepAmount);
|
|
}
|
|
|
|
if (noteSpeed_ResetButton != null)
|
|
{
|
|
noteSpeed_ResetButton.onClick.RemoveAllListeners();
|
|
noteSpeed_ResetButton.onClick.AddListener(ResetNoteSpeed);
|
|
}
|
|
}
|
|
|
|
private void SetupDelayTapper()
|
|
{
|
|
if (launch_delayTapper == null) return;
|
|
launch_delayTapper.onClick.RemoveAllListeners();
|
|
launch_delayTapper.onClick.AddListener(OnLaunchDelayTapperClicked);
|
|
}
|
|
|
|
private bool IsPointerOverAnyKeyButton()
|
|
{
|
|
if (EventSystem.current == null) return false;
|
|
|
|
Vector2 mousePos = Mouse.current != null ? Mouse.current.position.ReadValue() : Vector2.zero;
|
|
PointerEventData pointerData = new PointerEventData(EventSystem.current) { position = mousePos };
|
|
List<RaycastResult> results = new List<RaycastResult>();
|
|
EventSystem.current.RaycastAll(pointerData, results);
|
|
if (results.Count == 0) return false;
|
|
|
|
foreach (RaycastResult result in results)
|
|
{
|
|
GameObject go = result.gameObject;
|
|
if (IsPointerOverButton(go, secondary_track3Button)) return true;
|
|
|
|
if (keyButtons == null) continue;
|
|
foreach (Button button in keyButtons)
|
|
{
|
|
if (IsPointerOverButton(go, button)) return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsPointerOverButton(GameObject go, Button button)
|
|
{
|
|
return go != null && button != null && (go == button.gameObject || go.transform.IsChildOf(button.transform));
|
|
}
|
|
|
|
private void OnKeyButtonClicked(int index)
|
|
{
|
|
if (isRebinding || !IsValidBindingSlot(index)) return;
|
|
|
|
isRebinding = true;
|
|
rebindingIndex = index;
|
|
previousKey = GetKeyForSlot(index);
|
|
previousKeyValid = true;
|
|
|
|
blinkCoroutine = StartCoroutine(BlinkUnderscore(index));
|
|
ShowPersistentMessage("按下新按键", messageErrorColor);
|
|
|
|
StartInteractiveRebind();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start an Input System interactive rebind that waits for the next keyboard press.
|
|
/// The captured control path is mapped back to KeyCode and fed into the existing
|
|
/// HandleRebindKey conflict-swap logic, keeping storage and behavior equivalent.
|
|
/// </summary>
|
|
private void StartInteractiveRebind()
|
|
{
|
|
CleanupRebindOperation();
|
|
|
|
rebindAction = new InputAction("Rebind", InputActionType.Button, expectedControlType: "Button");
|
|
// PerformInteractiveRebinding needs an existing binding (index 0) to override.
|
|
rebindAction.AddBinding("<Keyboard>/space");
|
|
rebindOperation = rebindAction.PerformInteractiveRebinding(0)
|
|
.WithControlsExcluding("<Mouse>/leftButton")
|
|
.WithControlsExcluding("<Mouse>/rightButton")
|
|
.WithControlsExcluding("<Mouse>/middleButton")
|
|
.WithControlsExcluding("<Mouse>/position")
|
|
.WithControlsExcluding("<Mouse>/delta")
|
|
.WithCancelingThrough("<Keyboard>/escape")
|
|
.OnCancel(_ => { CleanupRebindOperation(); CancelRebind(); })
|
|
.OnComplete(op =>
|
|
{
|
|
// selectedControl.name is the control name (e.g. "d", "space"); map to KeyCode.
|
|
KeyCode captured = ResolveKeyCodeFromControl(op.selectedControl);
|
|
CleanupRebindOperation();
|
|
|
|
if (captured == KeyCode.None)
|
|
{
|
|
// Unmappable key: treat like a cancel so we don't clobber the binding.
|
|
CancelRebind();
|
|
return;
|
|
}
|
|
|
|
HandleRebindKey(captured);
|
|
})
|
|
.Start();
|
|
}
|
|
|
|
private static KeyCode ResolveKeyCodeFromControl(InputControl control)
|
|
{
|
|
if (control == null) return KeyCode.None;
|
|
// control.path -> "/Keyboard/d"; the last segment is the control name.
|
|
string name = control.name; // e.g. "d", "space", "leftShift"
|
|
return InputSystemKeyMap.PathToKeyCode("<Keyboard>/" + name);
|
|
}
|
|
|
|
private void CleanupRebindOperation()
|
|
{
|
|
if (rebindOperation != null)
|
|
{
|
|
rebindOperation.Dispose();
|
|
rebindOperation = null;
|
|
}
|
|
if (rebindAction != null)
|
|
{
|
|
rebindAction.Dispose();
|
|
rebindAction = null;
|
|
}
|
|
}
|
|
|
|
private IEnumerator BlinkUnderscore(int index)
|
|
{
|
|
Text txt = GetButtonText(index);
|
|
if (txt == null) yield break;
|
|
|
|
while (isRebinding && rebindingIndex == index)
|
|
{
|
|
txt.text = "_";
|
|
yield return new WaitForSeconds(0.5f);
|
|
txt.text = "";
|
|
yield return new WaitForSeconds(0.25f);
|
|
}
|
|
|
|
RefreshButtonLabel(index);
|
|
}
|
|
|
|
private void HandleRebindKey(KeyCode key)
|
|
{
|
|
if (key == KeyCode.Escape)
|
|
{
|
|
CancelRebind();
|
|
return;
|
|
}
|
|
|
|
if (!IsValidBindingSlot(rebindingIndex)) return;
|
|
|
|
int conflictSlot = FindSlotByKey(key, rebindingIndex);
|
|
KeyCode oldKey = GetKeyForSlot(rebindingIndex);
|
|
|
|
if (conflictSlot >= 0)
|
|
{
|
|
SetKeyForSlot(rebindingIndex, key);
|
|
SetKeyForSlot(conflictSlot, oldKey);
|
|
|
|
string msg = $"按键冲突:{KeyBindingManager.GetDisplayName(key)}已与{GetSlotDisplayName(conflictSlot)}互换。";
|
|
ShowMessage(msg, messageErrorColor, 2f);
|
|
StartCoroutine(FlashButtonInvalid(rebindingIndex));
|
|
}
|
|
else
|
|
{
|
|
SetKeyForSlot(rebindingIndex, key);
|
|
ClearMessage();
|
|
ShowMessage("按键设置已保存", messageSuccessColor, 2f);
|
|
}
|
|
|
|
previousKeyValid = false;
|
|
previousKey = KeyCode.None;
|
|
RefreshButtonLabels();
|
|
InputManager.Instance?.RefreshKeyLabels();
|
|
GameplayInputActions.Instance?.RefreshBindings();
|
|
StopRebind();
|
|
}
|
|
|
|
private int FindSlotByKey(KeyCode key, int ignoredSlot)
|
|
{
|
|
for (int i = 0; i <= SecondaryTrack3Slot; i++)
|
|
{
|
|
if (i == ignoredSlot || !IsValidBindingSlot(i)) continue;
|
|
if (GetKeyForSlot(i) == key) return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
private bool IsValidBindingSlot(int index)
|
|
{
|
|
return (index >= 0 && index < colors.Length) || index == SecondaryTrack3Slot;
|
|
}
|
|
|
|
private KeyCode GetKeyForSlot(int index)
|
|
{
|
|
if (index == SecondaryTrack3Slot) return KeyBindingManager.GetSecondaryKeyForTrack3();
|
|
if (index >= 0 && index < colors.Length) return KeyBindingManager.GetKeyForColor(colors[index]);
|
|
return KeyCode.None;
|
|
}
|
|
|
|
private void SetKeyForSlot(int index, KeyCode key)
|
|
{
|
|
if (index == SecondaryTrack3Slot)
|
|
{
|
|
KeyBindingManager.ChangeSecondaryTrack3KeyBinding(key);
|
|
return;
|
|
}
|
|
|
|
if (index >= 0 && index < colors.Length)
|
|
{
|
|
KeyBindingManager.ChangeKeyBinding(colors[index], key);
|
|
}
|
|
}
|
|
|
|
private string GetSlotDisplayName(int index)
|
|
{
|
|
if (index >= 0 && index < bindingDisplayNames.Length) return bindingDisplayNames[index];
|
|
return $"键位{index + 1}";
|
|
}
|
|
|
|
private void ShowMessage(string message, Color color, float duration = 2f)
|
|
{
|
|
if (errorText == null) return;
|
|
if (messageCoroutine != null) StopCoroutine(messageCoroutine);
|
|
messageCoroutine = StartCoroutine(ShowMessageCoroutine(message, color, duration));
|
|
}
|
|
|
|
private IEnumerator ShowMessageCoroutine(string message, Color color, float duration)
|
|
{
|
|
errorText.text = message;
|
|
errorText.color = color;
|
|
float t = 0f;
|
|
while (t < duration)
|
|
{
|
|
t += Time.deltaTime;
|
|
yield return null;
|
|
}
|
|
|
|
errorText.text = string.Empty;
|
|
messageCoroutine = null;
|
|
}
|
|
|
|
private void ShowPersistentMessage(string message, Color color)
|
|
{
|
|
if (errorText == null) return;
|
|
if (messageCoroutine != null)
|
|
{
|
|
StopCoroutine(messageCoroutine);
|
|
messageCoroutine = null;
|
|
}
|
|
|
|
errorText.text = message;
|
|
errorText.color = color;
|
|
}
|
|
|
|
private void ClearMessage()
|
|
{
|
|
if (messageCoroutine != null)
|
|
{
|
|
StopCoroutine(messageCoroutine);
|
|
messageCoroutine = null;
|
|
}
|
|
|
|
if (errorText != null) errorText.text = string.Empty;
|
|
}
|
|
|
|
private IEnumerator FlashButtonInvalid(int index)
|
|
{
|
|
Button button = GetButton(index);
|
|
if (button == null || button.image == null) yield break;
|
|
|
|
Color original = button.image.color;
|
|
button.image.color = Color.red;
|
|
yield return new WaitForSeconds(0.35f);
|
|
button.image.color = original;
|
|
}
|
|
|
|
private void CancelRebind()
|
|
{
|
|
CleanupRebindOperation();
|
|
|
|
if (previousKeyValid && IsValidBindingSlot(rebindingIndex))
|
|
{
|
|
SetKeyForSlot(rebindingIndex, previousKey);
|
|
InputManager.Instance?.RefreshKeyLabels();
|
|
GameplayInputActions.Instance?.RefreshBindings();
|
|
ClearMessage();
|
|
ShowMessage("按键设置已保存", messageSuccessColor, 2f);
|
|
}
|
|
|
|
previousKeyValid = false;
|
|
previousKey = KeyCode.None;
|
|
StopRebind();
|
|
}
|
|
|
|
private void StopRebind()
|
|
{
|
|
isRebinding = false;
|
|
rebindingIndex = -1;
|
|
if (blinkCoroutine != null)
|
|
{
|
|
StopCoroutine(blinkCoroutine);
|
|
blinkCoroutine = null;
|
|
}
|
|
|
|
RefreshButtonLabels();
|
|
}
|
|
|
|
private void RefreshButtonLabels()
|
|
{
|
|
for (int i = 0; i < colors.Length; i++)
|
|
{
|
|
RefreshButtonLabel(i);
|
|
}
|
|
|
|
RefreshButtonLabel(SecondaryTrack3Slot);
|
|
}
|
|
|
|
private void RefreshButtonLabel(int index)
|
|
{
|
|
Text txt = GetButtonText(index);
|
|
if (txt == null) return;
|
|
|
|
txt.text = KeyBindingManager.GetDisplayName(GetKeyForSlot(index));
|
|
}
|
|
|
|
private Button GetButton(int index)
|
|
{
|
|
if (index == SecondaryTrack3Slot) return secondary_track3Button;
|
|
if (keyButtons == null || index < 0 || index >= keyButtons.Length) return null;
|
|
return keyButtons[index];
|
|
}
|
|
|
|
private Text GetButtonText(int index)
|
|
{
|
|
Button button = GetButton(index);
|
|
return button != null ? button.GetComponentInChildren<Text>() : null;
|
|
}
|
|
|
|
private void OnNoteSpeedSliderChanged(float value)
|
|
{
|
|
UpdateNoteSpeedText(value);
|
|
SaveNoteSpeedValue(value);
|
|
}
|
|
|
|
private void OnNoteSpeedSliderPointerUp()
|
|
{
|
|
if (noteSpeedMultipler_slider == null) return;
|
|
PlayerPrefs.SetFloat(NoteSpeedPrefKey, noteSpeedMultipler_slider.value);
|
|
PlayerPrefs.Save();
|
|
}
|
|
|
|
private void UpdateNoteSpeedText(float value)
|
|
{
|
|
if (noteSpeedMultipler_valueText == null) return;
|
|
int percentage = Mathf.RoundToInt(value * 100f);
|
|
noteSpeedMultipler_valueText.text = $"{percentage}%";
|
|
}
|
|
|
|
private void SaveNoteSpeedValue(float value)
|
|
{
|
|
float v = Mathf.Clamp(value, NoteSpeedMin, NoteSpeedMax);
|
|
PlayerPrefs.SetFloat(NoteSpeedPrefKey, v);
|
|
PlayerPrefs.Save();
|
|
}
|
|
|
|
private void ChangeNoteSpeedBy(float delta)
|
|
{
|
|
if (noteSpeedMultipler_slider == null) return;
|
|
|
|
float current = noteSpeedMultipler_slider.value;
|
|
float newValue = current;
|
|
const float eps = 1e-5f;
|
|
|
|
if (delta > 0f)
|
|
{
|
|
float ceilStep = Mathf.Ceil(current / StepAmount) * StepAmount;
|
|
newValue = Mathf.Abs(current - ceilStep) < eps
|
|
? Mathf.Min(current + StepAmount, NoteSpeedMax)
|
|
: Mathf.Min(ceilStep, NoteSpeedMax);
|
|
}
|
|
else if (delta < 0f)
|
|
{
|
|
float floorStep = Mathf.Floor(current / StepAmount) * StepAmount;
|
|
newValue = Mathf.Abs(current - floorStep) < eps
|
|
? Mathf.Max(current - StepAmount, NoteSpeedMin)
|
|
: Mathf.Max(floorStep, NoteSpeedMin);
|
|
}
|
|
|
|
newValue = Mathf.Clamp(newValue, NoteSpeedMin, NoteSpeedMax);
|
|
newValue = (float)Math.Round(newValue, 3);
|
|
noteSpeedMultipler_slider.value = newValue;
|
|
}
|
|
|
|
private void ResetNoteSpeed()
|
|
{
|
|
if (noteSpeedMultipler_slider == null) return;
|
|
noteSpeedMultipler_slider.value = NoteSpeedDefault;
|
|
SaveNoteSpeedValue(NoteSpeedDefault);
|
|
UpdateNoteSpeedText(NoteSpeedDefault);
|
|
}
|
|
|
|
private static void EnsureDefaultNoteSpeedPreference()
|
|
{
|
|
if (PlayerPrefs.HasKey(NoteSpeedPrefKey))
|
|
{
|
|
int version = PlayerPrefs.GetInt(NoteSpeedDefaultVersionKey, 0);
|
|
float saved = PlayerPrefs.GetFloat(NoteSpeedPrefKey, NoteSpeedDefault);
|
|
if (version < NoteSpeedDefaultVersion && Mathf.Approximately(saved, 3f))
|
|
{
|
|
saved = NoteSpeedDefault;
|
|
}
|
|
|
|
saved = Mathf.Clamp(saved, NoteSpeedMin, NoteSpeedMax);
|
|
PlayerPrefs.SetFloat(NoteSpeedPrefKey, saved);
|
|
PlayerPrefs.SetInt(NoteSpeedDefaultVersionKey, NoteSpeedDefaultVersion);
|
|
PlayerPrefs.Save();
|
|
return;
|
|
}
|
|
|
|
PlayerPrefs.SetFloat(NoteSpeedPrefKey, NoteSpeedDefault);
|
|
PlayerPrefs.SetInt(NoteSpeedDefaultVersionKey, NoteSpeedDefaultVersion);
|
|
PlayerPrefs.Save();
|
|
}
|
|
|
|
private void AddButtonContinuousEvents(Button button, float delta)
|
|
{
|
|
EventTrigger trigger = button.gameObject.GetComponent<EventTrigger>();
|
|
if (trigger == null) trigger = button.gameObject.AddComponent<EventTrigger>();
|
|
|
|
EventTrigger.Entry downEntry = new EventTrigger.Entry { eventID = EventTriggerType.PointerDown };
|
|
downEntry.callback.AddListener(_ => StartContinuousChange(delta));
|
|
trigger.triggers.Add(downEntry);
|
|
|
|
EventTrigger.Entry upEntry = new EventTrigger.Entry { eventID = EventTriggerType.PointerUp };
|
|
upEntry.callback.AddListener(_ => StopContinuousChange());
|
|
trigger.triggers.Add(upEntry);
|
|
|
|
EventTrigger.Entry exitEntry = new EventTrigger.Entry { eventID = EventTriggerType.PointerExit };
|
|
exitEntry.callback.AddListener(_ => StopContinuousChange());
|
|
trigger.triggers.Add(exitEntry);
|
|
}
|
|
|
|
private void StartContinuousChange(float delta)
|
|
{
|
|
ChangeNoteSpeedBy(delta);
|
|
if (continuousChangeCoroutine != null) StopCoroutine(continuousChangeCoroutine);
|
|
continuousChangeCoroutine = StartCoroutine(ContinuousChangeRoutine(delta));
|
|
}
|
|
|
|
private void StopContinuousChange()
|
|
{
|
|
if (continuousChangeCoroutine == null) return;
|
|
StopCoroutine(continuousChangeCoroutine);
|
|
continuousChangeCoroutine = null;
|
|
}
|
|
|
|
private IEnumerator ContinuousChangeRoutine(float delta)
|
|
{
|
|
yield return new WaitForSeconds(ContinuousInitialDelay);
|
|
while (true)
|
|
{
|
|
ChangeNoteSpeedBy(delta);
|
|
yield return new WaitForSeconds(ContinuousRepeatRate);
|
|
}
|
|
}
|
|
}
|