571 lines
19 KiB
C#
571 lines
19 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.SceneManagement;
|
||
using System.IO;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using UnityEngine.UI;
|
||
using TMPro;
|
||
using UnityEngine.Rendering;
|
||
using UnityEngine.Rendering.Universal;
|
||
using Bansonic;
|
||
|
||
public class pressStart : MonoBehaviour
|
||
{
|
||
public string sceneName_toNextScene = "selectYourSongFirst";
|
||
public string testModeSceneName = "gamePlay_gamePlay";
|
||
[SerializeField] private string banflag_filePath = "C:/ProgramData/Bansonic/Caches/Bansonic_TestOutput";
|
||
private bool banflag_exists;
|
||
|
||
public static string testmode_audioFile_path;
|
||
public static string testmode_bgPicFile_path;
|
||
public static string testmode_beatmapJSON_path;
|
||
|
||
// new: store banflag content for other scenes to read
|
||
public static string banflagContent = string.Empty;
|
||
|
||
// UI text to show status
|
||
public Text statusText;
|
||
public Text warningText;
|
||
|
||
[Header("New UI Buttons")]
|
||
public Button startButton;
|
||
public Button helpButton;
|
||
public Button closeButton;
|
||
public Button exitButton;
|
||
public GameObject helpContentObject;
|
||
|
||
[Header("Fade Settings")]
|
||
public CanvasGroup mainCanvasGroup;
|
||
public float fadeDelay = 0f;
|
||
public float fadeDuration = 1f;
|
||
|
||
[Header("TMP Flashing Settings")]
|
||
public TextMeshProUGUI flashingText;
|
||
public float flashInterval = 0.5f;
|
||
|
||
[Header("Start Sequence Settings")]
|
||
public Volume globalVolume;
|
||
public Image fadeOutImage;
|
||
public float startButtonDelay = 2f; // UI starts showing after 2 seconds; allow clicking then.
|
||
|
||
[Header("UI_UI Jump Blocker")]
|
||
public bool blockUIUISceneJump = false;
|
||
public string blockUIUISceneJumpMessage = "暂不可进入。";
|
||
|
||
// polling
|
||
private Coroutine pollCoroutine;
|
||
private float pollInterval = 3f;
|
||
|
||
// Start is called before the first frame update
|
||
void Start()
|
||
{
|
||
// sceneName_toNextScene = "Songs_Select";
|
||
banflag_exists = false;
|
||
testmode_audioFile_path = string.Empty;
|
||
testmode_bgPicFile_path = string.Empty;
|
||
testmode_beatmapJSON_path = string.Empty;
|
||
banflagContent = string.Empty;
|
||
|
||
// Ensure status text shows idle
|
||
UpdateStatus(LocalizationService.Get("press_start.idle", "Press Enter to continue."), Color.green);
|
||
|
||
// Bind buttons
|
||
if (startButton != null)
|
||
{
|
||
startButton.onClick.AddListener(OnStartButtonClick);
|
||
}
|
||
if (helpButton != null)
|
||
{
|
||
helpButton.onClick.AddListener(OnHelpButtonClick);
|
||
}
|
||
if (closeButton != null)
|
||
{
|
||
closeButton.onClick.AddListener(OnCloseButtonClick);
|
||
}
|
||
if (exitButton != null)
|
||
{
|
||
exitButton.onClick.AddListener(OnExitButtonClick);
|
||
}
|
||
if (helpContentObject != null)
|
||
{
|
||
helpContentObject.SetActive(false);
|
||
}
|
||
|
||
// Initialize fadeOutImage
|
||
if (fadeOutImage != null)
|
||
{
|
||
Color c = fadeOutImage.color;
|
||
c.a = 0f;
|
||
fadeOutImage.color = c;
|
||
fadeOutImage.gameObject.SetActive(false);
|
||
}
|
||
|
||
// Initialize Global Volume Bloom intensity to 0
|
||
if (globalVolume != null && globalVolume.profile.TryGet<Bloom>(out var bloom))
|
||
{
|
||
bloom.intensity.Override(0f);
|
||
}
|
||
|
||
// Initialize CanvasGroup and start fade-in
|
||
if (mainCanvasGroup != null)
|
||
{
|
||
mainCanvasGroup.alpha = 0f;
|
||
StartCoroutine(FadeInMainCanvasGroup());
|
||
}
|
||
|
||
// Start flashing logic
|
||
if (flashingText != null)
|
||
{
|
||
StartCoroutine(FlashTMPText());
|
||
}
|
||
|
||
// Start button delay logic
|
||
if (startButton != null)
|
||
{
|
||
StartCoroutine(StartButtonActivationDelay());
|
||
}
|
||
}
|
||
|
||
private IEnumerator StartButtonActivationDelay()
|
||
{
|
||
if (startButton == null) yield break;
|
||
|
||
// 初始禁用按钮
|
||
startButton.interactable = false;
|
||
|
||
// Keep the click lock aligned with when the main UI starts appearing.
|
||
float activationDelay = Mathf.Max(0f, startButtonDelay);
|
||
if (mainCanvasGroup != null)
|
||
{
|
||
activationDelay = Mathf.Min(activationDelay, Mathf.Max(0f, fadeDelay));
|
||
}
|
||
|
||
if (activationDelay > 0f)
|
||
{
|
||
yield return new WaitForSecondsRealtime(activationDelay);
|
||
}
|
||
|
||
// 启用按钮
|
||
startButton.interactable = true;
|
||
Debug.Log("[pressStart] Start button is now active.");
|
||
}
|
||
|
||
private IEnumerator FlashTMPText()
|
||
{
|
||
if (flashingText == null) yield break;
|
||
|
||
float elapsed = 0f;
|
||
while (true)
|
||
{
|
||
elapsed += Time.deltaTime;
|
||
// 使用 Mathf.PingPong 在 0 到 1 之间往复
|
||
// 除以 flashInterval 可以控制循环的速度
|
||
float alpha = Mathf.PingPong(elapsed / flashInterval, 1f);
|
||
|
||
Color color = flashingText.color;
|
||
color.a = alpha;
|
||
flashingText.color = color;
|
||
|
||
yield return null;
|
||
}
|
||
}
|
||
|
||
private IEnumerator FadeInMainCanvasGroup()
|
||
{
|
||
if (fadeDelay > 0)
|
||
yield return new WaitForSeconds(fadeDelay);
|
||
|
||
float elapsed = 0f;
|
||
while (elapsed < fadeDuration)
|
||
{
|
||
elapsed += Time.deltaTime;
|
||
mainCanvasGroup.alpha = Mathf.Lerp(0f, 1f, elapsed / fadeDuration);
|
||
yield return null;
|
||
}
|
||
mainCanvasGroup.alpha = 1f;
|
||
}
|
||
|
||
private void OnStartButtonClick()
|
||
{
|
||
Debug.Log("[pressStart] Start button clicked.");
|
||
HandleGameStartLogic();
|
||
}
|
||
|
||
private void OnHelpButtonClick()
|
||
{
|
||
if (helpContentObject != null)
|
||
{
|
||
bool isActive = helpContentObject.activeSelf;
|
||
helpContentObject.SetActive(!isActive);
|
||
Debug.Log("[pressStart] Help button clicked. Help content active: " + !isActive);
|
||
}
|
||
}
|
||
|
||
private void OnCloseButtonClick()
|
||
{
|
||
if (helpContentObject != null)
|
||
{
|
||
helpContentObject.SetActive(false);
|
||
Debug.Log("[pressStart] Close button clicked. Help content hidden.");
|
||
}
|
||
}
|
||
|
||
private void OnExitButtonClick()
|
||
{
|
||
Debug.Log("[pressStart] Exit button clicked. Application quitting.");
|
||
#if UNITY_EDITOR
|
||
UnityEditor.EditorApplication.isPlaying = false;
|
||
#else
|
||
Application.Quit();
|
||
#endif
|
||
}
|
||
|
||
// Update is called once per frame
|
||
private bool check_enterNextScene = false;
|
||
private static readonly string WARNING_TEXT_CONTENT = "<color=orange>当前处于测试阶段</color><color=red>未授权版本</color><color=orange>版本号:</color><color=red>v1.0.0</color><color=orange>,请注意风险。</color>";
|
||
|
||
void Update()
|
||
{
|
||
if (!(Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter)))
|
||
return;
|
||
|
||
// 如果按钮未激活 (还在倒计时),不允许通过键盘进入
|
||
if (startButton != null && !startButton.interactable)
|
||
{
|
||
Debug.Log("[pressStart] Start request ignored. Button is not active yet.");
|
||
return;
|
||
}
|
||
|
||
HandleGameStartLogic();
|
||
}
|
||
|
||
private void HandleGameStartLogic()
|
||
{
|
||
bool requestTestMode = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||
if (requestTestMode)
|
||
{
|
||
Debug.Log("[pressStart] Enter+Shift or button start requested, starting test mode polling.");
|
||
GameConfig.testMode = true;
|
||
if (pollCoroutine != null) StopCoroutine(pollCoroutine);
|
||
pollCoroutine = StartCoroutine(PollForBanflagAndProceed());
|
||
return;
|
||
}
|
||
|
||
// 修改为点击一次即可开始进入游戏序列
|
||
Debug.Log("[pressStart] Start requested, initiating transition sequence.");
|
||
GameConfig.testMode = false;
|
||
|
||
if (blockUIUISceneJump)
|
||
{
|
||
string message = string.IsNullOrWhiteSpace(blockUIUISceneJumpMessage)
|
||
? "暂不可进入。"
|
||
: blockUIUISceneJumpMessage;
|
||
gNotice.error.display(message);
|
||
Debug.LogWarning("[pressStart] UI_UI scene jump blocked: " + message);
|
||
return;
|
||
}
|
||
|
||
if (warningText != null)
|
||
{
|
||
warningText.gameObject.SetActive(true);
|
||
warningText.text = LocalizationService.Get("press_start.warning_content", WARNING_TEXT_CONTENT);
|
||
}
|
||
|
||
// 启动进入游戏的异步序列
|
||
StartCoroutine(StartGameTransitionSequence());
|
||
}
|
||
|
||
private IEnumerator StartGameTransitionSequence()
|
||
{
|
||
// 1. 0.5秒内将 Global Volume 的 Bloom Intensity 从 0 变到 3
|
||
if (globalVolume != null && globalVolume.profile.TryGet<Bloom>(out var bloom))
|
||
{
|
||
float elapsed = 0f;
|
||
float duration = 0.5f;
|
||
float startVal = bloom.intensity.value;
|
||
float targetVal = 3f;
|
||
while (elapsed < duration)
|
||
{
|
||
elapsed += Time.deltaTime;
|
||
bloom.intensity.Override(Mathf.Lerp(startVal, targetVal, elapsed / duration));
|
||
yield return null;
|
||
}
|
||
bloom.intensity.Override(targetVal);
|
||
}
|
||
|
||
// 2. 结束变 3 后,将 Image 的不透明度在 0.5 秒内从 0 变到 255 (alpha = 1)
|
||
if (fadeOutImage != null)
|
||
{
|
||
fadeOutImage.gameObject.SetActive(true);
|
||
float elapsed = 0f;
|
||
float duration = 0.5f;
|
||
Color color = fadeOutImage.color;
|
||
while (elapsed < duration)
|
||
{
|
||
elapsed += Time.deltaTime;
|
||
color.a = Mathf.Lerp(0f, 1f, elapsed / duration);
|
||
fadeOutImage.color = color;
|
||
yield return null;
|
||
}
|
||
color.a = 1f;
|
||
fadeOutImage.color = color;
|
||
}
|
||
|
||
// 3. 切换场景
|
||
Debug.Log("[pressStart] Transition sequence complete. Loading UI_UI scene.");
|
||
Time.timeScale = 1f;
|
||
if (!gTransition.LoadScene("UI_UI", LoadSceneMode.Single))
|
||
{
|
||
SceneManager.LoadScene("UI_UI");
|
||
}
|
||
}
|
||
|
||
private IEnumerator PollForBanflagAndProceed()
|
||
{
|
||
UpdateStatus(LocalizationService.Get("press_start.waiting_banflag", "Waiting for banflag..."), new Color(1f, 0.5f, 0f)); // orange
|
||
|
||
while (true)
|
||
{
|
||
bool found = check_banflag_testBanflag();
|
||
if (found)
|
||
{
|
||
UpdateStatus(LocalizationService.Get("press_start.loading_test_mode", "Banflag detected. Loading test mode..."), Color.green);
|
||
// small delay to show status
|
||
yield return new WaitForSeconds(0.5f);
|
||
if (gTransition.LoadScene(testModeSceneName, LoadSceneMode.Single))
|
||
{
|
||
while (gTransition.IsBusy)
|
||
{
|
||
yield return null;
|
||
}
|
||
yield break;
|
||
}
|
||
|
||
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(testModeSceneName);
|
||
while (asyncLoad != null && !asyncLoad.isDone)
|
||
{
|
||
yield return null;
|
||
}
|
||
yield break;
|
||
}
|
||
else
|
||
{
|
||
UpdateStatus(LocalizationService.Get("press_start.waiting_banflag", "Waiting for banflag..."), new Color(1f, 0.5f, 0f)); // orange
|
||
}
|
||
|
||
yield return new WaitForSeconds(pollInterval);
|
||
}
|
||
}
|
||
|
||
private IEnumerator LoadSceneAsync(string sceneName)
|
||
{
|
||
if (gTransition.LoadScene(sceneName, LoadSceneMode.Single))
|
||
{
|
||
while (gTransition.IsBusy)
|
||
{
|
||
yield return null;
|
||
}
|
||
yield break;
|
||
}
|
||
|
||
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName);
|
||
while (asyncLoad != null && !asyncLoad.isDone)
|
||
{
|
||
yield return null;
|
||
}
|
||
}
|
||
|
||
private void UpdateStatus(string message, Color color)
|
||
{
|
||
if (statusText != null)
|
||
{
|
||
statusText.text = message;
|
||
statusText.color = color;
|
||
}
|
||
Debug.Log(message);
|
||
}
|
||
|
||
private string NormalizeToForwardSlashes(string path)
|
||
{
|
||
if (string.IsNullOrEmpty(path)) return string.Empty;
|
||
try
|
||
{
|
||
// Get full absolute path to remove any relative segments
|
||
string full = Path.GetFullPath(path);
|
||
// Replace backslashes with forward slashes
|
||
string fwd = full.Replace('\\', '/');
|
||
// Collapse multiple slashes into single
|
||
string collapsed = Regex.Replace(fwd, "/{2,}", "/");
|
||
// Remove trailing slash except when path is root like "C:/"
|
||
if (collapsed.Length > 3 && collapsed.EndsWith("/"))
|
||
collapsed = collapsed.TrimEnd('/');
|
||
return collapsed;
|
||
}
|
||
catch
|
||
{
|
||
// Fallback: replace backslashes and collapse
|
||
string f = path.Replace('\\', '/');
|
||
string c = Regex.Replace(f, "/{2,}", "/");
|
||
if (c.Length > 3 && c.EndsWith("/")) c = c.TrimEnd('/');
|
||
return c;
|
||
}
|
||
}
|
||
|
||
// returns true if banflag detected
|
||
private bool check_banflag_testBanflag()
|
||
{
|
||
try
|
||
{
|
||
// reset previous results
|
||
banflag_exists = false;
|
||
testmode_audioFile_path = string.Empty;
|
||
testmode_bgPicFile_path = string.Empty;
|
||
testmode_beatmapJSON_path = string.Empty;
|
||
banflagContent = string.Empty;
|
||
|
||
if (string.IsNullOrEmpty(banflag_filePath))
|
||
{
|
||
Debug.LogWarning("banflag_filePath 未设置");
|
||
UpdateStatus(LocalizationService.Get("press_start.error_path_not_configured", "Error: banflag path not configured"), Color.red);
|
||
return false;
|
||
}
|
||
|
||
string filePath = Path.Combine(banflag_filePath, "test.banflag");
|
||
if (!File.Exists(filePath))
|
||
{
|
||
Debug.LogWarning($"未找到对应 banflag 文件: {filePath}");
|
||
return false;
|
||
}
|
||
|
||
// mark exists
|
||
banflag_exists = true;
|
||
|
||
byte[] data = File.ReadAllBytes(filePath);
|
||
if (data == null || data.Length == 0)
|
||
{
|
||
Debug.LogWarning($"banflag 文件为空: {filePath}");
|
||
}
|
||
else
|
||
{
|
||
// Documentation text normalized.
|
||
string text = null;
|
||
try
|
||
{
|
||
text = Encoding.UTF8.GetString(data);
|
||
}
|
||
catch
|
||
{
|
||
text = null;
|
||
}
|
||
|
||
bool looksLikeText = false;
|
||
if (!string.IsNullOrEmpty(text))
|
||
{
|
||
// Documentation text normalized.
|
||
int nonPrintable = 0;
|
||
int checkLen = Mathf.Min(256, text.Length);
|
||
for (int i = 0; i < checkLen; i++)
|
||
{
|
||
char c = text[i];
|
||
if (char.IsControl(c) && c != '\r' && c != '\n' && c != '\t') nonPrintable++;
|
||
}
|
||
looksLikeText = (nonPrintable == 0);
|
||
}
|
||
|
||
if (looksLikeText)
|
||
{
|
||
Debug.Log($"已读取 banflag 文件: {filePath}\n内容:\n{text}");
|
||
// store the readable content for other scenes
|
||
banflagContent = text;
|
||
}
|
||
else
|
||
{
|
||
// Documentation text normalized.
|
||
string base64 = System.Convert.ToBase64String(data);
|
||
Debug.Log($"已读取 banflag 文件(二进制): {filePath},长度={data.Length} bytes,Base64 前200字符预览:\n{(base64.Length > 200 ? base64.Substring(0,200) + "..." : base64)}");
|
||
banflagContent = $"[Binary Data] Length: {data.Length} bytes";
|
||
}
|
||
}
|
||
|
||
// Documentation text normalized.
|
||
string dir = Path.GetDirectoryName(filePath);
|
||
if (string.IsNullOrEmpty(dir)) dir = banflag_filePath;
|
||
|
||
// find JSON
|
||
string foundJson = string.Empty;
|
||
try
|
||
{
|
||
string[] jsons = Directory.GetFiles(dir, "*.json", SearchOption.TopDirectoryOnly);
|
||
if (jsons != null && jsons.Length > 0)
|
||
{
|
||
foundJson = jsons[0];
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
// find audio: mp3, wav, ogg (in that priority)
|
||
string foundAudio = string.Empty;
|
||
try
|
||
{
|
||
string[] exts = new string[] { "*.mp3", "*.wav", "*.ogg" };
|
||
foreach (var pat in exts)
|
||
{
|
||
string[] matches = Directory.GetFiles(dir, pat, SearchOption.TopDirectoryOnly);
|
||
if (matches != null && matches.Length > 0)
|
||
{
|
||
foundAudio = matches[0];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
// find image: jpg/jpeg, png (priority jpg then png)
|
||
string foundImage = string.Empty;
|
||
try
|
||
{
|
||
string[] imgExts = new string[] { "*.jpg", "*.jpeg", "*.png" };
|
||
foreach (var pat in imgExts)
|
||
{
|
||
string[] matches = Directory.GetFiles(dir, pat, SearchOption.TopDirectoryOnly);
|
||
if (matches != null && matches.Length > 0)
|
||
{
|
||
foundImage = matches[0];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
catch { }
|
||
|
||
// normalize and assign
|
||
testmode_beatmapJSON_path = string.IsNullOrEmpty(foundJson) ? string.Empty : NormalizeToForwardSlashes(foundJson);
|
||
testmode_audioFile_path = string.IsNullOrEmpty(foundAudio) ? string.Empty : NormalizeToForwardSlashes(foundAudio);
|
||
testmode_bgPicFile_path = string.IsNullOrEmpty(foundImage) ? string.Empty : NormalizeToForwardSlashes(foundImage);
|
||
|
||
// Debug the normalized paths
|
||
Debug.Log($"banflag_exists={banflag_exists}");
|
||
Debug.Log($"testmode_beatmapJSON_path = {testmode_beatmapJSON_path}");
|
||
Debug.Log($"testmode_audioFile_path = {testmode_audioFile_path}");
|
||
Debug.Log($"testmode_bgPicFile_path = {testmode_bgPicFile_path}");
|
||
|
||
UpdateStatus(LocalizationService.Get("press_start.banflag_found", "Banflag found"), Color.green);
|
||
|
||
return banflag_exists;
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
Debug.LogError($"读取 banflag 文件时发生错误: {ex}");
|
||
UpdateStatus(LocalizationService.Get("press_start.error_reading_banflag", "Error reading banflag"), Color.red);
|
||
// ensure flags cleared on error
|
||
banflag_exists = false;
|
||
testmode_audioFile_path = string.Empty;
|
||
testmode_bgPicFile_path = string.Empty;
|
||
testmode_beatmapJSON_path = string.Empty;
|
||
banflagContent = string.Empty;
|
||
return false;
|
||
}
|
||
}
|
||
}
|