加入了浮动小游戏功能和设置界面
运行时请手动修改运行库目录fdBrowser
This commit is contained in:
@@ -209,7 +209,7 @@ public class HoldNote : BaseNote
|
||||
{
|
||||
Debug.Log($"[HoldNote] START段超时自动Miss(自动): {noteColor}");
|
||||
|
||||
// 只做 Miss 结果展示与登记
|
||||
// register as missed
|
||||
JudgeManager.Instance.RegisterStartJudged(noteID, false);
|
||||
|
||||
InputManager.Instance?.ShowJudgeResult(trackIndex, "Miss");
|
||||
@@ -255,7 +255,7 @@ public class HoldNote : BaseNote
|
||||
float maxWindow = (judgeConfig?.missRange ?? 0.5f) * holdWindowMultiplier;
|
||||
if (Mathf.Abs(pressTimeLocal - hitTime) <= maxWindow)
|
||||
{
|
||||
HandleStart();
|
||||
HandleStart(false);
|
||||
isJudged = true; // mark judged to avoid duplicates
|
||||
}
|
||||
else
|
||||
@@ -269,7 +269,7 @@ public class HoldNote : BaseNote
|
||||
// legacy fallback (shouldn't normally hit because above handles Start)
|
||||
if (Input.GetKeyDown(keyToPress))
|
||||
{
|
||||
HandleStart();
|
||||
HandleStart(false);
|
||||
isJudged = true; // 无论成功失败都标记为已判定,避免重复尝试
|
||||
}
|
||||
}
|
||||
@@ -348,7 +348,7 @@ public class HoldNote : BaseNote
|
||||
{
|
||||
Debug.Log($"[HoldNote] Start段离开判定线未判定,补偿 Miss 并回收: {noteColor}");
|
||||
// 确保未判定的 Start 段在离开判定线时登记为 Miss
|
||||
HandleStart();
|
||||
HandleStart(true);
|
||||
isJudged = true;
|
||||
ReturnToPool();
|
||||
}
|
||||
@@ -422,44 +422,52 @@ public class HoldNote : BaseNote
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStart()
|
||||
private void HandleStart(bool forceMiss = false)
|
||||
{
|
||||
if (!JudgeManager.Instance.TryResolveStart(noteID))
|
||||
{
|
||||
Debug.Log($"[HoldNote] START 已被判定,跳过: {noteColor}");
|
||||
return;
|
||||
}
|
||||
|
||||
// If forced miss (e.g. leaving judge zone without press), register miss immediately
|
||||
if (forceMiss)
|
||||
{
|
||||
Debug.Log($"[HoldNote] START 强制 Miss (未按下): {noteColor}");
|
||||
ScoreManager.Instance.countMiss += 1;
|
||||
JudgeManager.Instance.RegisterStartJudged(noteID, false);
|
||||
isHoldActive = false;
|
||||
InputManager.Instance?.ShowJudgeResult(trackIndex, "Miss");
|
||||
JudgeSoundManager.Instance?.PlayJudgeSound("Miss");
|
||||
teamUIController.Instance?.OnJudgeResult("Miss");
|
||||
return;
|
||||
}
|
||||
|
||||
float pressTime = Time.time;
|
||||
float rawOffsetMs = (hitTime - pressTime) * 1000f;
|
||||
float offset = Mathf.Abs(pressTime - hitTime);
|
||||
string result;
|
||||
|
||||
// compute scaled judgement windows for hold note start
|
||||
// further scale windows according to visualSpeedMultiplier so slow visuals get more leniency
|
||||
float visualScaleFactor = Mathf.Clamp(visualSpeedMultiplier, 0.5f, 2f);
|
||||
float pRange = (judgeConfig?.perfectRange ?? 0.1f) * holdWindowMultiplier * visualScaleFactor;
|
||||
float gRange = (judgeConfig?.greatRange ?? 0.2f) * holdWindowMultiplier * visualScaleFactor;
|
||||
float gdRange = (judgeConfig?.goodRange ?? 0.3f) * holdWindowMultiplier * visualScaleFactor;
|
||||
// evaluate Start judgement using scaled windows
|
||||
|
||||
if (offset <= pRange)
|
||||
{
|
||||
result = "Perfect";
|
||||
ScoreManager.Instance.countPerfect += 1;
|
||||
if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs;
|
||||
Debug.Log($"[HoldNote] START判定成功(偏差={offset:F2}秒): {noteColor}");
|
||||
JudgeManager.Instance.RegisterStartJudged(noteID, true);
|
||||
isHoldActive = true; // 成功判定Start,长按状态激活
|
||||
isHoldActive = true;
|
||||
PlayHitAnimation();
|
||||
AnimationController.Global?.StartHoldParticles(noteColor);
|
||||
|
||||
// Trigger skills ... (unchanged)
|
||||
}
|
||||
else if (offset <= gRange)
|
||||
{
|
||||
result = "Great";
|
||||
ScoreManager.Instance.countGreat += 1;
|
||||
if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs;
|
||||
Debug.Log($"[HoldNote] START判定 Great(偏差={offset:F2}秒): {noteColor}");
|
||||
JudgeManager.Instance.RegisterStartJudged(noteID, true);
|
||||
isHoldActive = true;
|
||||
PlayHitAnimation();
|
||||
@@ -470,7 +478,6 @@ public class HoldNote : BaseNote
|
||||
result = "Good";
|
||||
ScoreManager.Instance.countGood += 1;
|
||||
if (noteData != null) noteData.judgeOffsetMs = rawOffsetMs;
|
||||
Debug.Log($"[HoldNote] START判定 Good(偏差={offset:F2}秒): {noteColor}");
|
||||
JudgeManager.Instance.RegisterStartJudged(noteID, true);
|
||||
isHoldActive = true;
|
||||
PlayHitAnimation();
|
||||
@@ -479,14 +486,9 @@ public class HoldNote : BaseNote
|
||||
else
|
||||
{
|
||||
result = "Miss";
|
||||
if (JudgeManager.Instance.TryResolveStart(noteID))
|
||||
{
|
||||
ScoreManager.Instance.countMiss += 1;
|
||||
}
|
||||
Debug.Log($"[HoldNote] START Miss(偏差={offset:F2}秒): {noteColor}");
|
||||
ScoreManager.Instance.countMiss += 1;
|
||||
JudgeManager.Instance.RegisterStartJudged(noteID, false);
|
||||
isHoldActive = false;
|
||||
|
||||
try
|
||||
{
|
||||
bool triggered = SkillBuilder.Instance?.NotifyNoteHit(this.trackIndex, result, SkillDefinition.NoteTypeTrigger.Tap, this.noteID) ?? false;
|
||||
|
||||
@@ -49,6 +49,12 @@ public class InputManager : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Ensure UI shows current bindings from KeyBindingManager/PlayerPrefs
|
||||
RefreshKeyLabels();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
foreach (string color in new string[] { "red", "green", "yellow", "purple", "blue" })
|
||||
@@ -87,6 +93,20 @@ public class InputManager : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh displayed labels for key bindings (called after rebind)
|
||||
public void RefreshKeyLabels()
|
||||
{
|
||||
string[] colors = new string[] { "red", "green", "yellow", "purple", "blue" };
|
||||
if (trackKeyTexts == null) return;
|
||||
for (int i = 0; i < colors.Length && i < trackKeyTexts.Length; i++)
|
||||
{
|
||||
var txt = trackKeyTexts[i];
|
||||
if (txt == null) continue;
|
||||
KeyCode k = KeyBindingManager.GetKeyForColor(colors[i]);
|
||||
txt.text = KeyBindingManager.GetDisplayName(k);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetIndexForColor(string color)
|
||||
{
|
||||
// 保持与 trackJudgeTexts 相同的索引映射
|
||||
|
||||
@@ -4,71 +4,114 @@ using System.Collections.Generic;
|
||||
public class KeyBindingManager : MonoBehaviour
|
||||
{
|
||||
private static Dictionary<string, KeyCode> keyBindings = new Dictionary<string, KeyCode>();
|
||||
private static readonly string[] colors = { "red", "green", "yellow", "purple", "blue" };
|
||||
private static readonly KeyCode[] defaultKeys = { KeyCode.D, KeyCode.F, KeyCode.Space, KeyCode.J, KeyCode.K };
|
||||
private static bool initialized = false;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Debug.Log("KeyBindingManager Awake() 被调用,开始加载按键映射...");
|
||||
|
||||
if (keyBindings.Count == 0)
|
||||
if (!initialized)
|
||||
{
|
||||
LoadKeyBindings();
|
||||
}
|
||||
Debug.Log("KeyBindingManager Awake() 被调用,开始加载按键映射...");
|
||||
}
|
||||
|
||||
/// <summary> 获取颜色对应的按键 </summary>
|
||||
public static KeyCode GetKeyForColor(string color)
|
||||
{
|
||||
if (keyBindings.TryGetValue(color.ToLower(), out KeyCode key))
|
||||
if (!initialized)
|
||||
{
|
||||
LoadKeyBindings();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(color)) return KeyCode.None;
|
||||
var keyLower = color.ToLower();
|
||||
if (keyBindings.TryGetValue(keyLower, out KeyCode key))
|
||||
{
|
||||
return key;
|
||||
}
|
||||
Debug.LogError($"未找到颜色 {color} 对应的按键!请检查 KeyBindingManager 是否正确初始化。");
|
||||
// fallback: try to map by index order
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
if (colors[i] == keyLower)
|
||||
{
|
||||
return defaultKeys[i];
|
||||
}
|
||||
}
|
||||
return KeyCode.None;
|
||||
}
|
||||
|
||||
/// <summary> 修改按键绑定 </summary>
|
||||
public static void ChangeKeyBinding(string color, KeyCode newKey)
|
||||
{
|
||||
if (keyBindings.ContainsKey(color.ToLower()))
|
||||
{
|
||||
keyBindings[color.ToLower()] = newKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyBindings.Add(color.ToLower(), newKey);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(color)) return;
|
||||
var keyLower = color.ToLower();
|
||||
keyBindings[keyLower] = newKey;
|
||||
SaveKeyBindings();
|
||||
}
|
||||
|
||||
/// <summary> 存储按键绑定到 `PlayerPrefs` </summary>
|
||||
private static void SaveKeyBindings()
|
||||
{
|
||||
foreach (var kvp in keyBindings)
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
PlayerPrefs.SetInt($"KeyBinding_{kvp.Key}", (int)kvp.Value);
|
||||
var col = colors[i];
|
||||
KeyCode k = defaultKeys[i];
|
||||
if (keyBindings.TryGetValue(col, out KeyCode stored)) k = stored;
|
||||
PlayerPrefs.SetInt($"KeyBinding_{col}", (int)k);
|
||||
}
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
/// <summary> 从 `PlayerPrefs` 加载按键绑定 </summary>
|
||||
private static void LoadKeyBindings()
|
||||
{
|
||||
string[] colors = { "red", "green", "yellow", "purple", "blue" };
|
||||
KeyCode[] defaultKeys = { KeyCode.D, KeyCode.F, KeyCode.Space, KeyCode.J, KeyCode.K };
|
||||
|
||||
keyBindings.Clear();
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
if (PlayerPrefs.HasKey($"KeyBinding_{colors[i]}"))
|
||||
string col = colors[i];
|
||||
if (PlayerPrefs.HasKey($"KeyBinding_{col}"))
|
||||
{
|
||||
keyBindings[colors[i]] = (KeyCode)PlayerPrefs.GetInt($"KeyBinding_{colors[i]}");
|
||||
keyBindings[col] = (KeyCode)PlayerPrefs.GetInt($"KeyBinding_{col}");
|
||||
}
|
||||
else
|
||||
{
|
||||
keyBindings[colors[i]] = defaultKeys[i];
|
||||
keyBindings[col] = defaultKeys[i];
|
||||
}
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
Debug.Log("KeyBindings 初始化成功:" + string.Join(", ", keyBindings));
|
||||
}
|
||||
|
||||
// Return a human-friendly display string for a KeyCode (symbols shown as their character)
|
||||
public static string GetDisplayName(KeyCode key)
|
||||
{
|
||||
if (key == KeyCode.Space) return "Space";
|
||||
switch (key)
|
||||
{
|
||||
case KeyCode.Quote: return "'"; // single quote
|
||||
case KeyCode.Semicolon: return ";";
|
||||
case KeyCode.Comma: return ",";
|
||||
case KeyCode.Period: return ".";
|
||||
case KeyCode.Slash: return "/";
|
||||
case KeyCode.Backslash: return "\\";
|
||||
case KeyCode.LeftBracket: return "[";
|
||||
case KeyCode.RightBracket: return "]";
|
||||
case KeyCode.Minus: return "-";
|
||||
case KeyCode.Equals: return "=";
|
||||
case KeyCode.BackQuote: return "`";
|
||||
case KeyCode.Keypad0: return "Num0";
|
||||
case KeyCode.Keypad1: return "Num1";
|
||||
case KeyCode.Keypad2: return "Num2";
|
||||
case KeyCode.Keypad3: return "Num3";
|
||||
case KeyCode.Keypad4: return "Num4";
|
||||
case KeyCode.Keypad5: return "Num5";
|
||||
case KeyCode.Keypad6: return "Num6";
|
||||
case KeyCode.Keypad7: return "Num7";
|
||||
case KeyCode.Keypad8: return "Num8";
|
||||
case KeyCode.Keypad9: return "Num9";
|
||||
default:
|
||||
// For letters and named keys, use ToString(); for better readability, split "Alpha0" -> "0"
|
||||
string s = key.ToString();
|
||||
if (s.StartsWith("Alpha") && s.Length > 5) return s.Substring(5);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,17 @@ public class NoteSpawner : MonoBehaviour
|
||||
|
||||
private static int holdNoteIdCounter = 0; // 全局唯一长音符 ID 计数器
|
||||
|
||||
private const string NoteSpeedPrefKey = "noteSpeedMultiplier";
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Load saved visual speed multiplier before any spawning logic uses it
|
||||
float saved = PlayerPrefs.GetFloat(NoteSpeedPrefKey, speedMultiplier);
|
||||
saved = Mathf.Clamp(saved, SpeedMultiplierMin, SpeedMultiplierMax);
|
||||
speedMultiplier = saved;
|
||||
if (GameConfig.verboseLogs) Debug.Log($"[NoteSpawner] Loaded speedMultiplier={speedMultiplier} from PlayerPrefs");
|
||||
}
|
||||
|
||||
public void LoadBeatmap(Beatmap loadedBeatmap)
|
||||
{
|
||||
if (isSpawning) return;
|
||||
|
||||
Reference in New Issue
Block a user