前置き
ゲームとかイラストとかBotとか書いてるHayaoです。
今、音ゲー制作をやってます。
ようやくα版がリリースしました。この制作の上で自作のUnityEditorを作ったのでそれを紹介します。
作ったもの
音ゲーと制作する上で必要になってくる譜面を製作するためのUnityEditorです。
そもそも
音ゲー作る上で一番きつい部分はどこでしょうか?
私自身が一番きつい部分は譜面制作です。(個人差あり)
で、私はCSVファイルに書き込み、それをUnity上で読み込むというふうにシステムを作り出しました。
ただ・・・・
これを手書きするのは面倒くさい・・・・
ということで自作でUnityEditorを作りました。うおーーー-
完成物
機能
これはaddblockという自作Editorで何小節目のどこかを指定してそれをCSVファイルに書き、画面上に反映させるものです。
作り方
実際にどのように作っていったか。部分的ではありますがコードを載せたいと思います。
まず縦線を表示する部分です。
for (int i = 0; i <= lineCount; i++)
{
var spos = new Vector3(xpos, 20.0f);
var vector = new Vector3(xpos, 2.5f, 0f);
var beat_4_top = new Vector3(before_xpos_per, 20.0f, 0f);
var beat_4_bottom = new Vector3(before_xpos_per, 1000.0f, 0f);
var beat_8_top = new Vector3(before_xpos_per, 20.0f, 0f);
var beat_8_bottom = new Vector3(before_xpos_per, 1000.0f, 0f);
var beat_12_top = new Vector3(before_xpos_per, 20.0f, 0f);
var beat_12_bottom = new Vector3(before_xpos_per, 1000.0f, 0f);
var beat_16_top = new Vector3(before_xpos_per, 20.0f, 0f);
var beat_16_bottom = new Vector3(before_xpos_per, 1000.0f, 0f);
int beat_count_4 = 0;
int beat_count_8 = 0;
int beat_count_12 = 0;
// baseLinePosは起点のPosition
if (i % 5 == 0)
{
vector.y = 0f;
}
EditorGUILayout.LabelField(i.ToString(), GUILayout.Width(perspace-3f));
Handles.DrawLine(spos, vector);
if(xpos != 0.0f){
for(int beat = 0; beat < 16; beat++){
beat_16_top.x = (space * scale) / 16 + beat_16_top.x;
beat_16_bottom.x = beat_16_top.x;
var beatColor = Handles.color;
Handles.color = new Color(35f,35f,35f,0.1f);
Handles.DrawLine(beat_16_top, beat_16_bottom);
Handles.color = beatColor;
if(beat_count_4 < 4){
beat_4_top.x = (space * scale) / 4 + beat_4_top.x;
beat_4_bottom.x = beat_4_top.x;
beatColor = Handles.color;
Handles.color = new Color(35f,35f,35f,0.4f);
Handles.DrawLine(beat_4_top, beat_4_bottom);
Handles.color = beatColor;
beat_count_4++;
}
if(beat_count_8 < 8){
beat_8_top.x = (space * scale) / 8 + beat_8_top.x;
beat_8_bottom.x = beat_8_top.x;
beatColor = Handles.color;
Handles.color = new Color(35f,35f,35f,0.3f);
Handles.DrawLine(beat_8_top, beat_8_bottom);
Handles.color = beatColor;
beat_count_8++;
}
if(beat_count_12 < 12){
beat_12_top.x = (space * scale) / 12 + beat_12_top.x;
beat_12_bottom.x = beat_12_top.x;
beatColor = Handles.color;
Handles.color = new Color(35f,35f,35f,0.05f);
Handles.DrawLine(beat_12_top, beat_12_bottom);
Handles.color = beatColor;
beat_count_12++;
}
before_xpos_per = xpos;
}
}
xpos = xpos + space * scale;
}
Handles.DrawLine(startPos, endPos);
Handles.color = prev;
EditorGUILayout.EndHorizontal();
このEditorでの1目盛りは1小節のため4分、8分、12分、16分で縦線を引いています。
次に降ってくるブロックを設定する部分です。
if(!EditMusicScore.GetisPlay() && !canSet){
startime = EditMusicScore.GetMusicTime();
endtime = EditMusicScore.GetMusicTime();
canSet = !canSet;
}
if(GUILayout.Button("Add File")){
filepath = EditorUtility.OpenFilePanel("Open csv", "", "CSV");
if(filepath == null){
return;
}else{
filename = Path.GetFileNameWithoutExtension(filepath);
}
}
GUILayout.Space(15f);
GUILayout.Label("Time or Measure");
_StartWirteIndex = GUILayout.SelectionGrid(_StartWirteIndex, WriteTipe, 3);
if(_StartWirteIndex == 1){
GUILayout.Space(15f);
GUILayout.Label("Measure Type");
_MeasureIndex = GUILayout.SelectionGrid(_MeasureIndex, MeasureType, 3);
GUILayout.Space(15f);
GUILayout.Label("拍子数");
measure_num = EditorGUILayout.DelayedFloatField(measure_num);
GUILayout.Label("拍数");
beat_num = EditorGUILayout.DelayedFloatField(beat_num);
}else{
GUILayout.Space(15f);
GUILayout.Label("Now Time");
startime = EditorGUILayout.DelayedFloatField(startime);
}
GUILayout.Space(15f);
GUILayout.Label("Select line");
_LineIndex = GUILayout.SelectionGrid(_LineIndex, Line, 3);
GUILayout.Label("Select Block Type");
_TypeIndex = GUILayout.SelectionGrid(_TypeIndex, BlockType, 3);
if(_TypeIndex == 1){
GUILayout.Space(15f);
GUILayout.Label("Time or Measure");
_EndWriteIndex = GUILayout.SelectionGrid(_EndWriteIndex, WriteTipe, 3);
if(_EndWriteIndex == 1){
GUILayout.Space(15f);
GUILayout.Label("Measure Type");
_EndMeasureIndex = GUILayout.SelectionGrid(_EndMeasureIndex, MeasureType, 3);
GUILayout.Space(15f);
GUILayout.Label("拍子数");
end_measure_num = EditorGUILayout.DelayedFloatField(end_measure_num);
GUILayout.Label("拍数");
end_beat_num = EditorGUILayout.DelayedFloatField(end_beat_num);
}else{
GUILayout.Label("Input Last time");
endtime = EditorGUILayout.DelayedFloatField(endtime);
}
}
GUILayout.Space(35f);
GUILayout.Label("notes speed");
notes_speed = EditorGUILayout.DelayedFloatField(notes_speed);
GUILayout.Space(35f);
if(GUILayout.Button("Add")){
if(_StartWirteIndex == 1){
startime = ((float)240/(float)EditMusicScore.GetBpm()/(float)((float)4*(float)(_MeasureIndex+1)))*((float)((4*(_MeasureIndex+1))*measure_num + beat_num));
}
if(_EndWriteIndex == 1){
endtime = ((float)240/(float)EditMusicScore.GetBpm()/(float)((float)4*(float)(_EndMeasureIndex+1)))*((float)((4*(_EndMeasureIndex+1))*end_measure_num + end_beat_num));
}
if(_TypeIndex == 1){
CSV.AddCSV(filepath, Line[_LineIndex], BlockType[_TypeIndex], startime, endtime, notes_speed);
}else{
CSV.AddCSV(filepath, Line[_LineIndex], BlockType[_TypeIndex], startime, 0.0f, notes_speed);
}
CSV.GetCSV(filename, CSVData);
EditMusicScore.UpdateCSV();
}
これとCSVファイルに書き込む部分と読み込む部分は以下の通りになります
public class CSV : MonoBehaviour
{
private static int rownum = 0;
public static void AddCSV(string filename, string linename, string tyepname, float startime, float endtime, float notes_speed){
var list = "";
Debug.Log(filename);
list = linename + "," + tyepname + "," + startime.ToString() + "," + endtime.ToString() + "," + notes_speed.ToString() + "\n";
if(filename == null){
return;
}
using (var file = new StreamWriter(filename, true, System.Text.Encoding.GetEncoding("UTF-8"))){
file.Write(list);
}
}
public static void GetCSV(string filename, List<string[]> csvDatas){
var csvfile = Resources.Load(filename) as TextAsset;
StringReader file = new StringReader(csvfile.text);
rownum = 0;
while(file.Peek() != -1){
string line = file.ReadLine();
csvDatas.Add(line.Split(','));
rownum = rownum + 1;
}
}
public static bool HasData(string filename){
int count = 0;
var csvfile = Resources.Load(filename) as TextAsset;
StringReader file = new StringReader(csvfile.text);
while(file.Peek() != -1){
string line = file.ReadLine();
count++;
}
if(count == 0){
return false;
}else{
Debug.Log(count);
return true;
}
}
public static int GetRowNum(){
return rownum;
}
}
こんな感じでゴニョゴニョすれば譜面を作るためのUnityEditorが完成します。
終わりに
実際にこのEditorを使用して、一譜面完成させましたが、しんどかったです。まじで・・・・
あと、改修しない部分も多く、削除部分だったり、既存の部分の編集だったり・・・これらの問題自体はUnityEditorはEditorWindowを継承しているのでMonoBehaviorよりとっつきにくく、難しいのです・・・・。今後も頑張っていこうと思います。
明日は鈴木乖離さんの「醜く生きるお絵描き道~大学生から目指すつよつよ絵描き~」です。よろしくお願いします。
参考にさせていただいた資料