original ver 25/06/01

This commit is contained in:
GardeniaRabbit
2025-06-02 00:48:08 +08:00
committed by FloatGaming
commit 89e03899f0
1065 changed files with 183622 additions and 0 deletions
@@ -0,0 +1,55 @@
using UnityEngine;
public class AnimationController : MonoBehaviour
{
public GameObject redEffect; // 红色击打动画对象
public GameObject greenEffect; // 绿色击打动画对象
public GameObject yellowEffect; // 黄色击打动画对象
public GameObject purpleEffect; // 紫色击打动画对象
public GameObject blueEffect; // 蓝色击打动画对象
private Animator redAnimator;
private Animator greenAnimator;
private Animator yellowAnimator;
private Animator purpleAnimator;
private Animator blueAnimator;
private void Awake()
{
// 获取每个动画对象的 Animator 组件
redAnimator = redEffect.GetComponent<Animator>();
greenAnimator = greenEffect.GetComponent<Animator>();
yellowAnimator = yellowEffect.GetComponent<Animator>();
purpleAnimator = purpleEffect.GetComponent<Animator>();
blueAnimator = blueEffect.GetComponent<Animator>();
}
public void PlayDestroyAnimation(string color)
{
switch (color)
{
case "red":
// 重置之前的触发器
redAnimator.ResetTrigger("PlayRedDestroy");
redAnimator.SetTrigger("PlayRedDestroy");
break;
case "green":
greenAnimator.ResetTrigger("PlayGreenDestroy");
greenAnimator.SetTrigger("PlayGreenDestroy");
break;
case "yellow":
yellowAnimator.ResetTrigger("PlayYellowDestroy");
yellowAnimator.SetTrigger("PlayYellowDestroy");
break;
case "purple":
purpleAnimator.ResetTrigger("PlayPurpleDestroy");
purpleAnimator.SetTrigger("PlayPurpleDestroy");
break;
case "blue":
blueAnimator.ResetTrigger("PlayBlueDestroy");
blueAnimator.SetTrigger("PlayBlueDestroy");
break;
}
}
}
@@ -0,0 +1,16 @@
fileFormatVersion: 2
guid: 8c9d972a8c1b8cd42836f774bb018416
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- redEffect: {instanceID: 0}
- greenEffect: {instanceID: 0}
- yellowEffect: {instanceID: 0}
- purpleEffect: {instanceID: 0}
- blueEffect: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
using UnityEngine;
public abstract class BaseNote : MonoBehaviour
{
public int TrackIndex { get; protected set; }
public string NoteColor { get; protected set; }
public float Speed { get; protected set; }
protected float hitTime;
public virtual void Initialize(int trackIndex, string noteColor, float speed, float hitTime)
{
TrackIndex = trackIndex;
NoteColor = noteColor;
Speed = speed;
this.hitTime = hitTime;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1685ef287fcd6ee449c4bc269594b792
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
using System;
using UnityEngine;
[Serializable]
public class NoteData
{
public int trackIndex; // 轨道索引
public float time; // 音符出现时间(秒)
public string color; // 音符颜色(如 "red", "blue"
public string type; // 音符类型("tap" = 单击,"hold" = 长按)
public float length; // 音符长度(仅用于长按音符,单位:秒)
}
[Serializable]
public class TrackData
{
public string color; // 轨道颜色
}
[Serializable]
public class Beatmap
{
public string title; // 歌曲名
public string composer; // 作曲家
public string illustrator; // 画师
public string charter; // 谱师
public string beatmapId; // 谱面自定编号
public float duration; // 歌曲时长(秒)
public float bpm; // BPM
public int difficulty; // 难度(数字)
public string difficultyName;// 难度代号(如 "Easy", "Hard", "Expert"
public string createdDate; // 谱面创建日期(字符串格式:yyyy-MM-dd)
public string musicFile; // 音乐文件路径
public string backgroundFile;// 曲绘文件路径
public TrackData[] tracks; // 轨道信息(每个轨道的颜色)
public NoteData[] notes; // 音符列表
public bool is_official = true;
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4938dcd8ed83be145a4ee5e19e480fab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,131 @@
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class BeatmapManager : MonoBehaviour
{
public Beatmap beatmap; // 当前谱面数据
// 音符生成器引用
public NoteSpawner noteSpawner;
// 保存谱面数据到 JSON 文件
//public void SaveBeatmap(string fileName)
//{
// string json = JsonUtility.ToJson(beatmap, true); // 格式化 JSON 输出
// File.WriteAllText(Application.dataPath + "/" + fileName, json);
// Debug.Log("谱面已保存:" + Application.dataPath + "/" + fileName);
//}
// 从 JSON 文件加载谱面数据
public void LoadBeatmap(string fileName)
{
string path = Application.streamingAssetsPath + "/" + fileName;
if (File.Exists(path))
{
string json = File.ReadAllText(path);
beatmap = JsonUtility.FromJson<Beatmap>(json);
Debug.Log("谱面已加载:" + json);
// 在加载后立即调用音符生成
noteSpawner.LoadBeatmap(beatmap);
}
else
{
Debug.LogError("文件不存在:" + path);
}
}
public void LoadBeatmap(Beatmap loadedBeatmap)
{
beatmap = loadedBeatmap;
Debug.Log("谱面已加载:" + beatmap.title);
// 在加载后立即调用音符生成
noteSpawner.LoadBeatmap(beatmap);
}
//public void LoadBeatmapFromFile(string fileName)
//{
// string path = Application.persistentDataPath + "/" + fileName;
// if (File.Exists(path))
// {
// string json = File.ReadAllText(path);
// Beatmap beatmap = JsonUtility.FromJson<Beatmap>(json);
// beatmapManager.LoadBeatmap(beatmap); // 使用 LoadBeatmap 方法传递 Beatmap 对象
// }
// else
// {
// Debug.LogError("文件不存在:" + path);
// }
//}
// 创建一个示例谱面并保存
//public void CreateSampleBeatmap()
//{
// beatmap = new Beatmap
// {
// title = "Moonlight Sonata",
// composer = "Ludwig van Beethoven",
// illustrator = "John Doe",
// charter = "Jane Smith",
// beatmapId = "ML-001",
// duration = 120.5f,
// bpm = 128f,
// difficulty = 5,
// difficultyName = "Hard",
// createdDate = System.DateTime.Now.ToString("yyyy-MM-dd"),
// musicFile = "moonlight_sonata.mp3",
// backgroundFile = "moonlight_art.png",
// tracks = new TrackData[]
// {
// new TrackData { color = "red" },
// new TrackData { color = "green" },
// new TrackData { color = "yellow" },
// new TrackData { color = "purple" },
// new TrackData { color = "blue" }
// },
// notes = new NoteData[]
// {
// new NoteData { trackIndex = 0, time = 1.0f, color = "red", type = "tap", length = 0.0f },
// new NoteData { trackIndex = 4, time = 2.0f, color = "blue", type = "tap", length = 0.0f },
// new NoteData { trackIndex = 1, time = 3.5f, color = "green", type = "tap", length = 0.0f },
// new NoteData { trackIndex = 2, time = 3.5f, color = "yellow", type = "tap", length = 0.0f },
// new NoteData { trackIndex = 2, time = 3.5f, color = "yellow", type = "tap", length = 0.0f },
// new NoteData { trackIndex = 3, time = 3.5f, color = "purple", type = "tap", length = 0.0f },
// }
// };
// SaveBeatmap("test_beatmap.json");
//}
// 读取并加载当前谱面到音符生成器
public void LoadBeatmapFromFile()
{
string path = Application.streamingAssetsPath + "/Emilia_demo.json";
if (File.Exists(path))
{
string json = File.ReadAllText(path);
beatmap = JsonUtility.FromJson<Beatmap>(json);
noteSpawner.LoadBeatmap(beatmap);
}
else
{
Debug.LogError("谱面文件未找到!");
}
}
// 在 Start() 方法中初始化
void Start()
{
// 这里可以选择在启动时创建一个示例谱面并加载
//CreateSampleBeatmap(); // 创建并保存一个示例谱面
LoadBeatmap("Emilia_demo.json"); // 加载并实例化音符
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c80308880c1feca40bcf476848142e2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,59 @@
using System.IO;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public BeatmapManager beatmapManager; // 负责加载和管理谱面
public NoteSpawner noteSpawner; // 音符生成器
public AudioSource musicSource; // 音乐播放器
void Start()
{
Debug.Log("GameManager Start() 被调用");
// 检查关键组件是否为空
if (beatmapManager == null) Debug.LogError("beatmapManager 未赋值!");
if (noteSpawner == null) Debug.LogError("noteSpawner 未赋值!");
if (musicSource == null) Debug.LogError("musicSource 未赋值!");
// 在游戏开始时加载并初始化谱面
string beatmapFilePath = Path.Combine(Application.streamingAssetsPath, "Emilia_demo.json");
if (!File.Exists(beatmapFilePath))
{
Debug.LogError($"谱面文件不存在: {beatmapFilePath}");
return;
}
string json = File.ReadAllText(beatmapFilePath);
Beatmap beatmap = JsonUtility.FromJson<Beatmap>(json); // 解析 JSON 为 Beatmap 对象
if (beatmap == null)
{
Debug.LogError("谱面解析失败,JSON 格式可能错误!");
return;
}
beatmapManager.LoadBeatmap(beatmap); // 传递给 BeatmapManager
noteSpawner.LoadBeatmap(beatmap); // 传递给 NoteSpawner
// 播放背景音乐
if (!string.IsNullOrEmpty(beatmap.musicFile))
{
AudioClip musicClip = Resources.Load<AudioClip>(beatmap.musicFile);
if (musicClip == null)
{
Debug.LogError($"音乐文件加载失败: {beatmap.musicFile}");
}
else
{
musicSource.clip = musicClip;
//musicSource.Play();
}
}
else
{
Debug.LogError("谱面中 musicFile 为空!");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a3cb3ef9478edd4a8b6718a11a0fc3c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GlobalInputManager : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55c02b3cb1ce2094990e79779b31a131
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,320 @@
using System.Collections;
using UnityEngine;
public enum NoteSegment { None, Start, Middle, End }
[RequireComponent(typeof(HoldNoteController))]
public class HoldNote : BaseNote
{
private KeyCode keyToPress = KeyCode.None;
private string noteColor = string.Empty;
private string noteID = string.Empty;
private NoteSegment segment = NoteSegment.None;
private float hitTime = 0f;
private float releaseTime = 0f;
private bool hasReleased = true;
private bool hasEnteredLine = false;
private Coroutine autoReturnCoroutine;
private HoldNoteController controller;
public int id;
public int trackIndex;
public float speed;
public float startTime;
public float delay;
public bool isEnd;
public float scheduledEndTime;
public string color;
public KeyCode key;
public string type; // "start", "middle", or "end"
private void Awake()
{
controller = GetComponent<HoldNoteController>();
}
private void OnEnable()
{
ResetState();
}
public void Setup(int id, int trackIndex, float speed, float time, float delay,
bool isEnd, float scheduledEnd, string color, KeyCode key, string type)
{
this.id = id;
this.trackIndex = trackIndex;
this.speed = speed;
this.startTime = time;
this.delay = delay;
this.isEnd = isEnd;
this.scheduledEndTime = scheduledEnd;
this.color = color;
this.key = key;
this.type = type;
this.noteID = id.ToString();
this.keyToPress = key;
this.noteColor = color;
this.hitTime = time + delay;
this.segment = (delay == 0f) ? NoteSegment.Start :
(isEnd ? NoteSegment.End : NoteSegment.Middle);
this.hasEnteredLine = false;
this.hasReleased = false;
JudgeManager.Instance?.RegisterNoteReleased(noteID, false);
if (segment == NoteSegment.Start)
JudgeManager.Instance?.RegisterStartJudged(noteID, false);
if (segment == NoteSegment.End)
JudgeManager.Instance?.RegisterScheduledEndTime(noteID, scheduledEndTime);
controller?.SetSpeed(speed);
controller?.SetSegmentDelay(delay);
}
private void Update()
{
if (segment == NoteSegment.Start && Input.GetKeyDown(keyToPress))
{
if (hasEnteredLine)
HandleStart();
}
else if (segment == NoteSegment.End && Input.GetKeyUp(keyToPress))
{
HandleEnd();
}
if (segment == NoteSegment.Middle && hasEnteredLine)
{
if (JudgeManager.Instance.IsStartJudged(noteID) &&
!JudgeManager.Instance.HasNoteReleased(noteID) &&
Input.GetKey(keyToPress))
{
// 不再立即回收,由协程等待 hitTime 再回收
}
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (!collision.CompareTag("JudgmentLine")) return;
if (segment == NoteSegment.Start || segment == NoteSegment.Middle)
{
hasEnteredLine = true;
}
if (segment == NoteSegment.Middle)
{
Debug.Log($"[HoldNote] Middle段进入判定线: color={noteColor}, isStartJudged={JudgeManager.Instance.IsStartJudged(noteID)}, hasReleased={JudgeManager.Instance.HasNoteReleased(noteID)}, isKeyDown={Input.GetKey(keyToPress)}");
if (autoReturnCoroutine != null)
StopCoroutine(autoReturnCoroutine);
autoReturnCoroutine = StartCoroutine(DelayedAutoReturnCheck());
}
if (segment == NoteSegment.End)
{
if (autoReturnCoroutine != null)
StopCoroutine(autoReturnCoroutine);
autoReturnCoroutine = StartCoroutine(DelayedAutoReturnCheck());
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (!collision.CompareTag("JudgmentLine")) return;
if (segment == NoteSegment.Start)
{
if (!JudgeManager.Instance.IsStartJudged(noteID))
{
ReturnToPool();
}
}
if (segment == NoteSegment.Middle)
{
if (!JudgeManager.Instance.IsStartJudged(noteID) ||
JudgeManager.Instance.HasNoteReleased(noteID))
{
Debug.Log($"[HoldNote] Middle段离开判定线强制回收: {noteColor}");
ReturnToPool();
}
}
else if (segment == NoteSegment.End)
{
if (!JudgeManager.Instance.IsStartJudged(noteID) || JudgeManager.Instance.HasNoteReleased(noteID))
{
ReturnToPool();
}
}
}
private IEnumerator DelayedAutoReturnCheck()
{
yield return null;
bool isStartJudgedBefore = JudgeManager.Instance.IsStartJudged(noteID);
bool isKeyHeldNow = Input.GetKey(keyToPress);
Debug.Log($"[HoldNote] 延迟检查开始: noteID={noteID}, color={noteColor}, isStartJudged={isStartJudgedBefore}, isKeyHeld={isKeyHeldNow}");
if (!isStartJudgedBefore && isKeyHeldNow)
{
Debug.Log($"[HoldNote] 延迟回补 Start 判定触发: {noteColor}");
HandleStart();
}
bool isStartJudged = JudgeManager.Instance.IsStartJudged(noteID);
bool hasReleased = JudgeManager.Instance.HasNoteReleased(noteID);
bool isKeyHeld = Input.GetKey(keyToPress);
Debug.Log($"[HoldNote] 延迟确认状态: isStartJudged={isStartJudged}, hasReleased={hasReleased}, isKeyHeld={isKeyHeld}");
if (segment == NoteSegment.Middle && isStartJudged && isKeyHeld)
{
float waitTime = Mathf.Max(0f, hitTime - Time.time);
Debug.Log($"[HoldNote] Middle段通过延迟确认,准备等待到 hitTime 回收: {noteColor}, 剩余时间={waitTime:F2}s");
yield return new WaitForSeconds(waitTime);
if (Input.GetKey(keyToPress) && !JudgeManager.Instance.HasNoteReleased(noteID))
{
Debug.Log($"[HoldNote] Middle段在 hitTime 达成条件,回收: {noteColor}");
ReturnToPool();
}
else
{
Debug.Log($"[HoldNote] Middle段在 hitTime 条件不符,不回收: {noteColor}");
}
}
else if (segment == NoteSegment.End && !hasReleased)
{
Debug.Log($"[HoldNote] End段延迟检查后仍未释放: {noteColor}");
}
else
{
Debug.Log($"[HoldNote] DelayedAutoReturnCheck 无需处理: segment={segment}, color={noteColor}");
}
}
private void HandleStart()
{
float pressTime = Time.time;
float offset = Mathf.Abs(pressTime - hitTime);
if (offset < 0.1f)
{
Debug.Log($"[HoldNote] START判定成功(偏差={offset:F2}秒): {noteColor}");
JudgeManager.Instance.RegisterStartJudged(noteID, true);
}
else
{
Debug.Log($"[HoldNote] START Miss(偏差={offset:F2}秒): {noteColor}");
}
StartCoroutine(DelayedReturn());
}
private IEnumerator DelayedReturn()
{
yield return new WaitForSeconds(0.05f);
ReturnToPool();
}
private void HandleEnd()
{
releaseTime = Time.time;
hasReleased = true;
JudgeManager.Instance.RegisterNoteReleased(noteID, true);
float diff = Mathf.Abs(releaseTime - scheduledEndTime);
string result;
if (releaseTime >= scheduledEndTime)
{
result = "Perfect";
}
else if (diff < 0.1f)
{
result = "Perfect";
}
else if (diff < 0.5f)
{
result = "Great";
}
else
{
result = "Good";
}
Debug.Log($"[HoldNote] END判定结果: {noteColor} {result} (release={releaseTime:F2}, target={scheduledEndTime:F2})");
ReturnToPool();
}
private void OnDisable()
{
if (autoReturnCoroutine != null)
{
StopCoroutine(autoReturnCoroutine);
autoReturnCoroutine = null;
}
if (segment == NoteSegment.End &&
!hasReleased &&
!JudgeManager.Instance.HasNoteReleased(noteID) &&
keyToPress != KeyCode.None &&
!Input.GetKey(keyToPress))
{
hasReleased = true;
JudgeManager.Instance.RegisterNoteReleased(noteID, true);
Debug.Log($"[HoldNote] OnDisable补偿KeyUp: {noteColor}");
}
controller?.StopMovement();
}
private void ReturnToPool()
{
if (!gameObject.activeSelf) return;
if (segment == NoteSegment.Start && !hasEnteredLine)
{
Debug.LogWarning($"[HoldNote] 阻止回收尚未进入判定线的 Start段: {noteColor}");
return;
}
gameObject.SetActive(false);
if (segment == NoteSegment.Start)
NotePool.Instance.ReturnStartNote(gameObject, noteColor);
else
NotePool.Instance.ReturnHoldNoteSegment(gameObject, noteColor);
}
public void ResetState()
{
hasReleased = false;
segment = NoteSegment.None;
keyToPress = KeyCode.None;
noteColor = string.Empty;
noteID = string.Empty;
hitTime = 0f;
scheduledEndTime = 0f;
hasEnteredLine = false;
if (autoReturnCoroutine != null)
{
StopCoroutine(autoReturnCoroutine);
autoReturnCoroutine = null;
}
controller?.StopMovement();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86977693d5a2d364bb086303f60ca1b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,69 @@
using UnityEngine;
using System;
public class HoldNoteController : MonoBehaviour
{
private bool isInsideJudgeZone = false;
public event Action<bool> OnJudgeZoneChanged;
private bool isMoving = false;
private float speed = 0f; // 音符下落速度
private float activationTime; // 用于延迟启用下落
private void Update()
{
// **控制音符下落**
if (!isMoving && Time.time >= activationTime)
{
isMoving = true;
}
if (isMoving)
{
transform.Translate(Vector3.down * speed * Time.deltaTime);
}
}
/// <summary>
/// 设置音符延迟下落的时间
/// </summary>
public void SetSegmentDelay(float segmentDelay)
{
activationTime = Time.time + segmentDelay;
isMoving = false;
}
/// <summary>
/// 设置音符的下落速度
/// </summary>
public void SetSpeed(float newSpeed)
{
speed = newSpeed;
}
/// <summary>
/// 停止音符下落(当音符被击中或Miss时)
/// </summary>
public void StopMovement()
{
isMoving = false;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (!collision.CompareTag("JudgmentLine")) return;
isInsideJudgeZone = true;
OnJudgeZoneChanged?.Invoke(true);
}
private void OnTriggerExit2D(Collider2D collision)
{
if (!collision.CompareTag("JudgmentLine")) return;
isInsideJudgeZone = false;
OnJudgeZoneChanged?.Invoke(false);
}
public bool IsInsideJudgArea()
{
return isInsideJudgeZone;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d40c6ec60e52049429740ffb978efab5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,26 @@
using UnityEngine;
using System;
public class InputManager : MonoBehaviour
{
public static event Action<KeyCode> OnKeyPressed;
public static event Action<KeyCode> OnKeyReleased;
private void Update()
{
foreach (string color in new string[] { "red", "green", "yellow", "purple", "blue" })
{
KeyCode key = KeyBindingManager.GetKeyForColor(color);
if (key == KeyCode.None) continue;
if (Input.GetKeyDown(key))
{
OnKeyPressed?.Invoke(key);
// 调用 JudgeManager 统一判断最早的音符
JudgeManager.Instance.JudgeEarliestNote(key);
}
if (Input.GetKeyUp(key))
OnKeyReleased?.Invoke(key);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2c4f957042317641bf529c141c39b91
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,122 @@
using System.Collections.Generic;
using UnityEngine;
public class JudgeManager : MonoBehaviour
{
public static JudgeManager Instance { get; private set; }
private Dictionary<KeyCode, Queue<Note>> judgeQueues = new Dictionary<KeyCode, Queue<Note>>();
private Dictionary<string, bool> startJudgedNotes = new Dictionary<string, bool>();
private Dictionary<string, bool> releasedNotes = new Dictionary<string, bool>();
// 记录 End 段的 scheduledEndTime(防止 End 被回收后仍然能判定)
private Dictionary<string, float> noteEndTimes = new Dictionary<string, float>();
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
return;
}
}
/// <summary>
/// 记录某个颜色的长音符 End 段的 scheduledEndTime
/// </summary>
public void RegisterScheduledEndTime(string noteColor, float endTime)
{
noteEndTimes[noteColor] = endTime;
}
/// <summary>
/// 获取某个颜色的 End 段的 scheduledEndTime
/// </summary>
public float GetScheduledEndTime(string noteColor)
{
return noteEndTimes.ContainsKey(noteColor) ? noteEndTimes[noteColor] : 0f;
}
public void RegisterStartJudged(string noteID, bool state)
{
startJudgedNotes[noteID] = state;
}
public bool IsStartJudged(string noteID)
{
return startJudgedNotes.ContainsKey(noteID) && startJudgedNotes[noteID];
}
public void RegisterNoteReleased(string noteID, bool state)
{
releasedNotes[noteID] = state;
Debug.Log($"[JudgeManager] RegisterNoteReleased: {noteID} = {state}");
}
public bool HasNoteReleased(string noteID)
{
return releasedNotes.ContainsKey(noteID) && releasedNotes[noteID];
}
/// <summary>
/// 当音符进入判定区域时调用
/// </summary>
public void RegisterNote(KeyCode key, Note note)
{
if (!judgeQueues.ContainsKey(key))
judgeQueues[key] = new Queue<Note>();
if (!judgeQueues[key].Contains(note))
{
judgeQueues[key].Enqueue(note);
}
}
/// <summary>
/// 当音符离开判定区域时调用
/// </summary>
public void UnregisterNote(KeyCode key, Note note)
{
if (!judgeQueues.ContainsKey(key)) return;
Queue<Note> newQueue = new Queue<Note>();
while (judgeQueues[key].Count > 0)
{
Note n = judgeQueues[key].Dequeue();
if (n != note)
{
newQueue.Enqueue(n);
}
else
{
// **如果音符未被判定,则触发 Miss**
if (!n.IsJudged())
{
n.JudgeMiss();
}
}
}
judgeQueues[key] = newQueue;
}
/// <summary>
/// 按键按下时,判定队列中最早进入的音符
/// </summary>
public void JudgeEarliestNote(KeyCode key)
{
if (judgeQueues.ContainsKey(key) && judgeQueues[key].Count > 0)
{
Note note = judgeQueues[key].Dequeue(); // 只取最早的音符
if (note != null && !note.IsJudged())
{
note.Judge();
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e726455158177b3499eb8f0910c54e30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,74 @@
using UnityEngine;
using System.Collections.Generic;
public class KeyBindingManager : MonoBehaviour
{
private static Dictionary<string, KeyCode> keyBindings = new Dictionary<string, KeyCode>();
private void Awake()
{
Debug.Log("KeyBindingManager Awake() 被调用,开始加载按键映射...");
if (keyBindings.Count == 0)
{
LoadKeyBindings();
}
}
/// <summary> 获取颜色对应的按键 </summary>
public static KeyCode GetKeyForColor(string color)
{
if (keyBindings.TryGetValue(color.ToLower(), out KeyCode key))
{
return key;
}
Debug.LogError($"未找到颜色 {color} 对应的按键!请检查 KeyBindingManager 是否正确初始化。");
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);
}
SaveKeyBindings();
}
/// <summary> 存储按键绑定到 `PlayerPrefs` </summary>
private static void SaveKeyBindings()
{
foreach (var kvp in keyBindings)
{
PlayerPrefs.SetInt($"KeyBinding_{kvp.Key}", (int)kvp.Value);
}
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 };
for (int i = 0; i < colors.Length; i++)
{
if (PlayerPrefs.HasKey($"KeyBinding_{colors[i]}"))
{
keyBindings[colors[i]] = (KeyCode)PlayerPrefs.GetInt($"KeyBinding_{colors[i]}");
}
else
{
keyBindings[colors[i]] = defaultKeys[i];
}
}
Debug.Log("KeyBindings 初始化成功:" + string.Join(", ", keyBindings));
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2639b2e07c4995842bdbe955d9fb6bfb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,87 @@
using UnityEngine;
public class Notes : MonoBehaviour
{
public int trackIndex; // 轨道索引
public float hitTime; // 该音符应该被击中的时间
private bool canBeJudged = false; // 是否进入判定区域
private bool isHit = false; // 是否已被击中
private void Update()
{
// 过了 Miss 判定时间,销毁音符
if (!isHit && Time.timeSinceLevelLoad > hitTime + 0.3f)
{
JudgeMiss();
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("JudgmentLine"))
{
canBeJudged = true;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("JudgmentLine"))
{
canBeJudged = false;
}
}
public void JudgeNote()
{
if (!canBeJudged || isHit) return; // 仅在进入判定区后才能被判定
float currentTime = Time.timeSinceLevelLoad;
float timeDifference = Mathf.Abs(currentTime - hitTime);
if (timeDifference <= 0.05f)
{
JudgePerfect();
}
else if (timeDifference <= 0.1f)
{
JudgeGreat();
}
else if (timeDifference <= 0.2f)
{
JudgeGood();
}
else if (timeDifference <= 0.3f)
{
JudgeMiss();
}
}
private void JudgePerfect()
{
Debug.Log($"Track {trackIndex}: Perfect!");
isHit = true;
Destroy(gameObject);
}
private void JudgeGreat()
{
Debug.Log($"Track {trackIndex}: Great!");
isHit = true;
Destroy(gameObject);
}
private void JudgeGood()
{
Debug.Log($"Track {trackIndex}: Good!");
isHit = true;
Destroy(gameObject);
}
private void JudgeMiss()
{
Debug.Log($"Track {trackIndex}: Miss!");
isHit = true;
Destroy(gameObject);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d8062cc45ed8f84da8460fcd2964b77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+130
View File
@@ -0,0 +1,130 @@
using UnityEngine;
public class Note : BaseNote
{
private KeyCode keyToPress;
private string noteColor;
private AnimationController anim;
private NoteController controller;
private bool isJudged = false;
private void Awake()
{
anim = GetComponent<AnimationController>();
controller = GetComponent<NoteController>();
}
public void Setup(KeyCode key, int trackIndex, float speed, float hitTime, string color)
{
keyToPress = key;
noteColor = color;
TrackIndex = trackIndex;
Speed = speed;
this.hitTime = hitTime;
isJudged = false;
if (controller != null)
{
controller.SetSpeed(speed);
}
InputManager.OnKeyPressed += HandlePress;
}
private void OnDestroy()
{
InputManager.OnKeyPressed -= HandlePress;
}
private void HandlePress(KeyCode key)
{
// 确保只有匹配的按键触发,并且音符处于判定区域
if (isJudged || key != keyToPress || (controller != null && !controller.IsInJudgeZone()))
return;
float timeDifference = Mathf.Abs(Time.time - hitTime);
if (timeDifference <= 0.08f)
{
Debug.Log($"短音符 {keyToPress}: Perfect");
}
else if (timeDifference <= 0.15f)
{
Debug.Log($"短音符 {keyToPress}: Great");
}
else
{
Debug.Log($"短音符 {keyToPress} Miss");
JudgeMiss();
return;
}
Judge();
}
public bool IsJudged()
{
return isJudged;
}
public void Judge()
{
if (isJudged)
return;
isJudged = true;
if (controller != null)
{
controller.StopMovement();
controller.PlayHitEffect();
}
ReturnToPool();
if (anim !=null)
{
Debug.Log("执行");
anim.PlayDestroyAnimation(noteColor);
}
}
public void JudgeMiss()
{
if (isJudged) return;
isJudged = true;
Debug.Log($"短音符 {keyToPress} Miss");
ReturnToPool();
}
private void ReturnToPool()
{
InputManager.OnKeyPressed -= HandlePress;
if (controller != null)
{
controller.ResetState();
}
gameObject.SetActive(false);
NotePool.Instance.ReturnNote(gameObject, noteColor);
}
public int GetTrackIndex()
{
return TrackIndex;
}
public KeyCode GetKey()
{
return keyToPress;
}
/// <summary>
/// 由 Controller 调用,通知 Note 是否在判定区域内
/// </summary>
public void SetJudgeZone(bool inZone)
{
// 如果音符离开判定区域且还未判定,则判为 Miss
if (!inZone && !isJudged)
{
JudgeMiss();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7d5a4ca6fc6b784d89e910ff90c2c7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum NoteColor
{
Red, // ºìÉ«Òô·û
Green, // ÂÌÉ«Òô·û
Yellow, // »ÆÉ«Òô·û
Purple, // ×ÏÉ«Òô·û
Blue // À¶É«Òô·û
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca21a5e0d2736724bb9a96eeca2b19dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,101 @@
using System.Collections;
using UnityEngine;
public class NoteController : MonoBehaviour
{
private float speed;
private bool isMoving = true;
private bool isInJudgeZone = false; // 判定区域标记
private ParticleSystem hitEffect;
private Note linkedNote;
private void Awake()
{
hitEffect = GetComponentInChildren<ParticleSystem>();
linkedNote = GetComponent<Note>();
}
private void Update()
{
if (isMoving)
{
transform.Translate(Vector3.down * speed * Time.deltaTime);
}
}
public void SetSpeed(float newSpeed)
{
speed = newSpeed;
}
public void StopMovement()
{
isMoving = false;
}
public void PlayHitEffect()
{
if (hitEffect != null)
{
hitEffect.Play();
}
}
public void ResetState()
{
speed = 0f;
isMoving = true;
isInJudgeZone = false;
if (hitEffect != null)
{
hitEffect.Stop();
hitEffect.Clear();
}
}
// 将 OnTriggerEnter2D 和 OnTriggerExit2D 挂载在 Controller 上
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("JudgmentLine"))
{
if (linkedNote != null)
{
isInJudgeZone = true;
TrackKeyManager.Instance.RegisterKey(linkedNote.GetTrackIndex(), linkedNote.GetKey());
}
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.CompareTag("JudgmentLine"))
{
if (linkedNote != null)
{
isInJudgeZone = false;
TrackKeyManager.Instance.UnregisterKey(linkedNote.GetTrackIndex(), linkedNote.GetKey());
linkedNote.SetJudgeZone(isInJudgeZone);
}
}
}
/// <summary>
/// 供 Note 查询是否处于判定区域
/// </summary>
public bool IsInJudgeZone()
{
return isInJudgeZone;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c62655f9ac1675e46ad7ea9a302508ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,190 @@
using System.Collections.Generic;
using UnityEngine;
public class NotePool : MonoBehaviour
{
public static NotePool Instance { get; private set; }
public GameObject[] notePrefabs; // 短音符预制体(按颜色存储)
public GameObject[] holdNotePrefabs; // 长音符片段预制体
public GameObject[] startNotePrefabs; // 长音符start片段预制体
private int poolSize = 12; // 每种类型的音符池大小
private int maxPoolSize = 60; // 池的最大容量
private Dictionary<int, Stack<GameObject>> notePools; // 短音符对象池
private Dictionary<int, Stack<GameObject>> holdNotePools; // 长音符片段对象池
private Dictionary<int, Stack<GameObject>> startNotePools; // 长音符start片段对象池
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
return;
}
notePools = new Dictionary<int, Stack<GameObject>>();
holdNotePools = new Dictionary<int, Stack<GameObject>>();
startNotePools = new Dictionary<int, Stack<GameObject>>();
for (int i = 0; i < notePrefabs.Length; i++)
{
notePools[i] = new Stack<GameObject>();
holdNotePools[i] = new Stack<GameObject>();
startNotePools[i] = new Stack<GameObject>();
for (int j = 0; j < poolSize; j++)
{
AddToPool(notePools[i], notePrefabs[i]);
AddToPool(startNotePools[i], startNotePrefabs[i]);
}
for (int j = 0; j < poolSize * 5; j++)
{
AddToPool(holdNotePools[i], holdNotePrefabs[i]);
}
}
}
private GameObject GetObjectFromPool(Stack<GameObject> pool, GameObject prefab)
{
if (pool.Count > 0)
{
GameObject obj = pool.Pop();
obj.SetActive(true);
return obj;
}
else
{
if (pool.Count < maxPoolSize)
{
GameObject obj = Instantiate(prefab);
obj.SetActive(true);
return obj;
}
else
{
Debug.LogWarning("对象池已满,生成新的音符!");
GameObject obj = Instantiate(prefab);
obj.SetActive(true);
return obj;
}
}
}
private void ReturnObjectToPool(Stack<GameObject> pool, GameObject obj)
{
if (obj == null || pool.Contains(obj)) return; // 防止重复回收
if (obj != null)
{
obj.SetActive(false); // 设置为非活动状态
if (pool.Count < maxPoolSize)
{
pool.Push(obj);
Debug.Log($"已归还对象:{obj.name},当前池容量:{pool.Count}");
}
else
{
Debug.LogWarning("对象池已满,无法继续归还对象!");
}
}
}
private void AddToPool(Stack<GameObject> pool, GameObject prefab)
{
if (pool.Count < maxPoolSize)
{
GameObject obj = Instantiate(prefab);
obj.SetActive(false);
pool.Push(obj);
}
}
public GameObject GetNote(string color)
{
int colorIndex = GetColorIndexFromName(color);
return GetObjectFromPool(notePools[colorIndex], notePrefabs[colorIndex]);
}
public GameObject GetStartNote(string color)
{
int colorIndex = GetColorIndexFromName(color);
return GetObjectFromPool(startNotePools[colorIndex], startNotePrefabs[colorIndex]);
}
public GameObject GetHoldNoteSegment(string color)
{
int colorIndex = GetColorIndexFromName(color);
return GetObjectFromPool(holdNotePools[colorIndex], holdNotePrefabs[colorIndex]);
}
public void ReturnNote(GameObject note, string color)
{
NoteController noteScript = note.GetComponent<NoteController>();
if (noteScript != null)
{
noteScript.ResetState();
}
ReturnObjectToPool(notePools[GetColorIndexFromName(color)], note);
}
public void ReturnStartNote(GameObject startNote, string color)
{
if (string.IsNullOrEmpty(color))
{
Debug.LogWarning($" 归还 StartNote 时 color 为空!音符名: {startNote.name}");
return;
}
HoldNote holdNote = startNote.GetComponent<HoldNote>();
if (holdNote != null)
{
holdNote.ResetState(); // 确保重置音符状态
}
ReturnObjectToPool(startNotePools[GetColorIndexFromName(color)], startNote);
}
public void ReturnHoldNoteSegment(GameObject holdNote, string color)
{
if (string.IsNullOrEmpty(color))
{
Debug.LogWarning($" 归还 HoldNoteSegment 时 color 为空!音符名: {holdNote.name}");
return;
}
HoldNote holdNoteScript = holdNote.GetComponent<HoldNote>();
if (holdNoteScript != null)
{
holdNoteScript.ResetState(); // 确保重置音符状态
}
ReturnObjectToPool(holdNotePools[GetColorIndexFromName(color)], holdNote);
}
private int GetColorIndexFromName(string colorName)
{
switch (colorName)
{
case "red": return 0;
case "green": return 1;
case "yellow": return 2;
case "purple": return 3;
case "blue": return 4;
default:
Debug.LogError($"未识别的颜色: {colorName},默认使用红色");
return 0;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f4db5764af5850449f3b6314af7fb46
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,217 @@
using UnityEngine;
using System.Collections;
using Unity.VisualScripting;
using System.Runtime.InteropServices.WindowsRuntime;
using TMPro;
public class NoteSpawner : MonoBehaviour
{
public NotePool notePool; // 引用 NotePool
public Transform[] spawnPoints; // 对应轨道的生成点
public GameObject[] notePrefabs; // 短音符预制体
public TextMeshProUGUI globalGameTime;
// 长音符预制体数组(分别为 start, middle, end
public GameObject[] holdNoteStartPrefabs;
public GameObject[] holdNoteMiddlePrefabs;
public GameObject[] holdNoteEndPrefabs;
public float spawnOffset = 0f; // 生成音符时的时间偏移量
public float bpm;
private Beatmap beatmap;
private float startTime; // 存储歌曲开始时间
private bool isSpawning = false;
private static int holdNoteIdCounter = 0; // 全局唯一长音符 ID 计数器
public void LoadBeatmap(Beatmap loadedBeatmap)
{
if (isSpawning) return;
isSpawning = true;
beatmap = loadedBeatmap;
if (beatmap == null)
{
Debug.LogError("加载的谱面为空!");
return;
}
bpm = beatmap.bpm;
startTime = Time.time;
Debug.Log($"歌曲开始时间: {startTime}");
StartCoroutine(SpawnNotes());
}
private IEnumerator SpawnNotes()
{
if (beatmap == null || beatmap.notes == null)
{
Debug.LogError("谱面数据为空!");
isSpawning = false;
yield break;
}
foreach (NoteData note in beatmap.notes)
{
float travelTime = (60f / bpm) * 4;
float spawnTime = note.time - travelTime;
float delay = spawnTime - (Time.time - startTime) + spawnOffset;
Debug.Log($"delay: {delay}");
if (delay > 0)
yield return new WaitForSeconds(delay);
if (note.type == "hold")
{
SpawnHoldNote(note);
}
else
{
SpawnNote(note);
}
}
isSpawning = false;
}
// 生成短音符
public void SpawnNote(NoteData noteData)
{
if (noteData.trackIndex < 0 || noteData.trackIndex >= spawnPoints.Length)
{
Debug.LogError("轨道索引超出范围!");
return;
}
KeyCode key = KeyBindingManager.GetKeyForColor(noteData.color);
if (key == KeyCode.None)
{
Debug.LogError($"未找到颜色 {noteData.color} 对应的按键!请检查 KeyBindingManager 是否正确初始化。");
return;
}
Debug.Log($"生成短音符:颜色={noteData.color}, 按键={key}, 轨道={noteData.trackIndex}");
GameObject note = notePool.GetNote(noteData.color);
if (note == null)
{
Debug.LogError("对象池返回了一个空音符!");
return;
}
note.transform.position = spawnPoints[noteData.trackIndex].position;
note.transform.rotation = Quaternion.identity;
Note noteScript = note.GetComponent<Note>();
if (noteScript != null)
{
float noteSpawnTime = Time.time;
noteScript.Setup(key, noteData.trackIndex, CalculateSpeed(), noteData.time, noteData.color);
Debug.Log($"到达时间:{noteSpawnTime + (60f / bpm) * 4}");
}
else
{
Debug.LogError("音符预制体缺少 Note 组件!");
}
}
public void SpawnHoldNote(NoteData noteData)
{
if (noteData.trackIndex < 0 || noteData.trackIndex >= spawnPoints.Length)
{
Debug.LogError("轨道索引超出范围!");
return;
}
KeyCode key = KeyBindingManager.GetKeyForColor(noteData.color);
if (key == KeyCode.None)
{
Debug.LogError($"未找到颜色 {noteData.color} 对应的按键!");
return;
}
if (string.IsNullOrEmpty(noteData.color))
{
Debug.LogError("[NoteSpawner] noteData.color 为空,无法生成音符!");
return;
}
Debug.Log($"生成时间:{Time.time}");
Debug.Log($"生成长音符:颜色={noteData.color}, 按键={key}, 轨道={noteData.trackIndex}");
float segmentInterval = (60f / bpm) / 4f;
int segmentCount = Mathf.CeilToInt(noteData.length / segmentInterval);
Transform spawnPoint = spawnPoints[noteData.trackIndex];
float scheduledEndTime = noteData.time + noteData.length;
// 生成唯一 id
int holdNoteId = Random.Range(100000, 999999);
// 生成 Start 段
GameObject startObj = notePool.GetStartNote(noteData.color);
if (startObj == null)
{
Debug.LogError("对象池返回空 hold note start");
return;
}
startObj.transform.position = spawnPoint.position;
startObj.transform.rotation = Quaternion.identity;
HoldNote holdNote = startObj.GetComponent<HoldNote>();
if (holdNote != null)
{
holdNote.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), noteData.time, 0f, false, scheduledEndTime, noteData.color, key, "start");
}
else
{
Debug.LogError("长音符 start 部分缺少 HoldNote 组件!");
return;
}
// 生成 Middle 和 End 段
for (int i = 1; i < segmentCount; i++)
{
float segmentDelay = i * segmentInterval;
bool isEndSegment = (i == segmentCount - 1);
string partType = isEndSegment ? "end" : "middle";
GameObject segObj = notePool.GetHoldNoteSegment(noteData.color);
if (segObj == null)
{
Debug.LogError("对象池返回空 hold note 片段!");
continue;
}
segObj.transform.position = spawnPoint.position;
segObj.transform.rotation = Quaternion.identity;
HoldNote holdSeg = segObj.GetComponent<HoldNote>();
if (holdSeg != null)
{
holdSeg.Setup(holdNoteId, noteData.trackIndex, CalculateSpeed(), noteData.time, segmentDelay, isEndSegment, scheduledEndTime, noteData.color, key, partType);
}
else
{
Debug.LogError("长音符片段缺少 HoldNote 组件!");
}
}
}
private float CalculateSpeed()
{
float noteTravelTime = (60f / bpm) * 4;
return 10.75f / noteTravelTime;
}
private void Update()
{
//if (globalGameTime.text=="0.01")
//{
// bgMusicAudioSource.Play();
//}
//else return;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5a8261645f54b8647ba4c89adc503be9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,58 @@
using System.Collections.Generic;
using UnityEngine;
public class TrackKeyManager : MonoBehaviour
{
public static TrackKeyManager Instance { get; private set; }
private Dictionary<int, Queue<KeyCode>> trackKeyMappings = new Dictionary<int, Queue<KeyCode>>();
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
}
/// <summary>
/// 当音符进入判定区域时,添加按键映射
/// </summary>
public void RegisterKey(int trackIndex, KeyCode key)
{
if (!trackKeyMappings.ContainsKey(trackIndex))
trackKeyMappings[trackIndex] = new Queue<KeyCode>();
trackKeyMappings[trackIndex].Enqueue(key);
}
/// <summary>
/// 当音符离开判定区域时,移除按键映射
/// </summary>
public void UnregisterKey(int trackIndex, KeyCode key)
{
if (!trackKeyMappings.ContainsKey(trackIndex) || trackKeyMappings[trackIndex].Count == 0)
return;
if (trackKeyMappings[trackIndex].Peek() == key)
{
trackKeyMappings[trackIndex].Dequeue();
}
}
/// <summary>
/// 获取轨道当前的按键(队列头)
/// </summary>
public KeyCode GetCurrentKey(int trackIndex)
{
if (trackKeyMappings.ContainsKey(trackIndex) && trackKeyMappings[trackIndex].Count > 0)
{
return trackKeyMappings[trackIndex].Peek();
}
return KeyCode.None; // 无按键
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ab4cc3cc2f90c3a4b9bc6a5dea674684
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,67 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class gameplay_global : MonoBehaviour
{
public static gameplay_global Instance; //单例
public bool game_isStarted = false; //游戏开始
public bool game_isPaused = false; //游戏暂停
public bool game_isEnded = false; //游戏结束,读到了最后的chart_is_end
public bool k1_pressed = false; //按下了按键
public bool k2_pressed = false;
public bool k3_pressed = false;
public bool k4_pressed = false;
public bool k5_pressed = false;
public bool k1_inJudgeZone = false;//进入了判定区域
public bool k2_inJudgeZone = false;
public bool k3_inJudgeZone = false;
public bool k4_inJudgeZone = false;
public bool k5_inJudgeZone = false;
public string[] notes;
public string songName;
public int difficulty_ID;
public string difficulty_Name;
public float startTime; //记录开始游戏的时间 用于谱面note加载时机计算
public float streamMultiply = 5f; //note流速
public float offset = -0.2f; //延迟
public float lifeTime = 0.15f; //perfect good lost判定提示效果的显示时间
public float top_position_Y; //top按钮的y坐标 用于确定note生成位置的y坐标
public int perfectScore;//perfect分值
public int goodScore;//good分值
public int score;//float类型的精确总分
public float multiNoteOffset = 0.26f;//用于多押调整错位的偏移量
public int combo = 0;//连击数
public int noteSum = 0;//谱面总物量
public int perfectNum = 0;//当前perfect数
public int goodNum = 0;//当前good数
public int missNum = 0;//当前miss数
public Vector3 k1Pos, k2Pos, k3Pos, k4Pos; //四个键位的坐标 用于确定note生成位置
public KeyCode key1 = KeyCode.D;
public KeyCode key2 = KeyCode.F;
public KeyCode key3 = KeyCode.J;
public KeyCode key4 = KeyCode.K;
// Start is called before the first frame update
private void Awake()
{
}
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe9f6b9f7cd4f444cb71251541a9cb29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bb4668e030e687a419b45cfe6c9dcb00
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: