File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -356,3 +356,12 @@ public sealed class FooGenerator : IGenerator<MaiChart>
356356 - 详见上文[多语言(i18n)相关](# 多语言i18n相关)部分的说明。
3573577. ** CLI** (可选):如需实现CLI,可在` Program.cs` 中增加相应的功能。
358358
359+ # ## Ongeki谱面的数据结构实现
360+ - 关于ogkr格式中各种指令的含义,可参考[ogkr格式详解](https://github.com/MikiraSora/OngekiFumianDescription/blob/master/ongeki.md)和[补充说明](https://github.com/MikiraSora/OngekiFumianDescription/blob/master/description.md)。
361+ - ogkr格式当中,大量的采用列举Pallete列表+使用ID引用具体的数据的实现方式。
362+ - 例如子弹` BLT` 指令,要通过` strId` 引用` [B_PALLETE]` 中内容,Tap、Hold等音符需要通过` groupId` 引用` Lane` 相关的内容,等
363+ - 而我们的数据结构实现的过程中,采用直接的对象引用而不是ID字符串引用;同时对于` BulletPallete` 和` Lane` ,谱面数据结构中** 不会保存其ID** 。
364+ - 因此,推荐的做法是:
365+ - 在Parser的解析过程中,在Parser内部缓存ID和具体对象的映射关系,每次解析完一个` [B_PALLETE]` 或` Lane` 对象时就把它放进该内部缓存中,以供后面解析具体的` BLT` 、Tap时直接找到对象。
366+ - 在Generator的生成过程中,也是在内部建立缓存、为每个相关对象编好ID,然后生成引用这些对象的具体音符的时候直接写入编号。
367+ - 也就是说,** ID相关的逻辑应当由Parser和Generator内部自行实现,谱面数据结构中不会保存ID。**
Original file line number Diff line number Diff line change 1+ using MuConvert . utils ;
2+ using Rationals ;
3+
4+ namespace MuConvert . ogk ;
5+
6+
7+ public interface IBullet
8+ {
9+ public BulletPallete ? Detail { get ; set ; }
10+ }
11+
12+ /**
13+ * 声明子弹的细节行为,包括运动路径、速度、类型、尺寸等。
14+ *
15+ * record各项声明的默认值,对应于一种子弹静止不动的最简单情况,也就是官谱中的A0(绿谱当中能见到的子弹基本都是这种情况)
16+ */
17+ public record BulletPallete (
18+ BulletShooter Shooter = BulletShooter . UPS , // 发出子弹的位置
19+
20+ bool TargetToPlayer = false , // true表示该子弹射向玩家(取子弹生成时玩家的当前位置为目标位置);false表示子弹射向基于TargetOffset所算出的目标位置。
21+ int TargetOffset = 0 , // 子弹的终点位置相对于起点位置的偏移量 (仅当TargetToPlayer为false时有效)。多数情况下,此值设置为0,得到的是不动的子弹,反之就是动的子弹。
22+ int RandomOffsetDist = 0 , // 如果不为0,则是随机子弹,结束位置会在正常位置的基础上随机偏移至多N格。
23+
24+ decimal Speed = 1.0m ,
25+ BulletSize Size = BulletSize . N ,
26+ BulletType Type = BulletType . CIR
27+ ) ;
28+
29+ public class Bullet : IBullet
30+ {
31+ public BulletPallete Detail { get ; set ; } = new ( ) ;
32+
33+ public Rational Time ;
34+ public int Pos ;
35+
36+ public BulletDamage Damage = BulletDamage . NML ;
37+ }
38+
39+ public record BeamPoint ( Rational Time , int Pos , int Width ) : OgkLanePoint ( Time , Pos ) ;
40+ public class Beam : OgkBaseLane < BeamPoint > , IBullet
41+ {
42+ public int ObliqueOffset = 0 ; // 如果不为0,表示这个激光是倾斜激光(OBS),否则是一般的激光(BMS)
43+
44+ // 激光是没有IBullet的Detail属性的。但接口还必须实现一个,所以实现成返回null、不准设置。
45+ public BulletPallete ? Detail { get => null ; set => throw new InvalidOperationException ( "Beam has no bullet detail!" ) ; }
46+ }
Original file line number Diff line number Diff line change 1+ using MuConvert . utils ;
2+ using Rationals ;
3+
4+ namespace MuConvert . ogk ;
5+
6+ public record OgkLanePoint ( Rational Time , int Pos ) ;
7+
8+ public abstract class OgkBaseLane < T > where T : OgkLanePoint
9+ {
10+ public List < T > Points ;
11+
12+ public Rational Time => Points . First ( ) . Time ;
13+ }
14+
15+ public class Lane ( LaneType type ) : OgkBaseLane < OgkLanePoint >
16+ {
17+ public LaneType Type = type ;
18+ public bool IsTransparent = false ;
19+ }
20+
21+ public class ColorfulLane ( ) : Lane ( LaneType . Colorful )
22+ {
23+ public ColorfulLaneColor Color ;
24+ public int Brightness ;
25+ }
26+
27+ // 这里的Start,是相对于Wall的开始时刻的!不是绝对时间!
28+ public record Block ( Rational Start , Rational Duration , int Pos , int EndPos ) ;
29+
30+ public class Wall ( ) : Lane ( LaneType . Wall )
31+ {
32+ public Direction Direction ;
33+
34+ /**
35+ * 给墙壁上添加“阻挡区域”。
36+ * 注意!Block中的Start是相对于Wall的起始时刻(Time)的,而不是绝对时间。
37+ *
38+ * 默认的“墙壁”描述的仅仅是场地的有效范围,显然玩家摇杆是可以穿越出去的;
39+ * 但如果这里加上Blocks的话,玩家摇杆就无法穿越出去了。
40+ */
41+ public List < Block > Blocks = [ ] ;
42+ }
43+
44+ public class EnemyMovement : OgkBaseLane < OgkLanePoint > ;
Original file line number Diff line number Diff line change 11using MuConvert . chart ;
2+ using MuConvert . utils ;
3+ using Rationals ;
24
35namespace MuConvert . ogk ;
46
57public class OgkChart : BaseChart < OgkNote >
68{
9+ public string Designer { get ; set ; } = "" ; // 谱师
10+
11+ // 全局声明的“子弹伤害类型与伤害数值的映射关系”。绝大多数情况下都是这个默认值,不需要做改动。
12+ public Dictionary < BulletDamage , decimal > BulletDamages = new ( ) {
13+ [ BulletDamage . NML ] = 1.0m , [ BulletDamage . STR ] = 2.0m , [ BulletDamage . DNG ] = 4.0m ,
14+ } ;
15+
16+ /**
17+ * 游戏中所有的轨道。
18+ * 包括:红绿蓝线、左墙右墙(也就是侧键的轨道)、以及部分谱面中会出现的彩色线
19+ */
20+ public List < Lane > Lanes = [ ] ;
21+
22+ /**
23+ * 游戏中所有的伤害弹。
24+ * 目前包括子弹(Bullet)和激光(Beam)两种子类型。
25+ */
26+ public List < IBullet > Bullets = [ ] ;
27+
28+ /**
29+ * 指定切换敌人的时刻。
30+ *
31+ * 可以不显式指定(保持默认的空数组状态),此时游戏会自动应用“前半小怪、后半boss”的机制。
32+ * Type是一个字符串,游戏内的典型取值:"WAVE1" "WAVE2" "BOSS"
33+ */
34+ public List < ( Rational Time , string Type ) > EnemyList = [ ] ;
735
36+ // 部分谱面中会有,上方的敌人在上面进行水平方向的移动的情况。
37+ public List < EnemyMovement > EnemyMovements = [ ] ;
838}
Original file line number Diff line number Diff line change 11using MuConvert . chart ;
2+ using MuConvert . utils ;
3+ using Rationals ;
24
35namespace MuConvert . ogk ;
46
5- public class OgkNote : BaseNote
7+ /**
8+ * 所有游戏内的参与计分的基础音符所构成的基类。
9+ * 包括:Tap(普通键和侧键)、Hold(普通键和侧键)、Flick、Bell四种子类。
10+ */
11+ public abstract class OgkNote : BaseNote
612{
13+ /**
14+ * 音符的水平位置。取值范围[-24,24]。
15+ * 之所以用Rational,是因为极个别TAP/HOLD音符可能不在整数位置上;但绝大多数音符,包括所有的bell、flick,都一定是在整数位置上的。
16+ */
17+ public Rational Pos { get ; set ; }
718
8- }
19+ public bool IsEx ;
20+ }
21+
22+ public class Tap : OgkNote
23+ {
24+ public required Lane Lane ;
25+ }
26+
27+ public class Hold : Tap
28+ {
29+ public override Rational EndTime { get ; set ; }
30+ public Rational EndPos ;
31+ }
32+
33+ public class Flick : OgkNote
34+ {
35+ public Direction Direction ;
36+ }
37+
38+ public class Bell : OgkNote , IBullet
39+ {
40+ public BulletPallete ? Detail { get ; set ; } = null ; // 大多数的bell默认是原地不动的。如果指定此属性,则可以实现和子弹类似的可动bell的效果。
41+ }
Original file line number Diff line number Diff line change 1+ namespace MuConvert . utils ;
2+
3+ public enum LaneType
4+ {
5+ Red ,
6+ Green ,
7+ Blue ,
8+ Colorful , // 多彩线,颜色详见ColorfulLaneColor枚举
9+ Wall , // 墙壁,即场地的左右边界
10+ }
11+
12+ public enum ColorfulLaneColor
13+ {
14+ Akari = 0 ,
15+ Yuzu = 1 ,
16+ Rio = 2 ,
17+ Riku = 3 ,
18+ Tsubaki = 4 ,
19+ Ayaka = 5 ,
20+ Kaede = 6 ,
21+ Saki = 7 ,
22+ Koboshi = 8 ,
23+ Arisu = 9 ,
24+ Mia = 10 ,
25+ Chinatsu = 11 ,
26+ Tsumugi = 12 ,
27+ Setsuna = 13 ,
28+ Brown = 14 ,
29+ Haruna = 15 ,
30+ Black = 16 ,
31+ Akane = 17 ,
32+ G = 18 ,
33+ Aoi = 19 ,
34+ }
35+
36+ public enum Direction { L , R }
37+
38+ public enum BulletSize { N , L } // Normal or Large
39+
40+ public enum BulletType
41+ {
42+ CIR , // 圆形子弹
43+ NDL , // 针状子弹
44+ SQR , // 圆柱形(方状)子弹
45+ }
46+
47+ public enum BulletShooter
48+ {
49+ UPS , // 从具体BLT命令行中所指定的那个Pos位置
50+ ENE , // 从敌人位置
51+ CEN , // 从谱面中心即Pos=0的位置
52+ }
53+
54+ public enum BulletDamage
55+ {
56+ NML , // Normal 使用BULLET_DAMAGE伤害
57+ STR , // Hard 使用HARDBULLET_DAMAGE伤害
58+ DNG , // Danger 使用DANGERBULLET_DAMAGE伤害
59+ }
You can’t perform that action at this time.
0 commit comments