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.See GitHub for more information.

Supported Engine Version
4.3
Version String
0.1.0
License Version
MIT
Support Level
community
Modified Date
19 minutes 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.

See GitHub for more information.

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.See GitHub for more information.

Supported Engine Version
4.3
Version String
0.1.0
License Version
MIT
Support Level
community
Modified Date
19 minutes 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