Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Threading.Tasks;
- using System.Drawing;
- using System.IO;
- using static System.Linq.Enumerable;
- namespace MusicGameMovieMaker
- {
- class Note : IComparable<Note>
- {
- public int Time { get; }
- public int Line { get; }
- public Note(int time, int line)
- {
- this.Time = time;
- this.Line = line;
- }
- public int CompareTo(Note other)
- {
- var p = this.Time.CompareTo(other.Time);
- if (p != 0)
- {
- return p;
- }
- return this.Line.CompareTo(other.Line);
- }
- }
- static class Program
- {
- const int height = 480;//画面の高さ
- const int width = 640;//画面の幅
- const int noteSize = 20;//各ノーツのサイズ
- const double distanceBase = 7.5;//HiSpeed x1.0での表示距離
- const int border = 20;//判定の位置
- const int maxDisplay = height - border;//表示される限界の位置
- const int horizonDistance = 75;//各列の幅
- const double left = width / 2 - horizonDistance * 1.5;//左端の列
- //C++のstd::partition_pointに似た挙動をする関数(詳しくはcpprefjpを見よ)
- static int PartitionPoint<T>(List<T> list, Func<T, bool> pred)
- {
- var min = 0;
- var max = list.Count;
- while (max - min > 1)
- {
- var mid = (min + max) / 2;
- if (pred(list[mid]))
- {
- min = mid;
- }
- else
- {
- max = mid;
- }
- }
- return max;
- }
- static (int, int) ReadTuple2(TextReader reader)
- {
- var ar = reader.ReadLine().Split(' ').Select(int.Parse).ToArray();
- return (ar[0], ar[1]);
- }
- //BPMのデータを読み込む
- static List<int> ReadBPMList(string path)
- {
- using (var stream = new StreamReader(path))
- {
- var (N, maxTime) = ReadTuple2(stream);
- var ret = new List<int>();
- var impl = new List<(int Time, int BPM)>();
- foreach(var i in Range(0, N))
- {
- impl.Add(ReadTuple2(stream));
- }
- impl.Add((maxTime, int.MaxValue));
- for (var (index, time) = (0, 0); time < maxTime; ++time)
- {
- if (time == impl[index + 1].Time)
- {
- ++index;
- }
- ret.Add(impl[index].BPM);
- }
- return ret;
- }
- }
- //32分刻みでのノーツのデータを読み込む
- static List<Note> ReadNoteList(string path)
- {
- using(var stream = new StreamReader(path))
- {
- var ret = new List<Note>();
- var N = int.Parse(stream.ReadLine());
- foreach(var i in Range(0, N))
- {
- var (time, line) = ReadTuple2(stream);
- ret.Add(new Note(time, line));
- }
- ret.Sort();
- return ret;
- }
- }
- static void Main(string[] args)
- {
- var BPMList = ReadBPMList("bpmdata.txt");
- var noteData = ReadNoteList("notedata.txt");
- var minIndex = 0;
- var bpmIndex = 0;
- var hispeed = 1.5;
- var line = 0.0;
- var (changeTime, realBPMList) = BPMCheck(BPMList);
- minIndex = SaveImage(noteData, minIndex, hispeed, line, 0);
- // 1.0/60秒ずつ進むことを仮定する(ほぼ誤差の範囲だが厳密には60FPSにはならないことに注意せよ)
- const double minTime = 1.0 / 60;
- var realTime = 0.0;
- for (var frame = 1; minIndex < noteData.Count; ++frame)
- {
- var time = minTime;
- while (time != default)
- {
- // BPMがxのときy秒で8*x*y/60回だけ32分を刻むことができる
- line += Math.Min(changeTime[bpmIndex] - realTime, time) * realBPMList[bpmIndex] * 8 / 60;
- time -= Math.Min(changeTime[bpmIndex] - realTime, time);
- if (time != default)
- {
- ++bpmIndex;
- }
- }
- realTime += minTime;
- minIndex = SaveImage(noteData, minIndex, hispeed, line, frame);
- }
- }
- static Pen Pen = new Pen(Color.Red, noteSize / 4);
- private static int SaveImage(List<Note> noteData, int minIndex, double hispeed, double line, int index)
- {
- // ノーツの中心と判定ラインの距離を返す
- double GetPosition(Note note)
- {
- return (note.Time - line) * hispeed * distanceBase;
- }
- var pIndex = PartitionPoint(noteData, n => GetPosition(n) < maxDisplay + noteSize);
- using (var bitmap = new Bitmap(width, height))
- {
- using (var g = Graphics.FromImage(bitmap))
- {
- foreach (var i in Range(0, 4))
- {
- g.DrawRectangle(Pens.Red, (int)(left + (horizonDistance * i) - noteSize), border - noteSize, 2 * noteSize, 2 * noteSize);
- }
- for (var i = minIndex; i < pIndex; ++i)
- {
- var pos = GetPosition(noteData[i]);
- if (pos < -noteSize - border)
- {
- minIndex = i + 1;
- }
- else
- {
- var noteLine = noteData[i].Line;
- g.FillRectangle(Brushes.Blue, (int)(left + (horizonDistance * noteLine) - noteSize), (int)(pos + border - noteSize), 2 * noteSize, 2 * noteSize);
- g.DrawRectangle(Pen, (int)(left + (horizonDistance * noteLine) - noteSize), (int)(pos + border - noteSize), 2 * noteSize, 2 * noteSize);
- }
- }
- }
- bitmap.Save($"image/{index + 1}.png", System.Drawing.Imaging.ImageFormat.Png);
- return minIndex;
- }
- }
- // BPMが変わる時間とそれに対応するBPMのリストをセットを返す
- private static (List<double> Change, List<int> RealBPMList) BPMCheck(List<int> BPMList)
- {
- var bpmChange = new List<double>();
- var realBpmList = new List<int>
- {
- BPMList[0]
- };
- var prev = BPMList[0];
- var time = 60.0 / (8.0 * prev);
- foreach (var bpm in BPMList.Skip(1))
- {
- if (bpm != prev)
- {
- bpmChange.Add(time);
- realBpmList.Add(bpm);
- }
- prev = bpm;
- time += 60.0 / (8.0 * bpm);
- }
- bpmChange.Add(double.MaxValue);
- return (bpmChange, realBpmList);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement