Install Asset
Install via Godot
To maintain one source of truth, Godot Asset Library is just a mirror of the old asset library so you can download directly on Godot via the integrated asset library browser
Quick Information
SRGL is a Godot 4.6 (.NET) based keyboard rhythm game library.It is specialized for creating casual Vertical Scrolling Rhythm Games (VSRG).Currently supports 2D only.# Features- Complete separation of game logic and visuals.- Uses JSON format for chart files.- Automatically generates long note ticks and barlines based on time signatures.- Supports basic scroll velocity (SV) changes and stop effects.- Automatically compensates for audio drift every frame.- Highly customizable timing windows, input systems, user offsets, and scroll speeds.- Easily add custom judgement logic for different note types via the IJudgementStrategy interface.- Design visual elements effortlessly with JudgementLine, NoteObject, and EffectObject classes.See GitHub for more information.
Simple Rhythm Game Library
Other Languages: English
SRGL์ Godot 4.6 (.NET) ๊ธฐ๋ฐ์ ํค๋ณด๋ ๋ฆฌ๋ฌ๊ฒ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค.
์บ์ฃผ์ผํ ๊ฑด๋ฐํ ๋ฆฌ๋ฌ๊ฒ์(VSRG) ์ ์์ ํนํ๋์ด์์ต๋๋ค.
ํ์ฌ 2D๋ง ์ง์ํฉ๋๋ค.
๐ ๋ชฉ์ฐจ
โจ ํน์ง
- ๊ฒ์ ๋ก์ง๊ณผ ๋น์ฃผ์ผ์ด ์์ ํ ๋ถ๋ฆฌ๋์ด ์์ต๋๋ค.
- JSON ํ์์ ์ฑ๋ณด ํ์ผ์ ์ฌ์ฉํฉ๋๋ค.
- ๋ฐ์ํ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋กฑ๋ ธํธ ํฑ๊ณผ ๋ง๋์ ์ ์๋์ผ๋ก ์์ฑํฉ๋๋ค.
- ๊ธฐ๋ณธ์ ์ธ ์คํฌ๋กค ๋ณ์๊ณผ ์ ์ง ์ฐ์ถ์ด ๊ฐ๋ฅํฉ๋๋ค.
- ๋งค ํ๋ ์๋ง๋ค ์ค๋์ค ๋๋ฆฌํํธ๋ฅผ ์๋์ผ๋ก ๋ณด์ ํฉ๋๋ค.
- ํ์ด๋ฐ ์๋์ฐ, ์ ๋ ฅ ์์คํ , ์ฌ์ฉ์ ์คํ์ , ์คํฌ๋กค ์๋ ๋ฑ์ ์ปค์คํ ํ ์ ์์ต๋๋ค.
IJudgementStrategy์ธํฐํ์ด์ค๋ก ๋ ธํธ ์ข ๋ฅ๋ณ ํ์ ๋ก์ง์ ์ฝ๊ฒ ์ถ๊ฐํ ์ ์์ต๋๋ค.JudgementLine,NoteObject,EffectObjectํด๋์ค๋ก ๋น์ฃผ์ผ ์์๋ค์ ์ฝ๊ฒ ๋์์ธ ํ ์ ์์ต๋๋ค.
๐ ๏ธ ์ค์น
(a) SRGL ์ค์น
SRGL์ C# ์คํฌ๋ฆฝํธ ๊ธฐ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค.
Godot 4.6 (.NET) ํ๋ก์ ํธ์ ํ์ ๊ฒฝ๋ก์ ํด๋ก ํ์ฌ ์ฌ์ฉํ์ธ์.
git clone https://github.com/joung2321/SRGL.git
(b) QuickStart ๋ฐ๋ชจ ๋น๋
๋ฐ๋ชจ ํ๋ก์ ํธ์ธ QuickStart๋ฅผ ๋น๋ํ๋ ค๋ฉด ์ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ์์ฑํด์ผ ํฉ๋๋ค.
REM Windows
mklink /J SRGL\demo\QuickStart\addons SRGL\addons
# Linux
# [CAUTION] Untested command
ln -s SRGL/addons SRGL/demo/QuickStart/addons
QuickStart์ project.godot๋ฅผ ์๋ํฐ๋ก ์ด์ด ๋ค์๊ณผ ๊ฐ์ด ์์ ํฉ๋๋ค:
- ํ๋ก์ ํธ๋ฅผ ๋น๋ํฉ๋๋ค.
- Main.tscn์ JudgementLine์ ์ ํํฉ๋๋ค.
- Inspector์์, Judgement Points ์์ ๐๏ธ๋ฅผ ๋๋ฆ ๋๋ค.
- ํฌ๊ธฐ๊ฐ 4์ธ ๋ฐฐ์ด์ด ๋ณต์๋๋ ๊ฒ์ ํ์ธํฉ๋๋ค.
๐ ํต ์คํํธ
๋ณธ ์ ์์๋ ๊ฐ๋จํ 4ํค ๋ฆฌ๋ฌ๊ฒ์์ ๊ตฌํํฉ๋๋ค.
์์ธํ ๋ด์ฉ์ demo/QuickStart๋ฅผ ์ฐธ๊ณ ํ์ธ์.
1. ์์ ํ์ผ ์ค๋น
์๋ ์
๋ณด์ ๋ํ ์์ ํ์ผ์ ์ค๋นํฉ๋๋ค. ํ์ฅ์๋ OGG๋ฅผ ๊ถ์ฅํฉ๋๋ค.
๋ณธ ์์ ์์๋ quickStart.ogg๋ฅผ ์ฌ์ฉํฉ๋๋ค.
โ ๏ธ์ฃผ์: MuseScore๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ, MuseScore๊ฐ ์์ฑํ ์์์ Audacity์์ OGG๋ก ๋ค์ ์ ์ฅํด์ผ Godot 4.6์์ ์ ์์ ์ผ๋ก ๋ก๋ํ ์ ์๋ OGG ํ์ผ์ด ๋ฉ๋๋ค.
2. ์ฑ๋ณด ํ์ผ ์์ฑ
์ฑ๋ณด ํ์ผ๋ก ์ฌ์ฉํ quickStart.json์ ์์ฑํฉ๋๋ค.
์์ธํ ์ฌํญ์ ์ฑ๋ณด ํ์ผ ๊ตฌ์กฐ๋ฅผ ์ฐธ๊ณ ํ์ธ์.
{
"FormatVersion": "0.1.0",
"Title": "",
"AudioPath": "res://chart/quickStart.ogg",
"PPQN": 10,
"EndOfTrack": 320,
"LaneCount": 4,
"OffsetUsec": 2000,
"Tempos": [ {"s":0, "b":120} ],
"TimeSignatures": [ {"s":0, "n":4, "d":4} ],
"SvChanges": [ {"s":0, "m":1} ],
"Notes":
[
{"s":10, "l":0},
{"s":20, "l":0},
{"s":30, "l":1},
{"s":50, "l":1},
{"s":60, "l":1},
{"s":70, "l":2},
{"s":90, "l":2},
{"s":100, "l":2},
{"s":110, "l":3},
{"s":130, "l":3},
{"s":140, "l":3},
{"s":150, "l":0}
]
}
3. ๋น์ฃผ์ผ ์์ ๋์์ธ
a) ํ์ ์ , ๋ง๋์ , ๋ ธํธ
SRGL์ด ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋
JudgementLine.cs,
BarlineObject.cs,
TapNoteObject.cs๋ก
JudgementLine.tscn,
BarlineObject.tscn,
TapNoteObject.tscn์ ๊ฐ๊ฐ ๋์์ธํฉ๋๋ค.
b) ์ดํํธ
SRGL์ EffectObject.cs์ ํ์ ํด๋์ค๋ฅผ ์ ๊ณตํ์ง ์์ต๋๋ค.HitEffectObject.cs๋ฅผ ์์ฑํ๊ณ ,
HitEffectObject.tscn์ ๋์์ธํฉ๋๋ค.
using Godot;
using SRGL;
using SRGL.Common;
public partial class HitEffectObject : EffectObject
{
[Export] private AnimatedSprite2D _spriteAnimation;
protected override void OnPlay(Judgement judgement)
{
_spriteAnimation.Stop();
switch(judgement.PartitionIndex)
{
case 0:
case 1:
_spriteAnimation.Play("pure");
break;
case 2:
_spriteAnimation.Play("far");
break;
default:
_spriteAnimation.Play("default"); // empty animation
break;
}
}
}
4. ๊ฒ์ํ๋ ์ด ๋ก์ง ์์ฑ
Main.cs๋ฅผ ์์ฑํฉ๋๋ค. ์ฌ๋ฏธ๋ฅผ ์ํด ComboCounter๋ฅผ ์ถ๊ฐํฉ๋๋ค.
using Godot;
using SRGL;
using SRGL.Common;
using SRGL.Standard;
using SRGL.Misc;
public partial class Main : Node
{
[Export] private JudgementLine _judgementLine;
[Export] private ComboCounter _comboCounter;
// logic
private SongPlayer _sp;
private JudgementQueue _jq;
private LogicManager _lm;
// visual
private NoteManager _nm;
private EffectManager _em;
public override void _Ready()
{
// ======== logic ========
// load chart
RawChart rc = RawChartLoader.Load("res://chart/quickStart.json");
Chart c = new Chart(rc);
// load song
_sp = new SongPlayer(this);
_sp.LoadSong(c.AudioPath);
// define timing window (reference: Arcaea)
TimingWindow tw = new TimingWindow(120 * 1_000); // radius of timing window (lost: 100 ~ 120 [ms])
tw.Partition(25 * 1_000); // pure (exact)
tw.Partition(50 * 1_000); // pure
tw.Partition(100 * 1_000); // far
// define judgement logic
_jq = new JudgementQueue(c.LaneCount, tw);
_jq.AddStrategy(0, new TapNoteJudgementStrategy());
// define key map
StandardInputMapper sim = new StandardInputMapper();
sim.AssignKey(Key.D, 0);
sim.AssignKey(Key.F, 1);
sim.AssignKey(Key.J, 2);
sim.AssignKey(Key.K, 3);
// user offset [us]
long userOffsetUsec = 0;
// create game logic
_lm = new LogicManager(c, _sp, _jq, sim, userOffsetUsec);
AddChild(_lm);
// ======== visual ========
// note pool
_nm = new NoteManager(_judgementLine, VisualVariationSelector.Select4K, 32);
_nm.AddNoteType(Constants.BarlineVisualType, "res://visual/BarlineObject.tscn", 8);
_nm.AddNoteType(0, "res://visual/note/TapNoteObject.tscn", 16);
_nm.UserSpeedPxPerSec = 500;
_nm.Listen(_jq, _lm);
// effect pool
_em = new EffectManager(_judgementLine);
_em.AddEffectType(TapNoteJudgementStrategy.CONTEXT_HIT, "res://visual/effect/HitEffectObject.tscn", 4);
_em.Listen(_jq);
// ======== misc ========
// combo counter
_comboCounter.ComboBreakPartitionIndex = tw.GetLastPartitionIndex();
_jq.NoteJudged += _comboCounter.OnNoteJudged;
}
public override void _UnhandledKeyInput(InputEvent @event)
{
if(@event is InputEventKey ek && ek.Pressed && !ek.Echo)
{
// resume and pause
if(ek.Keycode == Key.Space)
{
if(_sp.Playing) { _sp.Pause(); }
else { _sp.Resume(); }
}
// reset gameplay
if(ek.Keycode == Key.Escape)
{
_lm.Reset();
_comboCounter.Reset();
}
}
}
}
5. ๊ฒ์ํ๋ ์ด ์ฌ ๋์์ธ
Main.tscn์ ๋ฃจํธ ๋
ธ๋์ Main.cs๋ฅผ ๋ถ์ฐฉํ๊ณ , JudgementLine.tscn๊ณผ ComboCounter๋ก ์ฌ์ฉํ Label์ ๋ฐฐ์นํ์ฌ ๊ฒ์ํ๋ ์ด ์ฌ์ ์์ฑํฉ๋๋ค.
๐ผ ์ฑ๋ณด ํ์ผ ๊ตฌ์กฐ
SRGL์ JSON ํ์์ ์ฑ๋ณด ํ์ผ์ ์ฌ์ฉํฉ๋๋ค.
(a) ๋ฉํ๋ฐ์ดํฐ
| Key | typeof(Value) | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|---|
| FormatVersion | string | ํ์ผ ํฌ๋งท ๋ฒ์ (System.Version์ผ๋ก ํ์ฑ ๊ฐ๋ฅํ ๋ฌธ์์ด) |
|
| Title | string | ์ ๋ชฉ | "" |
| Composer | string | ์๊ณก๊ฐ | "" |
| Illustrator | string | ์ผ๋ฌ์คํธ๋ ์ดํฐ | "" |
| Charter | string | ์ฑ๋ณด ์ ์์ | "" |
| DifficultyCategory | int | ๋์ด๋ ๋ถ๋ฅ ์์: Easy = 0, Normal = 1, Hard = 2 |
|
| DifficultyLevel | float | ๋์ด๋ ์์น ์์: 7.8, 9.4, 10.9 |
|
| Description | string | ์ค๋ช | "" |
(b) ์ฑ๋ณด ๋ฐ์ดํฐ
| Key | typeof(Value) | ์ค๋ช |
|---|---|---|
| ImagePath | string | ์จ๋ฒ ์ปค๋ฒ ์ด๋ฏธ์ง ํ์ผ ๊ฒฝ๋ก |
| AudioPath | string | ์์ ํ์ผ ๊ฒฝ๋ก |
| PPQN | long | 4๋ถ์ํ 1๊ฐ๋น ํฑ ์ |
| EndOfTrack | long | ๋ง๋์ ์์ฑ ๋ฒ์ (๋จ์: ํฑ) |
| LaneCount | int | ๋ ์ธ ๊ฐ์ |
| OffsetUsec | long | ์์ ํ์ผ ๊ธฐ์ค, ์ฑ๋ณด์ ์์ ์์ (๋จ์: us) |
| Tempos | RawTempo[] | BPM ๋ณํ |
| TimeSignatures | RawTimeSignature[] | ๋ฐ์ํ ๋ณํ |
| SvChanges | RawSvChange[] | ์คํฌ๋กค ์๋ ๋ณํ |
| Notes | RawNote[] | ๋ ธํธ ๋ฐ์ดํฐ |
(c) RawTempo: BPM ๋ณํ
| Key | ์ถ์ฝํ | typeof(Value) | ์ค๋ช |
|---|---|---|---|
| StartTick | S | long | BPM ๋ณํ ์์ (๋จ์: ํฑ) |
| Bpm | B | double | BPM ๊ฐ |
(d) RawTimeSignature: ๋ฐ์ํ ๋ณํ
| Key | ์ถ์ฝํ | typeof(Value) | ์ค๋ช |
|---|---|---|---|
| StartTick | S | long | ๋ฐ์ํ ๋ณํ ์์ (๋จ์: ํฑ) |
| Numerator | N | int | ๋ฐ์ํ์ ๋ถ์ |
| Denominator | D | int | ๋ฐ์ํ์ ๋ถ๋ชจ |
(e) RawSvChange: ์คํฌ๋กค ์๋(Scroll Velocity) ๋ณํ
| Key | ์ถ์ฝํ | typeof(Value) | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|---|---|
| StartTick | S | long | ์คํฌ๋กค ์๋ ๋ณํ ์์ (๋จ์: ํฑ) |
|
| Multiplier | M | double | ์คํฌ๋กค ์๋ (์ ์ ์๋ = 1.0) | |
| Interpolation | I | InterpolationType | ์คํฌ๋กค ์๋ ๋ณด๊ฐ ๋ฐฉ์ 0 = Step: ๋ณด๊ฐ ์์1 = Linear: ์ ํ ๋ณด๊ฐ2 = Impulse: ์คํฌ๋กค ์ ์ง |
0 = Step |
(f) RawNote: ๋ ธํธ ๋ฐ์ดํฐ
| Key | ์ถ์ฝํ | typeof(Value) | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|---|---|
| StartTick | S | long | ๋
ธํธ ์์ ์์ (๋จ์: ํฑ) |
|
| EndTick | E | long | ๋
ธํธ ์ข
๋ฃ ์์ (๋จ์: ํฑ)(๋กฑ๋ ธํธ: StartTick < EndTick) |
|
| Lane | L | int | ๋ ์ธ ์ธ๋ฑ์ค | |
| LogicType | J | int | ํ์ ๋ก์ง์ ์ข
๋ฅ ( JudgementQueue.AddStrategy()์ ๋งค๊ฐ๋ณ์ logicType์ ํด๋น) |
0 |
| VisualType | V | int | ๋
ธํธ ์ค๋ธ์ ํธ์ ์ข
๋ฅ ( NoteManager.AddNoteType()์ ๋งค๊ฐ๋ณ์ visualType์ ํด๋น) |
0 |
| TickRate | T | int | ๋กฑ๋
ธํธ ํฑ ์์ฑ์, (Denominator)๋ถ์ํ 1๊ฐ๋ฅผ TickRate๊ฐ๋ก ๋ถํ |
|
| NoteOptions | O | NoteOptions | ๋
ธํธ๋ณ ์ถ๊ฐ ์ต์
1 << 0 = Dummy: ๋๋ฏธ ๋
ธํธAllowHoldAgain: ๋์น ๋กฑ๋
ธํธ๋ฅผ ๋ค์ ํ๋ํ ์ ์์CheckRelease: ๋ผ๋ ํ์ ํ์ฑํ |
0 |
๐ ๋ผ์ด์ ์ค
SRGL์ MIT ๋ผ์ด์ ์ค๋ฅผ ๋ฐ๋ฆ ๋๋ค.
SRGL is a Godot 4.6 (.NET) based keyboard rhythm game library.
It is specialized for creating casual Vertical Scrolling Rhythm Games (VSRG).
Currently supports 2D only.
# Features
- Complete separation of game logic and visuals.
- Uses JSON format for chart files.
- Automatically generates long note ticks and barlines based on time signatures.
- Supports basic scroll velocity (SV) changes and stop effects.
- Automatically compensates for audio drift every frame.
- Highly customizable timing windows, input systems, user offsets, and scroll speeds.
- Easily add custom judgement logic for different note types via the IJudgementStrategy interface.
- Design visual elements effortlessly with JudgementLine, NoteObject, and EffectObject classes.
See GitHub for more information.
Reviews
Quick Information
SRGL is a Godot 4.6 (.NET) based keyboard rhythm game library.It is specialized for creating casual Vertical Scrolling Rhythm Games (VSRG).Currently supports 2D only.# Features- Complete separation of game logic and visuals.- Uses JSON format for chart files.- Automatically generates long note ticks and barlines based on time signatures.- Supports basic scroll velocity (SV) changes and stop effects.- Automatically compensates for audio drift every frame.- Highly customizable timing windows, input systems, user offsets, and scroll speeds.- Easily add custom judgement logic for different note types via the IJudgementStrategy interface.- Design visual elements effortlessly with JudgementLine, NoteObject, and EffectObject classes.See GitHub for more information.