A recreation of Phigros engine in Sonolus. For Sonolus 0.8.0+.
Use sonolus.h and sonolus-server-cpp to develop.
Todo List:
- Finish writing play mode
- Finish writing buckets system
- Finish writing tutorial mode
- Finish writing watch mode
- Designed particle effect for phigros
You need to install sonolus-server-cpp first!
./sonolus buildcpp play phigros
Because in watch mode, it is necessary to sort each entity by time in order to calculate the correct combo number and other information
But in a level, we not only have note entities, but also judgment line event entities, which means we cannot directly sort them. We must filter out note entities and sort them
And the entity index in Sonolus cannot be modified
In summary, we consider creating a linked list for all note entities and storing the index of the next entity in the Entity Shared Memory block of that note entity
If you simply use bubble sort, when the number of notes is too large, it will get stuck on the loading page or even cause the application to crash
Consider using merge sort
However, Sonolus does not support the use of recursive functions, so we are considering using a non-recursive version of merge sort
Algorithm keywords: merge sort, linked list, non-recursive
The detailed alogrithm implementation: ./engine/watch/Preprocess.cpp
The alogrithm implementation in C++: ./mergeSort.cpp
Accepted Submission in Online Judge Platform: https://www.luogu.com.cn/record/167007458 (arraySize <= 1e5, 1s, 256MB)
Because Pigeon Games uses straight lines to fit of all event curves, there are too many events in the official chart.
In order to reduce the number of events, I have implemented such a optimizer to fit all straight lines back to curves.
Tips: I use random algorithms for fitting, therefore this program may not have much technical content.
Use commands below to compile the optimizer
program:
g++ optimizer.cpp -o optimizer -ljsoncpp -O3
Basic usage is as follows:
./optimizer Chart_AT.json Chart_AT_Optimized.json
According to the original official chart format, this program has made the following changes to the generated chart file:
- Change
formatVersion
from3
to13
- Add
easing
property to each event - Split
judgeLineMoveEvents
intojudgeLineMoveXEvents
andjudgeLineMoveYEvents
The easing functions are as follows:
Easing | Name | Expression |
---|---|---|
0 | EaseLinear | [](double x){ return x; } |
1 | EaseInQuad | [](double x){ return x * x; } |
2 | EaseOutQuad | [](double x){ return 1 - (1 - x) * (1 - x); } |
3 | EaseInOutQuad | [](double x){ return (x < 0.5 ? EasingFunction[1](x * 2) / 2 : EasingFunction[2](x * 2 - 1) / 2 + 0.5); } |
4 | EaseOutInQuad | [](double x){ return (x < 0.5 ? EasingFunction[2](x * 2) / 2 : EasingFunction[1](x * 2 - 1) / 2 + 0.5); } |
5 | EaseInCubic | [](double x){ return x * x * x; } |
6 | EaseOutCubic | [](double x){ return 1 - pow(1 - x, 3); } |
7 | EaseInOutCubic | [](double x){ return (x < 0.5 ? EasingFunction[5](x * 2) / 2 : EasingFunction[6](x * 2 - 1) / 2 + 0.5); } |
8 | EaseOutInCubic | [](double x){ return (x < 0.5 ? EasingFunction[6](x * 2) / 2 : EasingFunction[5](x * 2 - 1) / 2 + 0.5); } |
9 | EaseInQuart | [](double x){ return x * x * x * x; } |
10 | EaseOutQuart | [](double x){ return 1 - pow(1 - x, 4); } |
11 | EaseInOutQuart | [](double x){ return (x < 0.5 ? EasingFunction[9](x * 2) / 2 : EasingFunction[10](x * 2 - 1) / 2 + 0.5); } |
12 | EaseOutInQuart | [](double x){ return (x < 0.5 ? EasingFunction[10](x * 2) / 2 : EasingFunction[9](x * 2 - 1) / 2 + 0.5); } |
13 | EaseInQuint | [](double x){ return x * x * x * x * x; } |
14 | EaseOutQuint | [](double x){ return 1 - pow(1 - x, 5); } |
15 | EaseInOutQuint | [](double x){ return (x < 0.5 ? EasingFunction[13](x * 2) / 2 : EasingFunction[14](x * 2 - 1) / 2 + 0.5); } |
16 | EaseOutInQuint | [](double x){ return (x < 0.5 ? EasingFunction[14](x * 2) / 2 : EasingFunction[13](x * 2 - 1) / 2 + 0.5); } |
17 | EaseInSine | [](double x){ return 1 - cos((x * acos(-1)) / 2); } |
18 | EaseOutSine | [](double x){ return sin((x * acos(-1)) / 2); } |
19 | EaseInOutSine | [](double x){ return (x < 0.5 ? EasingFunction[17](x * 2) / 2 : EasingFunction[18](x * 2 - 1) / 2 + 0.5); } |
20 | EaseOutInSine | [](double x){ return (x < 0.5 ? EasingFunction[18](x * 2) / 2 : EasingFunction[17](x * 2 - 1) / 2 + 0.5); } |
21 | EaseInExpo | [](double x){ return (x == 0 ? 0 : pow(2, 10 * x - 10)); } |
22 | EaseOutExpo | [](double x){ return (x == 1 ? 1 : 1 - pow(2, -10 * x)); } |
23 | EaseInOutExpo | [](double x){ return (x < 0.5 ? EasingFunction[21](x * 2) / 2 : EasingFunction[22](x * 2 - 1) / 2 + 0.5); } |
24 | EaseOutInExpo | [](double x){ return (x < 0.5 ? EasingFunction[22](x * 2) / 2 : EasingFunction[21](x * 2 - 1) / 2 + 0.5); } |
25 | EaseInCirc | [](double x){ return 1 - sqrt(1 - pow(x, 2)); } |
26 | EaseOutCirc | [](double x){ return sqrt(1 - pow(x - 1, 2)); } |
27 | EaseInOutCirc | [](double x){ return (x < 0.5 ? EasingFunction[25](x * 2) / 2 : EasingFunction[26](x * 2 - 1) / 2 + 0.5); } |
28 | EaseOutInCirc | [](double x){ return (x < 0.5 ? EasingFunction[26](x * 2) / 2 : EasingFunction[25](x * 2 - 1) / 2 + 0.5); } |
29 | EaseInElastic | [](double x){ return 2.70158 * x * x * x - 1.70158 * x * x; } |
30 | EaseOutElastic | [](double x){ return 1 + 2.70158 * pow(x - 1, 3) + 1.70158 * pow(x - 1, 2); } |
31 | EaseInOutElastic | [](double x){ return (x < 0.5 ? EasingFunction[29](x * 2) / 2 : EasingFunction[30](x * 2 - 1) / 2 + 0.5); } |
32 | EaseOutInElastic | [](double x){ return (x < 0.5 ? EasingFunction[30](x * 2) / 2 : EasingFunction[29](x * 2 - 1) / 2 + 0.5); } |
33 | EaseInBack | [](double x){ return (x == 0 ? 0 : x == 1 ? 1 : -pow(2, 10 * x - 10) * sin((x * 10 - 10.75) * (2 * acos(-1)) / 3)); } |
34 | EaseOutBack | [](double x){ return (x == 0 ? 0 : x == 1 ? 1 : pow(2, -10 * x) * sin((x * 10 - 0.75) * (2 * acos(-1)) / 3) + 1); } |
35 | EaseInOutBack | [](double x){ return (x < 0.5 ? EasingFunction[33](x * 2) / 2 : EasingFunction[34](x * 2 - 1) / 2 + 0.5); } |
36 | EaseOutInBack | [](double x){ return (x < 0.5 ? EasingFunction[34](x * 2) / 2 : EasingFunction[33](x * 2 - 1) / 2 + 0.5); } |
37 | EaseNone | [](double x){ return (x < 0.5 ? 0 : 1); } |
Performances:
Chart Name | PRAGMATISM RESURRECTION AT | 祈-我ら神祖と共に歩む者なり- AT | ERABYE CONNEC10N AT | Indelible Scar AT |
---|---|---|---|---|
Execution Time | 8.752s | 12.835s | 10.228s | 6.163s |
Events(Before) | 154436 | 218502 | 146286 | 120007 |
Events(After) | 7779 | 8669 | 8080 | 5043 |
Chart.json(Before) | 9256343 | 13226001 | 8741497 | 7308163 |
Chart.json(After) | 754428 | 959750 | 849974 | 567775 |
LevelData(Before) | 7406212 | 10463793 | 6893964 | 6000468 |
LevelData(After) | 387917 | 449083 | 405189 | 272209 |