修了很多bug和增加功能,优化不少问题
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e093cb75f7bba0747936df55e37b2493
|
||||
AudioImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 8
|
||||
defaultSettings:
|
||||
serializedVersion: 2
|
||||
loadType: 0
|
||||
sampleRateSetting: 0
|
||||
sampleRateOverride: 44100
|
||||
compressionFormat: 1
|
||||
quality: 1
|
||||
conversionMode: 0
|
||||
preloadAudioData: 1
|
||||
platformSettingOverrides: {}
|
||||
forceToMono: 0
|
||||
normalize: 1
|
||||
loadInBackground: 0
|
||||
ambisonic: 0
|
||||
3D: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,669 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using Bansonic;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
public class delayTapperPrefab : MonoBehaviour
|
||||
{
|
||||
[Header("data")]
|
||||
[SerializeField] private float delayAmountSeconds;
|
||||
|
||||
[Header("delay tapper")]
|
||||
public TMP_InputField delayAmountNow;
|
||||
public Slider runSlider;
|
||||
public TMP_InputField bpmInputField;
|
||||
public Button startButton;
|
||||
public Button resetButton;
|
||||
public Button saveButton;
|
||||
public Button quitButton;
|
||||
public TMP_Dropdown modeDropdown;
|
||||
public Image[] modeSpecificImages; // 4个特定的Image
|
||||
|
||||
public Button endiasble_hori;
|
||||
public GameObject hori_object;
|
||||
|
||||
[HideInInspector] public controllerSettings mainSettings;
|
||||
public Button toggle_extra_ui;
|
||||
public GameObject extra_ui_object;
|
||||
|
||||
public enum TapperMode { EightBeats, FourBeats }
|
||||
private TapperMode currentMode = TapperMode.FourBeats;
|
||||
|
||||
public GameObject tapperHandlePrefab;
|
||||
public GameObject tapperHandlePrefab_toput;
|
||||
|
||||
[Header("Audio Playback")]
|
||||
public AudioSource audioSource;
|
||||
public AudioSource hit_sfx_as;
|
||||
public float musicStartTime = 0f;
|
||||
public float musicEndTime = 0f;
|
||||
|
||||
[Header("Tapper Stats")]
|
||||
[SerializeField] public List<string> tapValueOffsets = new List<string>();
|
||||
private float totalOffsetSum = 0f; // 用于计算平均值的累计偏移
|
||||
private int tapCount = 0; // 累计点击次数
|
||||
|
||||
[SerializeField] private int BPM = 120;
|
||||
|
||||
private Coroutine sliderCoroutine;
|
||||
private Coroutine audioCoroutine;
|
||||
private bool isInternalSetting = false;
|
||||
private bool isAudioReady = false;
|
||||
private bool isPaused = false;
|
||||
private bool isRunning = false;
|
||||
private float currentElapsed = 0f;
|
||||
|
||||
private const string DELAY_PREFS_KEY = "UserGlobalDelaySeconds";
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 强制确保 List 已初始化
|
||||
if (tapValueOffsets == null) tapValueOffsets = new List<string>();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (bpmInputField != null)
|
||||
{
|
||||
// 锁定 BPM 为 120 且不可编辑
|
||||
isInternalSetting = true;
|
||||
bpmInputField.text = "120";
|
||||
bpmInputField.interactable = false; // 禁用交互
|
||||
isInternalSetting = false;
|
||||
|
||||
// 监听点击或编辑事件
|
||||
bpmInputField.onSelect.AddListener(delegate { OnInputFieldSelected(); });
|
||||
bpmInputField.onValueChanged.AddListener(OnInputFieldChanged);
|
||||
}
|
||||
|
||||
if (startButton != null)
|
||||
{
|
||||
// 在代码中显式绑定按钮,防止 Inspector 遗漏
|
||||
startButton.onClick.RemoveListener(OnStartButtonClicked);
|
||||
startButton.onClick.AddListener(OnStartButtonClicked);
|
||||
|
||||
// 允许点击按钮
|
||||
startButton.interactable = true;
|
||||
UpdateStartButtonText();
|
||||
}
|
||||
|
||||
if (saveButton != null)
|
||||
{
|
||||
saveButton.onClick.RemoveListener(OnSaveButtonClicked);
|
||||
saveButton.onClick.AddListener(OnSaveButtonClicked);
|
||||
}
|
||||
|
||||
if (quitButton != null)
|
||||
{
|
||||
quitButton.onClick.RemoveListener(OnQuitButtonClicked);
|
||||
quitButton.onClick.AddListener(OnQuitButtonClicked);
|
||||
}
|
||||
|
||||
// 读取保存的延迟值
|
||||
float savedDelay = PlayerPrefs.GetFloat(DELAY_PREFS_KEY, 0f);
|
||||
if (delayAmountNow != null)
|
||||
{
|
||||
isInternalSetting = true;
|
||||
delayAmountNow.text = Mathf.RoundToInt(savedDelay * 1000f).ToString();
|
||||
isInternalSetting = false;
|
||||
}
|
||||
|
||||
if (modeDropdown != null)
|
||||
{
|
||||
modeDropdown.onValueChanged.RemoveListener(OnModeChanged);
|
||||
modeDropdown.onValueChanged.AddListener(OnModeChanged);
|
||||
// 默认设置为四拍模式 (index 1)
|
||||
modeDropdown.value = 1;
|
||||
// 初始化显示状态
|
||||
OnModeChanged(modeDropdown.value);
|
||||
}
|
||||
|
||||
if (delayAmountNow != null)
|
||||
{
|
||||
delayAmountNow.onEndEdit.RemoveListener(OnDelayAmountEndEdit);
|
||||
delayAmountNow.onEndEdit.AddListener(OnDelayAmountEndEdit);
|
||||
}
|
||||
|
||||
if (resetButton != null)
|
||||
{
|
||||
resetButton.onClick.RemoveListener(OnResetButtonClicked);
|
||||
resetButton.onClick.AddListener(OnResetButtonClicked);
|
||||
}
|
||||
|
||||
if (endiasble_hori != null)
|
||||
{
|
||||
endiasble_hori.onClick.RemoveListener(OnEndisableHoriClicked);
|
||||
endiasble_hori.onClick.AddListener(OnEndisableHoriClicked);
|
||||
}
|
||||
|
||||
if (toggle_extra_ui != null)
|
||||
{
|
||||
toggle_extra_ui.onClick.RemoveListener(OnToggleExtraUIClicked);
|
||||
toggle_extra_ui.onClick.AddListener(OnToggleExtraUIClicked);
|
||||
}
|
||||
|
||||
// 开始加载音频
|
||||
StartCoroutine(PrepareAudioAndEnableButton());
|
||||
}
|
||||
|
||||
public void OnResetButtonClicked()
|
||||
{
|
||||
Debug.Log("[delayTapperPrefab] Reset 按钮被点击");
|
||||
|
||||
// 1. 停止滚动和音频
|
||||
StopAndResetTapper();
|
||||
isRunning = false;
|
||||
isPaused = false;
|
||||
currentElapsed = 0f;
|
||||
UpdateStartButtonText();
|
||||
|
||||
// 2. 清空 Prefab 和采样数据
|
||||
ClearTapperHandles();
|
||||
|
||||
// 3. 重置 UI 显示
|
||||
if (delayAmountNow != null)
|
||||
{
|
||||
isInternalSetting = true;
|
||||
delayAmountNow.text = "0";
|
||||
isInternalSetting = false;
|
||||
}
|
||||
|
||||
// 4. 取消按钮焦点
|
||||
if (EventSystem.current != null)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEndisableHoriClicked()
|
||||
{
|
||||
if (hori_object != null)
|
||||
{
|
||||
bool isActive = hori_object.activeSelf;
|
||||
hori_object.SetActive(!isActive);
|
||||
Debug.Log($"[delayTapperPrefab] hori_object state toggled to: {!isActive}");
|
||||
}
|
||||
|
||||
// 取消按钮焦点,防止空格触发重复点击
|
||||
if (EventSystem.current != null)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnToggleExtraUIClicked()
|
||||
{
|
||||
if (extra_ui_object != null)
|
||||
{
|
||||
bool isActive = extra_ui_object.activeSelf;
|
||||
extra_ui_object.SetActive(!isActive);
|
||||
Debug.Log($"[delayTapperPrefab] extra_ui_object state toggled to: {!isActive}");
|
||||
}
|
||||
|
||||
// 取消按钮焦点,防止空格触发重复点击
|
||||
if (EventSystem.current != null)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSaveButtonClicked()
|
||||
{
|
||||
if (delayAmountNow != null && float.TryParse(delayAmountNow.text, out float msValue))
|
||||
{
|
||||
float seconds = msValue / 1000f;
|
||||
PlayerPrefs.SetFloat(DELAY_PREFS_KEY, seconds);
|
||||
PlayerPrefs.Save();
|
||||
gNotice.warning.display($"已保存全局延迟: {msValue}ms ({seconds:F3}s)");
|
||||
Debug.Log($"[delayTapperPrefab] Saved delay to PlayerPrefs: {seconds}s");
|
||||
|
||||
// 刷新父界面的显示
|
||||
if (mainSettings != null)
|
||||
{
|
||||
mainSettings.RefreshDelayText();
|
||||
}
|
||||
}
|
||||
|
||||
if (EventSystem.current != null)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(null);
|
||||
}
|
||||
|
||||
// 保存后销毁物体
|
||||
OnQuitButtonClicked();
|
||||
}
|
||||
|
||||
private void OnQuitButtonClicked()
|
||||
{
|
||||
Debug.Log("[delayTapperPrefab] Quit 按钮被点击,销毁物体");
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
|
||||
private void UpdateStartButtonText()
|
||||
{
|
||||
if (startButton == null) return;
|
||||
|
||||
Text btnText = startButton.GetComponentInChildren<Text>();
|
||||
if (btnText == null)
|
||||
{
|
||||
TMP_Text tmpText = startButton.GetComponentInChildren<TMP_Text>();
|
||||
if (tmpText != null)
|
||||
{
|
||||
if (!isRunning) tmpText.text = "开始测试";
|
||||
else if (isPaused) tmpText.text = "继续";
|
||||
else tmpText.text = "暂停";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isRunning) btnText.text = "开始测试";
|
||||
else if (isPaused) btnText.text = "继续";
|
||||
else btnText.text = "暂停";
|
||||
}
|
||||
|
||||
private void OnStartButtonClicked()
|
||||
{
|
||||
if (!isAudioReady)
|
||||
{
|
||||
gNotice.warning.display("音频未就绪");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isRunning)
|
||||
{
|
||||
// 第一次开始
|
||||
isRunning = true;
|
||||
isPaused = false;
|
||||
currentElapsed = 0f;
|
||||
StartTapperFromInput();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 切换暂停/继续
|
||||
isPaused = !isPaused;
|
||||
if (isPaused)
|
||||
{
|
||||
// 暂停
|
||||
if (audioSource != null && audioSource.isPlaying) audioSource.Pause();
|
||||
Debug.Log("[delayTapperPrefab] 暂停测试");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 继续
|
||||
if (audioSource != null) audioSource.UnPause();
|
||||
Debug.Log("[delayTapperPrefab] 继续测试");
|
||||
}
|
||||
}
|
||||
|
||||
UpdateStartButtonText();
|
||||
|
||||
if (EventSystem.current != null)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDelayAmountEndEdit(string input)
|
||||
{
|
||||
if (isInternalSetting) return;
|
||||
|
||||
if (int.TryParse(input, out int value))
|
||||
{
|
||||
if (value < -1000 || value > 1000)
|
||||
{
|
||||
int clampedValue = Mathf.Clamp(value, -1000, 1000);
|
||||
gNotice.warning.display($"延迟值超出范围 (-1000~1000),已重置为 {clampedValue}");
|
||||
isInternalSetting = true;
|
||||
delayAmountNow.text = clampedValue.ToString();
|
||||
isInternalSetting = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gNotice.warning.display("非法的延迟值输入,已重置为 0");
|
||||
isInternalSetting = true;
|
||||
delayAmountNow.text = "0";
|
||||
isInternalSetting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnModeChanged(int index)
|
||||
{
|
||||
// 0 = 8拍模式, 1 = 4拍模式 (由 Dropdown 设置顺序决定)
|
||||
currentMode = (index == 1) ? TapperMode.FourBeats : TapperMode.EightBeats;
|
||||
|
||||
// 如果是 4 拍模式,将 4 张图 alpha 设置为 0
|
||||
if (modeSpecificImages != null)
|
||||
{
|
||||
foreach (var img in modeSpecificImages)
|
||||
{
|
||||
if (img != null)
|
||||
{
|
||||
Color c = img.color;
|
||||
c.a = (currentMode == TapperMode.FourBeats) ? 0f : 1f;
|
||||
img.color = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[delayTapperPrefab] 模式切换:{currentMode}");
|
||||
|
||||
// 切换模式时重置数据
|
||||
StopAndResetTapper();
|
||||
ClearTapperHandles();
|
||||
}
|
||||
|
||||
private IEnumerator PrepareAudioAndEnableButton()
|
||||
{
|
||||
isAudioReady = false;
|
||||
if (audioSource == null || audioSource.clip == null)
|
||||
{
|
||||
Debug.LogWarning("[delayTapperPrefab] 无音频源或片段");
|
||||
isAudioReady = true;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 如果音频还未加载,则手动触发加载
|
||||
if (audioSource.clip.loadState != AudioDataLoadState.Loaded)
|
||||
{
|
||||
Debug.Log($"[delayTapperPrefab] 正在加载音频: {audioSource.clip.name}...");
|
||||
audioSource.clip.LoadAudioData();
|
||||
|
||||
// 等待加载完成
|
||||
while (audioSource.clip.loadState == AudioDataLoadState.Loading)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (audioSource.clip.loadState != AudioDataLoadState.Loaded)
|
||||
{
|
||||
Debug.LogError($"[delayTapperPrefab] 音频加载失败! 状态: {audioSource.clip.loadState}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[delayTapperPrefab] 音频加载成功");
|
||||
isAudioReady = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[delayTapperPrefab] 音频已在内存中");
|
||||
isAudioReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInputFieldSelected()
|
||||
{
|
||||
if (isInternalSetting) return;
|
||||
Debug.Log("[delayTapperPrefab] InputField 被选中,停止播放并重置");
|
||||
StopAndResetTapper();
|
||||
}
|
||||
|
||||
private void OnInputFieldChanged(string val)
|
||||
{
|
||||
if (isInternalSetting) return;
|
||||
Debug.Log("[delayTapperPrefab] InputField 内容改变,停止播放并重置");
|
||||
StopAndResetTapper();
|
||||
}
|
||||
|
||||
public void StartTapperFromInput()
|
||||
{
|
||||
Debug.Log("[delayTapperPrefab] StartTapperFromInput 按钮被点击");
|
||||
|
||||
// 0. 点击后立刻取消按钮焦点,防止空格触发重复点击
|
||||
if (EventSystem.current != null)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(null);
|
||||
}
|
||||
|
||||
if (!isAudioReady)
|
||||
{
|
||||
gNotice.warning.display("音频未就绪");
|
||||
return;
|
||||
}
|
||||
|
||||
// 强制锁定 BPM 为 120
|
||||
BPM = 120;
|
||||
if (bpmInputField != null)
|
||||
{
|
||||
isInternalSetting = true;
|
||||
bpmInputField.text = "120";
|
||||
isInternalSetting = false;
|
||||
}
|
||||
|
||||
// 点击启动按钮时,手动清理一次旧数据
|
||||
ClearTapperHandles();
|
||||
|
||||
RunTapperProcess();
|
||||
StartAudioPlayback();
|
||||
}
|
||||
|
||||
private void StartAudioPlayback()
|
||||
{
|
||||
if (audioSource == null || audioSource.clip == null)
|
||||
{
|
||||
Debug.LogWarning("[delayTapperPrefab] AudioSource 或 AudioClip 未分配,跳过播放");
|
||||
return;
|
||||
}
|
||||
|
||||
// 停止当前可能正在运行的播放协程
|
||||
if (audioCoroutine != null) StopCoroutine(audioCoroutine);
|
||||
|
||||
// 强制停止当前音频并直接跳转到指定时间,确保没有重音或残留播放
|
||||
audioSource.Stop();
|
||||
float start = Mathf.Clamp(musicStartTime, 0f, audioSource.clip.length);
|
||||
audioSource.time = start;
|
||||
|
||||
audioCoroutine = StartCoroutine(AudioPlaybackRoutine());
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 如果暂停,不执行任何 Update 逻辑
|
||||
if (isPaused) return;
|
||||
|
||||
// 改进监听逻辑:只要 Slider 正在滚动,就允许记录空格
|
||||
if (sliderCoroutine != null && Input.GetKeyDown(KeyCode.Space))
|
||||
{
|
||||
SpawnTapperHandle();
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnTapperHandle()
|
||||
{
|
||||
Debug.Log("[delayTapperPrefab] SpawnTapperHandle 被调用");
|
||||
if (runSlider == null) { Debug.LogError("runSlider 为空"); return; }
|
||||
|
||||
// 1. 直接基于当前时间(currentElapsed)计算偏移,不再依赖 Slider 的百分比,提高精度
|
||||
float beatDuration = 60f / Mathf.Max(BPM, 1); // 每拍时长 (120BPM 为 0.5s)
|
||||
float nearestBeatTime = Mathf.Round(currentElapsed / beatDuration) * beatDuration;
|
||||
float offsetSeconds = currentElapsed - nearestBeatTime;
|
||||
|
||||
// 2. 记录差值到 List (Inspector 显示用,保留 ms 字符串格式)
|
||||
string offsetStr = (offsetSeconds * 1000f).ToString("F2") + "ms";
|
||||
tapValueOffsets.Add(offsetStr);
|
||||
|
||||
// 3. 计算平均偏移逻辑
|
||||
tapCount++;
|
||||
totalOffsetSum += offsetSeconds;
|
||||
float averageOffsetSeconds = totalOffsetSum / tapCount;
|
||||
int finalDelayMs = Mathf.RoundToInt(averageOffsetSeconds * 1000f);
|
||||
|
||||
// 4. 输出到 delayAmountNow
|
||||
if (delayAmountNow != null)
|
||||
{
|
||||
isInternalSetting = true;
|
||||
delayAmountNow.text = finalDelayMs.ToString();
|
||||
isInternalSetting = false;
|
||||
}
|
||||
|
||||
Debug.Log($"[delayTapperPrefab] 模式={currentMode}, 点击#{tapCount}: Time={currentElapsed:F3}s, NearestBeat={nearestBeatTime:F3}s, Offset={offsetSeconds*1000f:F2}ms, AvgDelay={finalDelayMs}ms");
|
||||
|
||||
if (tapperHandlePrefab == null || tapperHandlePrefab_toput == null) return;
|
||||
|
||||
// 获取 Slider 的 Handle
|
||||
RectTransform handle = runSlider.handleRect;
|
||||
if (handle == null) return;
|
||||
|
||||
// 实例化到指定父物体下
|
||||
GameObject newHandle = Instantiate(tapperHandlePrefab, tapperHandlePrefab_toput.transform);
|
||||
|
||||
// 同步 Handle 的位置
|
||||
newHandle.transform.position = handle.position;
|
||||
newHandle.transform.rotation = handle.rotation;
|
||||
|
||||
// 播放点击音效
|
||||
if (hit_sfx_as != null)
|
||||
{
|
||||
hit_sfx_as.Play();
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearTapperHandles()
|
||||
{
|
||||
Debug.Log("[delayTapperPrefab] ClearTapperHandles 被显式调用,即将清空 List 和累计数据");
|
||||
if (tapperHandlePrefab_toput == null) return;
|
||||
|
||||
int childCount = tapperHandlePrefab_toput.transform.childCount;
|
||||
for (int i = childCount - 1; i >= 0; i--)
|
||||
{
|
||||
Destroy(tapperHandlePrefab_toput.transform.GetChild(i).gameObject);
|
||||
}
|
||||
|
||||
// 重置所有累计数据
|
||||
tapValueOffsets.Clear();
|
||||
totalOffsetSum = 0f;
|
||||
tapCount = 0;
|
||||
|
||||
Debug.Log("[delayTapperPrefab] 已手动清理 TapperHandles 和所有采样数据");
|
||||
}
|
||||
|
||||
private IEnumerator AudioPlaybackRoutine()
|
||||
{
|
||||
// 再次确保开始时间不超过结束时间且在音频长度范围内
|
||||
float start = Mathf.Clamp(musicStartTime, 0f, audioSource.clip.length);
|
||||
float end = Mathf.Clamp(musicEndTime, start, audioSource.clip.length);
|
||||
|
||||
if (end <= start)
|
||||
{
|
||||
Debug.LogWarning("[delayTapperPrefab] 音乐结束时间小于或等于开始时间,无法播放");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 此时直接开始播放即可,因为在 StartAudioPlayback 中已经跳转过时间
|
||||
audioSource.Play();
|
||||
Debug.Log($"[delayTapperPrefab] 音频开始播放(跳转):{start}s -> {end}s");
|
||||
|
||||
while (audioSource.isPlaying && audioSource.time < end)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
audioSource.Stop();
|
||||
Debug.Log("[delayTapperPrefab] 音频播放结束,停止 Slider 并清理数据");
|
||||
|
||||
// 重置运行状态和按钮文本
|
||||
isRunning = false;
|
||||
isPaused = false;
|
||||
UpdateStartButtonText();
|
||||
|
||||
// 音频停止时,同时停止并重置 Slider
|
||||
StopAndResetTapper();
|
||||
|
||||
// 清理所有数据和 Prefab
|
||||
ClearTapperHandles();
|
||||
}
|
||||
|
||||
public void StopAndResetTapper()
|
||||
{
|
||||
if (sliderCoroutine != null)
|
||||
{
|
||||
Debug.Log("[delayTapperPrefab] 停止当前滚动协程");
|
||||
StopCoroutine(sliderCoroutine);
|
||||
sliderCoroutine = null;
|
||||
}
|
||||
|
||||
if (audioCoroutine != null)
|
||||
{
|
||||
StopCoroutine(audioCoroutine);
|
||||
audioCoroutine = null;
|
||||
}
|
||||
|
||||
if (audioSource != null && audioSource.isPlaying)
|
||||
{
|
||||
audioSource.Stop();
|
||||
}
|
||||
|
||||
if (runSlider != null)
|
||||
{
|
||||
runSlider.value = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void RunTapperProcess()
|
||||
{
|
||||
Debug.Log("[delayTapperPrefab] RunTapperProcess 准备启动协程");
|
||||
if (sliderCoroutine != null) StopCoroutine(sliderCoroutine);
|
||||
sliderCoroutine = StartCoroutine(RunTapperProcessCoroutine());
|
||||
}
|
||||
|
||||
private IEnumerator RunTapperProcessCoroutine()
|
||||
{
|
||||
if (runSlider == null)
|
||||
{
|
||||
Debug.LogError("[delayTapperPrefab] runSlider 为空,无法开始滚动!");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 强制确保 Slider 的范围是 0 到 1
|
||||
runSlider.minValue = 0f;
|
||||
runSlider.maxValue = 1f;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// 每次循环开始时清理视觉 Prefab (如果不是第一次启动)
|
||||
// 注意:这里我们只清理生成的 Handle,不重置数据,因为数据是跨循环累计的
|
||||
ClearVisualHandlesOnly();
|
||||
|
||||
// 如果不是暂停后继续,则重置 currentElapsed
|
||||
if (!isPaused) currentElapsed = 0f;
|
||||
|
||||
// 8拍模式走完 8 拍,4拍模式走完 4 拍
|
||||
int beatsInCycle = (currentMode == TapperMode.FourBeats) ? 4 : 8;
|
||||
float cycleDuration = (60f / Mathf.Max(BPM, 1)) * beatsInCycle;
|
||||
|
||||
Debug.Log($"[delayTapperPrefab] 开始 {beatsInCycle} 拍滚动,BPM: {BPM}, 单次循环时长: {cycleDuration}s");
|
||||
|
||||
while (currentElapsed < cycleDuration)
|
||||
{
|
||||
if (!isPaused)
|
||||
{
|
||||
currentElapsed += Time.deltaTime;
|
||||
runSlider.value = Mathf.Clamp01(currentElapsed / cycleDuration);
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
|
||||
currentElapsed = 0f;
|
||||
runSlider.value = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearVisualHandlesOnly()
|
||||
{
|
||||
if (tapperHandlePrefab_toput == null) return;
|
||||
int childCount = tapperHandlePrefab_toput.transform.childCount;
|
||||
for (int i = childCount - 1; i >= 0; i--)
|
||||
{
|
||||
Destroy(tapperHandlePrefab_toput.transform.GetChild(i).gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public void on_close_img()
|
||||
{
|
||||
gNotice.recommendation.display("打开了!但是没有完全打开!", 2f, 2f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a26c6b23f9ba1a94c88e8d116aabbf8b
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08012d5c72488f746bcbfd620fff0c31
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &17151476098217447
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1534408947502825999}
|
||||
- component: {fileID: 6208739441775674782}
|
||||
- component: {fileID: 2261012931051897728}
|
||||
m_Layer: 0
|
||||
m_Name: tapperHandlePrefab
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1534408947502825999
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 17151476098217447}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: -1.2461243, y: 29.666}
|
||||
m_SizeDelta: {x: 5.8462, y: 41.802}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &6208739441775674782
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 17151476098217447}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &2261012931051897728
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 17151476098217447}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.28773582, g: 0.7711722, b: 1, a: 0.49019608}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 21300000, guid: 7f3b1220317607e44bbb8f0e1998044a, type: 3}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 7
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39b925655673e8c449bac36513b550db
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user