Check out our latest project โœจ OpenChapter.io: free ebooks the way its meant to be ๐Ÿ“–

Simple Rhythm Game Library

An asset by joung2321
The page banner background of a mountain and forest
Simple Rhythm Game Library hero image

Quick Information

0 ratings
Simple Rhythm Game Library icon image
joung2321
Simple Rhythm Game Library

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.

Supported Engine Version
4.3
Version String
0.1.0
License Version
MIT
Support Level
community
Modified Date
8 hours ago
Git URL
Issue URL
icon

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๋ฅผ ์—๋””ํ„ฐ๋กœ ์—ด์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค:

  1. ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค.
  2. Main.tscn์˜ JudgementLine์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
  3. Inspector์—์„œ, Judgement Points ์˜†์˜ ๐Ÿ”„๏ธ๋ฅผ ๋ˆ„๋ฆ…๋‹ˆ๋‹ค.
  4. ํฌ๊ธฐ๊ฐ€ 4์ธ ๋ฐฐ์—ด์ด ๋ณต์›๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๐Ÿš€ ํ€ต ์Šคํƒ€ํŠธ

๋ณธ ์ ˆ์—์„œ๋Š” ๊ฐ„๋‹จํ•œ 4ํ‚ค ๋ฆฌ๋“ฌ๊ฒŒ์ž„์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
์ž์„ธํ•œ ๋‚ด์šฉ์€ demo/QuickStart๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

1. ์Œ์› ํŒŒ์ผ ์ค€๋น„

์•„๋ž˜ ์•…๋ณด์— ๋Œ€ํ•œ ์Œ์› ํŒŒ์ผ์„ ์ค€๋น„ํ•ฉ๋‹ˆ๋‹ค. ํ™•์žฅ์ž๋Š” OGG๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.
๋ณธ ์˜ˆ์ œ์—์„œ๋Š” quickStart.ogg๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. README quickStart_score โš ๏ธ์ฃผ์˜: 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์„ ๋ฐฐ์น˜ํ•˜์—ฌ ๊ฒŒ์ž„ํ”Œ๋ ˆ์ด ์”ฌ์„ ์™„์„ฑํ•ฉ๋‹ˆ๋‹ค. README quickStart_main

๐ŸŽผ ์ฑ„๋ณด ํŒŒ์ผ ๊ตฌ์กฐ

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: ๋”๋ฏธ ๋…ธํŠธ
1 << 1 = AllowHoldAgain: ๋†“์นœ ๋กฑ๋…ธํŠธ๋ฅผ ๋‹ค์‹œ ํ™€๋“œํ•  ์ˆ˜ ์žˆ์Œ
1 << 2 = 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.

Reviews

0 ratings

Your Rating

Headline must be at least 3 characters but not more than 50
Review must be at least 5 characters but not more than 500
Please sign in to add a review

Quick Information

0 ratings
Simple Rhythm Game Library icon image
joung2321
Simple Rhythm Game Library

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.

Supported Engine Version
4.3
Version String
0.1.0
License Version
MIT
Support Level
community
Modified Date
8 hours ago
Git URL
Issue URL

Open Source

Released under the AGPLv3 license

Plug and Play

Browse assets directly from Godot

Community Driven

Created by developers for developers