diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..51cc140 Binary files /dev/null and b/.DS_Store differ diff --git a/Audio/track.mp3 b/Audio/track.mp3 new file mode 100644 index 0000000..2cb7c8e Binary files /dev/null and b/Audio/track.mp3 differ diff --git a/Audio/track.mp3.import b/Audio/track.mp3.import new file mode 100644 index 0000000..1d81f6b --- /dev/null +++ b/Audio/track.mp3.import @@ -0,0 +1,19 @@ +[remap] + +importer="mp3" +type="AudioStreamMP3" +uid="uid://767hle7lvb6b" +path="res://.godot/imported/track.mp3-200430bc283eed98be8a5eaa80a158a9.mp3str" + +[deps] + +source_file="res://Audio/track.mp3" +dest_files=["res://.godot/imported/track.mp3-200430bc283eed98be8a5eaa80a158a9.mp3str"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/Charts/track.json b/Charts/track.json index 2e189b9..706030d 100644 --- a/Charts/track.json +++ b/Charts/track.json @@ -1,30 +1,52 @@ { + "MusicPath": "res://Audio/track.mp3", "BPM": 120, - "Offset": 0.5, - "TicksPerBeat": 480, + "Offset": 0.0, + "TicksPerBeat": 480.0, + "TempoPoints": [ + { + "Tick": 0, + "BPM": 120 + } + ], + "RelativeSpeedPoints": [ + { + "Tick": 0, + "Speed": 20.0, + "IsLinear": false + } + ], + "AbsoluteSpeedPoints": [ + { + "Tick": 0, + "Speed": 1.0, + "IsLinear": false + } + ], "Events": [ { "$type": "Onrinto.Chart.ChartEvent", "Type": "Beat", - "Tick": 1000, - "Position": {"x": 1.0, "y": 0.0} + "Tick": 0, + "Position": { "X": 0.0, "Y": 0.0 } }, { - "$type": "Onrinto.Chart.AnimatedEvent", - "Type": "Catch", - "Tick": 2000, - "Position": {"x": 0.0, "y": 0.0}, - "AniType": "Sin", - "In_Out": 1, - "AniStartTime": 1500, - "AniEndTime": 2000, - "RelativeSpeed": 1.5, - "StartPosition": {"x": -2.0, "y": 4.0}, - "EndPosition": {"x": 2.0, "y": 3.0} + "$type": "Onrinto.Chart.ChartEvent", + "Type": "Beat", + "Tick": 480, + "Position": { "X": -2.0, "Y": 0.0 } + }, + { + "$type": "Onrinto.Chart.ChartEvent", + "Type": "Beat", + "Tick": 960, + "Position": { "X": 2.0, "Y": 0.0 } + }, + { + "$type": "Onrinto.Chart.ChartEvent", + "Type": "Beat", + "Tick": 1440, + "Position": { "X": 0.0, "Y": 0.0 } } - ], - "RelativeSpeedPoints": [ - { "Tick": 0, "Speed": 1.0 , "IsLinear": false}, - { "Tick": 5000, "Speed": 2.0, "IsLinear": true } ] } \ No newline at end of file diff --git a/ProjectOnrinto.csproj b/ProjectOnrinto.csproj index e457872..42fc5bc 100644 --- a/ProjectOnrinto.csproj +++ b/ProjectOnrinto.csproj @@ -1,4 +1,4 @@ - + net8.0 net9.0 diff --git a/ProjectOnrinto.csproj.old b/ProjectOnrinto.csproj.old new file mode 100644 index 0000000..e457872 --- /dev/null +++ b/ProjectOnrinto.csproj.old @@ -0,0 +1,7 @@ + + + net8.0 + net9.0 + true + + \ No newline at end of file diff --git a/Scenes/Game/level.tscn b/Scenes/Game/level.tscn index 5c26ba2..9b36df4 100644 --- a/Scenes/Game/level.tscn +++ b/Scenes/Game/level.tscn @@ -2,6 +2,7 @@ [ext_resource type="PackedScene" uid="uid://n8na6nkfmnme" path="res://Scenes/Audio/audio_stream_player.tscn" id="1_5xcqo"] [ext_resource type="Script" uid="uid://bmanbpeqbrkk1" path="res://Scripts/Core/TrackGenerator.cs" id="2_gayhk"] +[ext_resource type="PackedScene" uid="uid://dqp4hku6gudds" path="res://Scenes/Prefabs/beat.tscn" id="3_gayhk"] [node name="Level" type="Node3D" unique_id=2071595859] @@ -9,3 +10,7 @@ [node name="Track" type="Node3D" parent="." unique_id=923723651] script = ExtResource("2_gayhk") +NotePrefab = ExtResource("3_gayhk") + +[node name="Camera3D" type="Camera3D" parent="." unique_id=381050533] +transform = Transform3D(-1, 2.262668e-08, -8.444392e-08, 0, 0.9659258, 0.25881904, 8.742278e-08, 0.25881904, -0.9659258, 0, 3, -7) diff --git a/Scripts/.DS_Store b/Scripts/.DS_Store new file mode 100644 index 0000000..6ec9b26 Binary files /dev/null and b/Scripts/.DS_Store differ diff --git a/Scripts/Core/GameManager.cs b/Scripts/Core/GameManager.cs index 4044e17..6c8a608 100644 --- a/Scripts/Core/GameManager.cs +++ b/Scripts/Core/GameManager.cs @@ -3,10 +3,13 @@ using Onrinto.Chart; public partial class GameManager : Node3D { - public TrackData CurrentTrack { get; private set; } + [Export] public float VisibleDistance = 150.0f; + [Export] public float PlayerSpeed = 1.5f; + public TrackData CurrentTrack { get; set; } public static GameManager Instance { get; private set; } public float CurrentAbsoluteSpeed { get; private set; } + public float FinalSpeed => CurrentAbsoluteSpeed * PlayerSpeed; public float CurrentAbsZ { get; private set; } private int absoluteSpeedPointIndexArrow = 0; @@ -23,6 +26,11 @@ public partial class GameManager : Node3D return; } + if(absoluteSpeedPointIndexArrow > 0 && time < CurrentTrack.TickToSeconds(points[absoluteSpeedPointIndexArrow].Tick)) + { + absoluteSpeedPointIndexArrow = 0; + } + for(int i = absoluteSpeedPointIndexArrow; i < points.Count - 1; i++) { double pointTime_from = CurrentTrack.TickToSeconds(points[i].Tick); @@ -34,8 +42,15 @@ public partial class GameManager : Node3D if(points[i].IsLinear) { - double t = (time - pointTime_from) / (pointTime_to - pointTime_from); - CurrentAbsoluteSpeed = Mathf.Lerp(points[i].Speed, points[i + 1].Speed, (float)t); + if (pointTime_to <= pointTime_from) + { + CurrentAbsoluteSpeed = points[i].Speed; + } + else + { + double t = (time - pointTime_from) / (pointTime_to - pointTime_from); + CurrentAbsoluteSpeed = Mathf.Lerp(points[i].Speed, points[i + 1].Speed, (float)t); + } } else { diff --git a/Scripts/Core/MusicClock.cs b/Scripts/Core/MusicClock.cs index efcf2fa..e277100 100644 --- a/Scripts/Core/MusicClock.cs +++ b/Scripts/Core/MusicClock.cs @@ -10,7 +10,21 @@ public partial class MusicClock : AudioStreamPlayer public override void _Ready() { Instance = this; - if (Stream == null) GD.PrintErr("MusicClock: No Audio File?"); + } + + public void LoadMusic(string path) + { + if (string.IsNullOrEmpty(path)) return; + + var stream = GD.Load(path); + if (stream != null) + { + Stream = stream; + } + else + { + GD.PrintErr($"Failed to load music at: {path}"); + } } public override void _Process(double delta) diff --git a/Scripts/Core/TrackGenerator.cs b/Scripts/Core/TrackGenerator.cs index e2c561d..69bb701 100644 --- a/Scripts/Core/TrackGenerator.cs +++ b/Scripts/Core/TrackGenerator.cs @@ -4,7 +4,6 @@ using Onrinto.Chart; using System.Text.Json.Serialization; using System.Linq; using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; public partial class TrackGenerator : Node3D { @@ -12,6 +11,8 @@ public partial class TrackGenerator : Node3D List _notes = new List(); [Export] public PackedScene NotePrefab; + private int _spawnIndex = 0; + // Called when the node enters the scene tree for the first time. public override void _Ready() { @@ -44,21 +45,46 @@ public partial class TrackGenerator : Node3D } _notes = track.Events.OrderBy(e => e.Tick).ToList(); // Ensure events are sorted by time. + track.Initialize(); // Pre-calculate hit times and positions. + GameManager.Instance.CurrentTrack = track; // Set the current track in the game manager. + + // Load and play music + if (!string.IsNullOrEmpty(track.MusicPath)) + { + MusicClock.Instance.LoadMusic(track.MusicPath); + MusicClock.Instance.Play(); + } } private void spawnNote(ChartEvent e) { var noteInstance = NotePrefab.Instantiate(); - float speed = 20.0f; - double hitTime = (double)(e.Tick / track.TicksPerBeat * 60.0 / track.BPM); - float initialZ = (float)(hitTime * speed); - - noteInstance.Initialize(e, hitTime, initialZ); // Initial Z position + noteInstance.Initialize(e); AddChild(noteInstance); } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { - + if (track == null) return; + + float currentZ = GameManager.Instance.CurrentAbsZ; + float spawnThreshold = GameManager.Instance.VisibleDistance; + + while (_spawnIndex < _notes.Count) + { + var e = _notes[_spawnIndex]; + float visualDist = (e.HitAbsZ - currentZ) * GameManager.Instance.FinalSpeed; + + if (visualDist <= spawnThreshold) + { + // Only spawn if it hasn't been missed for too long + if (e.HitTime - MusicClock.Instance.CurrentTime >= -0.5) + { + spawnNote(e); + } + _spawnIndex++; + } + else break; + } } } diff --git a/Scripts/Data/ChartEvent.cs b/Scripts/Data/ChartEvent.cs index 36a02e0..26d9b11 100644 --- a/Scripts/Data/ChartEvent.cs +++ b/Scripts/Data/ChartEvent.cs @@ -13,7 +13,7 @@ public class TempoPoint public float BPM { get; set; } [JsonConstructor] - public TempoPoint(double tick, float bpm, bool isLinear) + public TempoPoint(double tick, float bpm) { Tick = tick; BPM = bpm; @@ -46,7 +46,7 @@ public class ChartEvent public double Tick { get; set; } public Vector2 Position { get; set; } public double HitTime { get; set; } - public double HitAbsZ { get; set; } + public float HitAbsZ { get; set; } public ChartEvent(EventType type, double tick, Vector2 position) { diff --git a/Scripts/Data/TrackData.cs b/Scripts/Data/TrackData.cs index d12c791..d81f2bb 100644 --- a/Scripts/Data/TrackData.cs +++ b/Scripts/Data/TrackData.cs @@ -6,6 +6,7 @@ namespace Onrinto.Chart; public class TrackData { + public string MusicPath { get; set; } public int BPM { get; set; } public double Offset { get; set; } public double TicksPerBeat { get; set; } @@ -107,6 +108,10 @@ public class TrackData if(point.IsLinear && idx < speedPoints.Count - 1) { double nextTime = TickToSeconds(speedPoints[idx + 1].Tick); + if (nextTime <= pointTime) + { + return cachedZ + (float)deltaTime * point.Speed; + } float t = (float)((time - pointTime) / (nextTime - pointTime)); float currentSpeed = Mathf.Lerp(point.Speed, speedPoints[idx + 1].Speed, t); return cachedZ + (float)deltaTime * (point.Speed + currentSpeed) * 0.5f; diff --git a/Scripts/GameObjects/NoteObject.cs b/Scripts/GameObjects/NoteObject.cs index 7fc144c..9be7068 100644 --- a/Scripts/GameObjects/NoteObject.cs +++ b/Scripts/GameObjects/NoteObject.cs @@ -4,12 +4,15 @@ using Onrinto.Chart; public partial class NoteObject : Node3D { private double _hitSeconds; + private float _hitAbsZ; private bool _isHit = false; private bool _initialized = false; - public void Initialize(ChartEvent chartEvent, double hitSeconds, float initialZ) + public void Initialize(ChartEvent chartEvent) { - _hitSeconds = hitSeconds; + _hitSeconds = chartEvent.HitTime; + _hitAbsZ = chartEvent.HitAbsZ; + float initialZ = _hitAbsZ - GameManager.Instance.CurrentAbsZ; Position = new Vector3(chartEvent.Position.X, chartEvent.Position.Y, initialZ); _initialized = true; @@ -26,14 +29,14 @@ public partial class NoteObject : Node3D { if (!_initialized) return; - float speed = 20.0f; // Scroll speed - double _currentTime = MusicClock.Instance.CurrentTime; - float newZ = (float)((_hitSeconds - _currentTime) * speed); + float newZ = (float)((_hitAbsZ - GameManager.Instance.CurrentAbsZ) * GameManager.Instance.FinalSpeed); Position = new Vector3(Position.X, Position.Y, newZ); // Update Z position if(_hitSeconds - _currentTime < -0.5) QueueFree(); // Remove note + // Toggle visibility instead of removing + Visible = (newZ >= 0 && newZ <= GameManager.Instance.VisibleDistance); } }