diff --git a/src/chart-standard/chart-format/rpe/beat.md b/src/chart-standard/chart-format/rpe/beat.md index 104c280..506b575 100644 --- a/src/chart-standard/chart-format/rpe/beat.md +++ b/src/chart-standard/chart-format/rpe/beat.md @@ -6,4 +6,54 @@ double beat = RPEBeat[1] / RPEBeat[2] + RPEBeat[0]; double seconds = BPM * beat / 60; ``` -多BPM计算方式待补充。 \ No newline at end of file +多BPM计算方式待补充。 + +## Python 示例 +- 若 `self.BPMList` 为一个 `list[BPMEvent]` +- `BPMEvent`定义: +```python +@dataclass +class BPMEvent: + startTime: Beat + bpm: float +``` +- `sec2beat` 中 `t` 为秒数, `bpmfactor` 为判定线中的 `bpmfactor` 字段` +- `beat2sec` 中 `t` 为拍数, `bpmfactor` 为判定线中的 `bpmfactor` 字段` +- 且 `beat2sec(sec2beat(x)) == x` 与 `sec2beat(beat2sec(x)) == x` 的结果均为 `True` +- 则有: +```python +def sec2beat(self, t: float, bpmfactor: float): + beat = 0.0 + for i, e in enumerate(self.BPMList): + bpmv = e.bpm * bpmfactor + if i != len(self.BPMList) - 1: + et_beat = self.BPMList[i + 1].startTime.value - e.startTime.value + et_sec = et_beat * (60 / bpmv) + + if t >= et_sec: + beat += et_beat + t -= et_sec + else: + beat += t / (60 / bpmv) + break + else: + beat += t / (60 / bpmv) + return beat + +def beat2sec(self, t: float, bpmfactor: float): + sec = 0.0 + for i, e in enumerate(self.BPMList): + bpmv = e.bpm * bpmfactor + if i != len(self.BPMList) - 1: + et_beat = self.BPMList[i + 1].startTime.value - e.startTime.value + + if t >= et_beat: + sec += et_beat * (60 / bpmv) + t -= et_beat + else: + sec += t * (60 / bpmv) + break + else: + sec += t * (60 / bpmv) + return sec +``` \ No newline at end of file diff --git a/src/chart-standard/chart-format/rpe/event.md b/src/chart-standard/chart-format/rpe/event.md index e31abeb..1eec558 100644 --- a/src/chart-standard/chart-format/rpe/event.md +++ b/src/chart-standard/chart-format/rpe/event.md @@ -26,4 +26,65 @@ - 若不透明度事件数值为负数,则会在隐藏判定线的同时隐藏这条判定线上的所有音符。(根据作者所述,此功能是废弃的非法功能但它仍然有效) - 音符流速事件只有上述的 `startTime`、`endTime`、`start`、`end`、`linkgroup` 字段。 - 音符流速事件**不支持缓动**,即只有线性变化。 - - 流速为负数时,音符会向上飞,若音符为 `Hold`,在 `Hold` 尾出现时,整个音符都会出现(即使 `Hold` 还没完全回到判定线正面)。 \ No newline at end of file + - 流速为负数时,音符会向上飞,若音符为 `Hold`,在 `Hold` 尾出现时,整个音符都会出现(即使 `Hold` 还没完全回到判定线正面)。 + +## Python 示例 (不支持bezier): +- 定义 `rpe_easing.py` (略) +- 定义 `Beat`: +```python +@dataclass +class Beat: + var1: int + var2: int + var3: int + + def __post_init__(self): + self.value = self.var1 + (self.var2 / self.var3) + self._hash = hash(self.value) + + def __hash__(self) -> int: + return self._hash +``` +- 定义 `LineEvent` +```python +@dataclass +class LineEvent: + startTime: Beat + endTime: Beat + start: float|str|list[int] + end: float|str|list[int] + easingType: int + easingFunc: typing.Callable[[float], float] = rpe_easing.ease_funcs[0] + + def __post_init__(self): + if not isinstance(self.easingType, int): self.easingType = 1 + self.easingType = 1 if self.easingType < 1 else (len(rpe_easing.ease_funcs) if self.easingType > len(rpe_easing.ease_funcs) else self.easingType) + self.easingFunc = rpe_easing.ease_funcs[self.easingType - 1] +``` +- 定义 `easing_interpolation` +```python +def easing_interpolation( + t: float, st: float, + et: float, sv: float, + ev: float, f: typing.Callable[[float], float] +): + if t == st: return sv + return f((t - st) / (et - st)) * (ev - sv) + sv +``` +- `default` 为事件默认值 +- 则有: +```python +def GetEventValue(t: float, es: list[LineEvent], default): + for e in es: + if e.startTime.value <= t <= e.endTime.value: + if isinstance(e.start, float|int): + return easing_interpolation(t, e.startTime.value, e.endTime.value, e.start, e.end, e.easingFunc) + elif isinstance(e.start, str): + return e.start + elif isinstance(e.start, list): + r = easing_interpolation(t, e.startTime.value, e.endTime.value, e.start[0], e.end[0], e.easingFunc) + g = easing_interpolation(t, e.startTime.value, e.endTime.value, e.start[1], e.end[1], e.easingFunc) + b = easing_interpolation(t, e.startTime.value, e.endTime.value, e.start[2], e.end[2], e.easingFunc) + return (r, g, b) + return default +``` \ No newline at end of file diff --git a/src/chart-standard/chart-format/rpe/extend.md b/src/chart-standard/chart-format/rpe/extend.md index c97e016..627cc18 100644 --- a/src/chart-standard/chart-format/rpe/extend.md +++ b/src/chart-standard/chart-format/rpe/extend.md @@ -65,6 +65,40 @@ RPE允许你设置判定线的 `Texture` 字段来修改判定线的纹理,当 你可以在[这个网站](https://easings.net/zh-cn)查看它们的函数等信息。 +### Python 缓动示例 +```python +import math +import typing - - +ease_funcs:list[typing.Callable[[float], float]] = [ + lambda t: t, # linear - 1 + lambda t: math.sin((t * math.pi) / 2), # out sine - 2 + lambda t: 1 - math.cos((t * math.pi) / 2), # in sine - 3 + lambda t: 1 - (1 - t) * (1 - t), # out quad - 4 + lambda t: t ** 2, # in quad - 5 + lambda t: -(math.cos(math.pi * t) - 1) / 2, # io sine - 6 + lambda t: 2 * (t ** 2) if t < 0.5 else 1 - (-2 * t + 2) ** 2 / 2, # io quad - 7 + lambda t: 1 - (1 - t) ** 3, # out cubic - 8 + lambda t: t ** 3, # in cubic - 9 + lambda t: 1 - (1 - t) ** 4, # out quart - 10 + lambda t: t ** 4, # in quart - 11 + lambda t: 4 * (t ** 3) if t < 0.5 else 1 - (-2 * t + 2) ** 3 / 2, # io cubic - 12 + lambda t: 8 * (t ** 4) if t < 0.5 else 1 - (-2 * t + 2) ** 4 / 2, # io quart - 13 + lambda t: 1 - (1 - t) ** 5, # out quint - 14 + lambda t: t ** 5, # in quint - 15 + lambda t: 1 if t == 1 else 1 - 2 ** (-10 * t), # out expo - 16 + lambda t: 0 if t == 0 else 2 ** (10 * t - 10), # in expo - 17 + lambda t: (1 - (t - 1) ** 2) ** 0.5, # out circ - 18 + lambda t: 1 - (1 - t ** 2) ** 0.5, # in circ - 19 + lambda t: 1 + 2.70158 * ((t - 1) ** 3) + 1.70158 * ((t - 1) ** 2), # out back - 20 + lambda t: 2.70158 * (t ** 3) - 1.70158 * (t ** 2), # in back - 21 + lambda t: (1 - (1 - (2 * t) ** 2) ** 0.5) / 2 if t < 0.5 else (((1 - (-2 * t + 2) ** 2) ** 0.5) + 1) / 2, # io circ - 22 + lambda t: ((2 * t) ** 2 * ((2.5949095 + 1) * 2 * t - 2.5949095)) / 2 if t < 0.5 else ((2 * t - 2) ** 2 * ((2.5949095 + 1) * (t * 2 - 2) + 2.5949095) + 2) / 2, # io back - 23 + lambda t: 0 if t == 0 else (1 if t == 1 else 2 ** (-10 * t) * math.sin((t * 10 - 0.75) * (2 * math.pi / 3)) + 1), # out elastic - 24 + lambda t: 0 if t == 0 else (1 if t == 1 else - 2 ** (10 * t - 10) * math.sin((t * 10 - 10.75) * (2 * math.pi / 3))), # in elastic - 25 + lambda t: 7.5625 * (t ** 2) if (t < 1 / 2.75) else (7.5625 * (t - (1.5 / 2.75)) * (t - (1.5 / 2.75)) + 0.75 if (t < 2 / 2.75) else (7.5625 * (t - (2.25 / 2.75)) * (t - (2.25 / 2.75)) + 0.9375 if (t < 2.5 / 2.75) else (7.5625 * (t - (2.625 / 2.75)) * (t - (2.625 / 2.75)) + 0.984375))), # out bounce - 26 + lambda t: 1 - (7.5625 * ((1 - t) ** 2) if ((1 - t) < 1 / 2.75) else (7.5625 * ((1 - t) - (1.5 / 2.75)) * ((1 - t) - (1.5 / 2.75)) + 0.75 if ((1 - t) < 2 / 2.75) else (7.5625 * ((1 - t) - (2.25 / 2.75)) * ((1 - t) - (2.25 / 2.75)) + 0.9375 if ((1 - t) < 2.5 / 2.75) else (7.5625 * ((1 - t) - (2.625 / 2.75)) * ((1 - t) - (2.625 / 2.75)) + 0.984375)))), # in bounce - 27 + lambda t: (1 - (7.5625 * ((1 - 2 * t) ** 2) if ((1 - 2 * t) < 1 / 2.75) else (7.5625 * ((1 - 2 * t) - (1.5 / 2.75)) * ((1 - 2 * t) - (1.5 / 2.75)) + 0.75 if ((1 - 2 * t) < 2 / 2.75) else (7.5625 * ((1 - 2 * t) - (2.25 / 2.75)) * ((1 - 2 * t) - (2.25 / 2.75)) + 0.9375 if ((1 - 2 * t) < 2.5 / 2.75) else (7.5625 * ((1 - 2 * t) - (2.625 / 2.75)) * ((1 - 2 * t) - (2.625 / 2.75)) + 0.984375))))) / 2 if t < 0.5 else (1 +(7.5625 * ((2 * t - 1) ** 2) if ((2 * t - 1) < 1 / 2.75) else (7.5625 * ((2 * t - 1) - (1.5 / 2.75)) * ((2 * t - 1) - (1.5 / 2.75)) + 0.75 if ((2 * t - 1) < 2 / 2.75) else (7.5625 * ((2 * t - 1) - (2.25 / 2.75)) * ((2 * t - 1) - (2.25 / 2.75)) + 0.9375 if ((2 * t - 1) < 2.5 / 2.75) else (7.5625 * ((2 * t - 1) - (2.625 / 2.75)) * ((2 * t - 1) - (2.625 / 2.75)) + 0.984375))))) / 2, # io bounce - 28 + lambda t: 0 if t == 0 else (1 if t == 0 else (-2 ** (20 * t - 10) * math.sin((20 * t - 11.125) * ((2 * math.pi) / 4.5))) / 2 if t < 0.5 else (2 ** (-20 * t + 10) * math.sin((20 * t - 11.125) * ((2 * math.pi) / 4.5))) / 2 + 1) # io elastic - 29 +] +``` \ No newline at end of file diff --git a/src/chart-standard/chart-format/rpe/judgeLine.md b/src/chart-standard/chart-format/rpe/judgeLine.md index 90e7c63..d68c654 100644 --- a/src/chart-standard/chart-format/rpe/judgeLine.md +++ b/src/chart-standard/chart-format/rpe/judgeLine.md @@ -26,3 +26,408 @@ - 若层级为空,在某个版本之前,字段为 `null` ,在某个版本及以后,空层级无字段。(当前已知至少在 `143` 版本时无字段) - 若所有层级都为空,`eventLayers` 字段不会出现。 +## 事件插值 + +### Python 示例 +- 定义 `rpe_easing.py` (略) +- 定义 `Chart_Objects_Rpe.py` (部分略) +```python +from __future__ import annotations + +import typing +from dataclasses import dataclass +from functools import lru_cache, cache + +import rpe_easing + +def easing_interpolation( + t: float, st: float, + et: float, sv: float, + ev: float, f: typing.Callable[[float], float] +): + if t == st: return sv + return f((t - st) / (et - st)) * (ev - sv) + sv + +def conrpepos(x: float, y: float): + return (x + 675) / 1350, 1.0 - (y + 450) / 900 + +def _init_events(es: list[LineEvent]): + aes = [] + for i, e in enumerate(es): + if i != len(es) - 1: + ne = es[i + 1] + if e.endTime.value < ne.startTime.value: + aes.append(LineEvent(e.endTime, ne.startTime, e.end, e.end, 1)) + es.extend(aes) + es.sort(key = lambda x: x.startTime.value) + if es: es.append(LineEvent(es[-1].endTime, Beat(31250000, 0, 1), es[-1].end, es[-1].end, 1)) + +@dataclass +class Beat: + var1: int + var2: int + var3: int + + def __post_init__(self): + self.value = self.var1 + (self.var2 / self.var3) + self._hash = hash(self.value) + + def __hash__(self) -> int: + return self._hash + +@dataclass +class Note: + ... + +@dataclass +class LineEvent: + ... + easingFunc: typing.Callable[[float], float] = rpe_easing.ease_funcs[0] + + def __post_init__(self): + if not isinstance(self.easingType, int): self.easingType = 1 + self.easingType = 1 if self.easingType < 1 else (len(rpe_easing.ease_funcs) if self.easingType > len(rpe_easing.ease_funcs) else self.easingType) + self.easingFunc = rpe_easing.ease_funcs[self.easingType - 1] + +@dataclass +class EventLayer: + ... + + def __post_init__(self): + self.speedEvents.sort(key = lambda x: x.startTime.value) + self.moveXEvents.sort(key = lambda x: x.startTime.value) + self.moveYEvents.sort(key = lambda x: x.startTime.value) + self.rotateEvents.sort(key = lambda x: x.startTime.value) + self.alphaEvents.sort(key = lambda x: x.startTime.value) + + _init_events(self.speedEvents) + _init_events(self.moveXEvents) + _init_events(self.moveYEvents) + _init_events(self.rotateEvents) + _init_events(self.alphaEvents) + +@dataclass +class Extended: + ... + + def __post_init__(self): + self.scaleXEvents.sort(key = lambda x: x.startTime.value) + self.scaleYEvents.sort(key = lambda x: x.startTime.value) + self.colorEvents.sort(key = lambda x: x.startTime.value) + self.textEvents.sort(key = lambda x: x.startTime.value) + + _init_events(self.scaleXEvents) + _init_events(self.scaleYEvents) + _init_events(self.colorEvents) + _init_events(self.textEvents) + +@dataclass +class MetaData: + ... + +@dataclass +class BPMEvent: + ... + +@dataclass +class JudgeLine: + ... + + def GetEventValue(self, t: float, es: list[LineEvent], default): + for e in es: + if e.startTime.value <= t <= e.endTime.value: + if isinstance(e.start, float|int): + return easing_interpolation(t, e.startTime.value, e.endTime.value, e.start, e.end, e.easingFunc) + elif isinstance(e.start, str): + return e.start + elif isinstance(e.start, list): + r = easing_interpolation(t, e.startTime.value, e.endTime.value, e.start[0], e.end[0], e.easingFunc) + g = easing_interpolation(t, e.startTime.value, e.endTime.value, e.start[1], e.end[1], e.easingFunc) + b = easing_interpolation(t, e.startTime.value, e.endTime.value, e.start[2], e.end[2], e.easingFunc) + return (r, g, b) + return default + + @lru_cache + def GetPos(self, t: float, master: Rpe_Chart) -> list[float, float]: + linePos = [0.0, 0.0] + for layer in self.eventLayers: + linePos[0] += self.GetEventValue(t, layer.moveXEvents, 0.0) + linePos[1] += self.GetEventValue(t, layer.moveYEvents, 0.0) + if self.father != -1: + try: + fatherPos = master.JudgeLineList[self.father].GetPos(t, master) + linePos = list(map(lambda x, y: x + y, linePos, fatherPos)) + except IndexError: + pass + return linePos + + def GetState(self, t: float, defaultColor: tuple[int, int, int], master: Rpe_Chart) -> tuple[tuple[float, float], float, float, tuple[int, int, int], float, float, str|None]: + "linePos, lineAlpha, lineRotate, lineColor, lineScaleX, lineScaleY, lineText" + linePos = self.GetPos(t, master) + lineAlpha = 0.0 + lineRotate = 0.0 + lineColor = defaultColor if not self.extended.textEvents else (255, 255, 255) + lineScaleX = 1.0 + lineScaleY = 1.0 + lineText = None + + for layer in self.eventLayers: + lineAlpha += self.GetEventValue(t, layer.alphaEvents, 0.0 if (t >= 0.0 or self.attachUI is not None) else -255.0) + lineRotate += self.GetEventValue(t, layer.rotateEvents, 0.0) + + if self.extended: + lineScaleX = self.GetEventValue(t, self.extended.scaleXEvents, lineScaleX) + lineScaleY = self.GetEventValue(t, self.extended.scaleYEvents, lineScaleY) + lineColor = self.GetEventValue(t, self.extended.colorEvents, lineColor) + lineText = self.GetEventValue(t, self.extended.textEvents, lineText) + + return conrpepos(*linePos), lineAlpha / 255, lineRotate, lineColor, lineScaleX, lineScaleY, lineText + + def __hash__(self) -> int: + return id(self) + + def __eq__(self, oth) -> bool: + if isinstance(oth, JudgeLine): + return self is oth + return False + +@dataclass +class Rpe_Chart: + ... + + def __post_init__(self): + self.BPMList.sort(key=lambda x: x.startTime.value) + + @cache + def sec2beat(self, t: float, bpmfactor: float): + beat = 0.0 + for i, e in enumerate(self.BPMList): + bpmv = e.bpm * bpmfactor + if i != len(self.BPMList) - 1: + et_beat = self.BPMList[i + 1].startTime.value - e.startTime.value + et_sec = et_beat * (60 / bpmv) + + if t >= et_sec: + beat += et_beat + t -= et_sec + else: + beat += t / (60 / bpmv) + break + else: + beat += t / (60 / bpmv) + return beat + + @cache + def beat2sec(self, t: float, bpmfactor: float): + sec = 0.0 + for i, e in enumerate(self.BPMList): + bpmv = e.bpm * bpmfactor + if i != len(self.BPMList) - 1: + et_beat = self.BPMList[i + 1].startTime.value - e.startTime.value + + if t >= et_beat: + sec += et_beat * (60 / bpmv) + t -= et_beat + else: + sec += t * (60 / bpmv) + break + else: + sec += t * (60 / bpmv) + return sec + + def __hash__(self) -> int: + return id(self) + + def __eq__(self, oth) -> bool: + if isinstance(oth, JudgeLine): + return self is oth + return False +``` +- 加载谱面: +```python +def load(chart: dict): + meta = chart.get("META", {}) + rpe_chart_obj = Chart_Objects_Rpe.Rpe_Chart( + META = Chart_Objects_Rpe.MetaData( + RPEVersion = meta.get("RPEVersion", -1), + offset = meta.get("offset", 0), + name = meta.get("name", "Unknow"), + id = meta.get("id", "-1"), + song = meta.get("song", "Unknow"), + background = meta.get("background", "Unknow"), + composer = meta.get("composer", "Unknow"), + charter = meta.get("charter", "Unknow"), + level = meta.get("level", "Unknow"), + ), + BPMList = [ + Chart_Objects_Rpe.BPMEvent( + startTime = Chart_Objects_Rpe.Beat( + *BPMEvent_item.get("startTime", [0, 0, 1]) + ), + bpm = BPMEvent_item.get("bpm", 140) + ) + for BPMEvent_item in chart.get("BPMList", []) + ], + JudgeLineList = [ + Chart_Objects_Rpe.JudgeLine( + isCover = judgeLine_item.get("isCover", 1), + Texture = judgeLine_item.get("Texture", "line.png"), + attachUI = judgeLine_item.get("attachUI", None), + bpmfactor = judgeLine_item.get("bpmfactor", 1.0), + father = judgeLine_item.get("father", -1), + zOrder = judgeLine_item.get("zOrder", 0), + eventLayers = [ + Chart_Objects_Rpe.EventLayer( + speedEvents = [ + Chart_Objects_Rpe.LineEvent( + startTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("endTime", [0, 0, 1]) + ), + start = LineEvent_item.get("start", 0.0), + end = LineEvent_item.get("end", 0.0), + easingType = 1 + ) + for LineEvent_item in EventLayer_item.get("speedEvents", []) + ] if EventLayer_item.get("speedEvents", []) is not None else [], + moveXEvents = [ + Chart_Objects_Rpe.LineEvent( + startTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("endTime", [0, 0, 1]) + ), + start = LineEvent_item.get("start", 0.0), + end = LineEvent_item.get("end", 0.0), + easingType = LineEvent_item.get("easingType", 1) + ) for LineEvent_item in EventLayer_item.get("moveXEvents", []) + ] if EventLayer_item.get("moveXEvents", []) is not None else [], + moveYEvents = [ + Chart_Objects_Rpe.LineEvent( + startTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("endTime", [0, 0, 1]) + ), + start = LineEvent_item.get("start", 0.0), + end = LineEvent_item.get("end", 0.0), + easingType = LineEvent_item.get("easingType", 1) + ) for LineEvent_item in EventLayer_item.get("moveYEvents", []) + ] if EventLayer_item.get("moveYEvents", []) is not None else [], + rotateEvents = [ + Chart_Objects_Rpe.LineEvent( + startTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("endTime", [0, 0, 1]) + ), + start = LineEvent_item.get("start", 0.0), + end = LineEvent_item.get("end", 0.0), + easingType = LineEvent_item.get("easingType", 1) + ) for LineEvent_item in EventLayer_item.get("rotateEvents", []) + ] if EventLayer_item.get("rotateEvents", []) is not None else [], + alphaEvents = [ + Chart_Objects_Rpe.LineEvent( + startTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("endTime", [0, 0, 1]) + ), + start = LineEvent_item.get("start", 0.0), + end = LineEvent_item.get("end", 0.0), + easingType = LineEvent_item.get("easingType", 1) + ) for LineEvent_item in EventLayer_item.get("alphaEvents", []) + ] if EventLayer_item.get("alphaEvents", []) is not None else [] + ) if EventLayer_item is not None else Chart_Objects_Rpe.EventLayer(speedEvents = [], moveXEvents = [], moveYEvents = [], rotateEvents = [], alphaEvents = []) + for EventLayer_item in judgeLine_item.get("eventLayers", []) + ], + extended = Chart_Objects_Rpe.Extended( + scaleXEvents = [ + Chart_Objects_Rpe.LineEvent( + startTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("endTime", [0, 0, 1]) + ), + start = LineEvent_item.get("start", 1.0), + end = LineEvent_item.get("end", 1.0), + easingType = LineEvent_item.get("easingType", 1) + ) for LineEvent_item in judgeLine_item.get("extended", {}).get("scaleXEvents", []) + ] if judgeLine_item.get("extended", {}).get("scaleXEvents", []) is not None else [], + scaleYEvents = [ + Chart_Objects_Rpe.LineEvent( + startTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("endTime", [0, 0, 1]) + ), + start = LineEvent_item.get("start", 1.0), + end = LineEvent_item.get("end", 1.0), + easingType = LineEvent_item.get("easingType", 1) + ) for LineEvent_item in judgeLine_item.get("extended", {}).get("scaleYEvents", []) + ] if judgeLine_item.get("extended", {}).get("scaleYEvents", []) is not None else [], + colorEvents = [ + Chart_Objects_Rpe.LineEvent( + startTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("endTime", [0, 0, 1]) + ), + start = LineEvent_item.get("start", [255, 255, 255]), + end = LineEvent_item.get("end", [255, 255, 255]), + easingType = LineEvent_item.get("easingType", 1) + ) for LineEvent_item in judgeLine_item.get("extended", {}).get("colorEvents", []) + ] if judgeLine_item.get("extended", {}).get("colorEvents", []) is not None else [], + textEvents = [ + Chart_Objects_Rpe.LineEvent( + startTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *LineEvent_item.get("endTime", [0, 0, 1]) + ), + start = LineEvent_item.get("start", ""), + end = LineEvent_item.get("end", ""), + easingType = LineEvent_item.get("easingType", 1) + ) for LineEvent_item in judgeLine_item.get("extended", {}).get("textEvents", []) + ] if judgeLine_item.get("extended", {}).get("textEvents", []) is not None else [], + ) if judgeLine_item.get("extended", {}) is not None else None, + notes = [ + Chart_Objects_Rpe.Note( + type = Note_item.get("type", 1), + startTime = Chart_Objects_Rpe.Beat( + *Note_item.get("startTime", [0, 0, 1]) + ), + endTime = Chart_Objects_Rpe.Beat( + *Note_item.get("endTime", [0, 0, 1]) + ), + positionX = Note_item.get("positionX", 0), + above = Note_item.get("above", 1), + isFake = Note_item.get("isFake", False), + speed = Note_item.get("speed", 1.0), + yOffset = Note_item.get("yOffset", 0.0), + visibleTime = Note_item.get("visibleTime", 999999.0), + width = Note_item.get("size", 1.0), + alpha = Note_item.get("alpha", 255), + ) + for Note_item in judgeLine_item.get("notes", []) + ] + ) + for judgeLine_item in chart.get("judgeLineList", []) + ] + ) + + return rpe_chart_obj + +result = load({}) # 这里传入你的谱面 +``` +- 最后调用 `result.JudgeLineList[i].GetState` , 并传入当前拍数为 `t` 和判定线默认颜色为 `defaultColor` , 和谱面对象 `master` 即可获取当前拍数下的判定线全部状态 \ No newline at end of file