ぴちてぶろぐ

ゲーム開発会社勤務のUnityエンジニアの情報発信場!!

【Unity】スプレッドシート(GoogleAppsScript)でJson出力&受け取り側のC#クラス作成機能を作ってみた

マスターデータはExcelで管理してVBA使って自動化することが多いのですが
家ではExcelライセンスがないので、スプレッドシートでできないかなあと
GoogleAppsScript(GAS)を学ぶついでに挑戦しました。

  • Jsonの出力
  • 受け取り側のC#クラスを作成する

をGASで作ってみました。
GASは全然触ったことないですが、書籍やサイトを見ながら挑戦。

こういうのが出来ます

スプレッドシート

f:id:peachtea_tom:20200508010617p:plain

  • マスターデータ出力を押下するとjsonが出力され
  • 受け取り側クラス生成を押下するとjsonを受け取る用のCSクラスが生成されます。

それぞれ、シート単体での出力と全シート一括出力が出来ます。

出力されるjsonデータ

{
  "datas": [
    {
       "id": 0,
       "sort_id": 2,
       "resource_name": "skin_00"
    },
    {
       "id": 1,
       "sort_id": 3,
       "resource_name": "skin_01"
    },
    {
       "id": 2,
       "sort_id": 1,
       "resource_name": "skin_02"
    },
    {
       "id": 3,
       "sort_id": 5,
       "resource_name": "skin_03"
    },
    {
       "id": 4,
       "sort_id": 6,
       "resource_name": "skin_04"
    },
    {
       "id": 5,
       "sort_id": 0,
       "resource_name": "skin_05"
    }
  ]
}

生成されるクラス

[System.Serializable]
public class MasterData_SkinData{
  [System.Serializable]
  public class Data{
    public int id; // ユニークID
    public int sort_id; // 表示順
    public string resource_name; // リソース名
  }
  public Data[] datas;
}

使い方

上記のスプレッドシートを参考にシートを作成し、
ツール→スクリプトエディタを押下。スクリプトエディタに下記ソースコードを貼り付ければ各種出力メニューが追加されます。
※出力メニューを表示するために一度スプレッドシートを更新(F5押下)が必要になります。

// Open時
function onOpen(){
  // メニューに登録
  SpreadsheetApp.getUi().createMenu('ExportMasterData')
    .addItem('マスターデータ出力(単体)', 'exportJson')
    .addItem('受け取り側クラス生成(単体)', 'createCS')
    .addItem('マスターデータ出力(全シート)', 'exportJsonAll')
    .addItem('受け取り側クラス生成(全シート)', 'createCSAll')
    .addToUi()

}

// --------------------------------------------------------------------
// 受け取り側CS作成
// --------------------------------------------------------------------
// CSファイル作成
function _createCS(sheet){
  var folderId = getCurrentFolderId()
  var data = getData(sheet)
  var csText = createCSText(sheet.getName(), data[0], data[1], data[3])
  var fileName = "MasterData_" + toCamelCace(sheet.getName()) + ".cs"
  deleteFile(fileName, folderId )
  createFile(csText, fileName, folderId )
}
// CSファイル作成(カレントシートのみ)
function createCS(){
  _createCS( SpreadsheetApp.getActiveSpreadsheet().getActiveSheet())
}
// CSファイル作成(全シート分)
function createCSAll(){
  var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets()
  for( var i = 0; i < sheets.length; i++ ){
      _createCS(sheets[i])
   }
}
// クラステキスト生成
function createCSText(sheetName = "",valiableTypes, valiableNames, comments ){
  var csText = ""
  csText += "[System.Serializable]\n"  
  // sheet名をクラス名にする
  var className = toCamelCace(sheetName)
  csText += "public class MasterData_" + className + "{\n"
  csText += "\t[System.Serializable]\n"
  csText += "\tpublic class Data{\n"
  // 変数追加
  for( var i = 0; i < valiableNames.length; i++ ){
    csText += "\t\tpublic " + valiableTypes[i] + " " + valiableNames[i] + ";\t\t// " + comments[i] + "\n"
  }
  
  csText += "\t}\n"
  csText += "\tpublic Data[] datas;\n"
  csText += "}"
  return csText
}

// --------------------------------------------------------------------
// Json出力
// --------------------------------------------------------------------
// Json出力
function _exportJson(sheet){
  var folderId = getCurrentFolderId()
  var data = getData(sheet)
  var jsonText = parseJson(data[0], data[1], data[2])
  var fileName = sheet.getName() + ".json"
  deleteFile(fileName, folderId )
  createFile(jsonText, fileName, folderId )
}
// Json出力
function exportJson() {
  _exportJson(SpreadsheetApp.getActiveSpreadsheet().getActiveSheet())
}
// Json出力
function exportJsonAll() {
  var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets()
  for( var i = 0; i < sheets.length; i++ ){
    var sheet = sheets[i]
    _exportJson(sheet)
  }
}
// jsonパース
function parseJson(valiableTypes, valiableNames, datas){
  var jsonText = "{\"datas\":["
  for( var i = 0; i < datas.length; i++ ){
    jsonText += "{"
    for( var j = 0; j < valiableNames.length; j++ ){
      var valiableName = valiableNames[j]
      if( valiableName.indexOf("ignore") !== -1 || valiableName === ""){
        continue
      }
      jsonText += "\"" + valiableName + "\":"
      var data = datas[i][j]
      var valiableType = valiableTypes[j]
      if( valiableType == "string" ){
        jsonText += "\"" + data + "\""
      }else{
        jsonText += data
      }
      if( valiableNames.length == j + 1){
        continue
      }
      if( valiableNames[j+1].indexOf("ignore") !== -1 || valiableNames[j+1] === ""){
        continue
      }
      if( j < valiableNames.length - 1){
        jsonText += ","
      }
    }
    jsonText += "}"
    if( i < datas.length - 1){
      jsonText += ","
    }
    jsonText += ""
  }
  jsonText += "]}"
  return jsonText
}

// --------------------------------------------------------------------
// Utility
// --------------------------------------------------------------------
// 各データ取得
// 返却値 array[0]:変数型配列
// 返却値 array[1]:変数名配列
// 返却値 array[2]:データ2次元配列
// 返却値 array[3]:コメント配列
function getData(sheet){
  // データ型取得
  var startRow = 1
  var startColumn = 2
  var values = sheet.getSheetValues(startRow, startColumn, 1, sheet.getLastColumn() - (startColumn - 1))
  var valiableTypes = Array.prototype.concat.apply([], values)
  // 変数名取得
  startRow = 2
  startColumn = 2
  values = sheet.getSheetValues(startRow, startColumn, 1, sheet.getLastColumn() - (startColumn - 1))
  var valiableNames = Array.prototype.concat.apply([], values)
  // コメント取得
  startRow = 3
  startColumn = 2
  values = sheet.getSheetValues(startRow, startColumn, 1, sheet.getLastColumn() - (startColumn - 1))
  var comments = Array.prototype.concat.apply([], values)
  // データ取得
  startRow = 4
  startColumn = 2
  var datas = sheet.getSheetValues(startRow, startColumn, sheet.getLastRow() - (startRow - 1 ), sheet.getLastColumn() - (startColumn - 1))
  return [valiableTypes, valiableNames, datas, comments]
}
// CamelCase変換
function toCamelCace(str){
  str = str.charAt(0).toUpperCase() + str.slice(1)
  return str.replace(/_./g,
                     function(s) {
                       return s.charAt(1).toUpperCase()
                     })
}
// ファイル削除
function deleteFile(fileName, folderId){
  var folder = DriveApp.getFolderById(folderId)
  var files = folder.getFiles()
  while( files.hasNext()){
    var file = files.next()
    if( file.getName().indexOf(fileName) != -1){
      DriveApp.getFolderById(folderId).removeFile(file)
    }
  }
}
// ファイル生成
function createFile(jsonData, fileName, folderId){
  var contentType = "text/plain"
  var charSet = "UTF-8"
  var blob = Utilities.newBlob("", contentType, fileName).setDataFromString(jsonData, charSet)
  DriveApp.getFolderById(folderId).createFile(blob)
}
// 同階層のフォルダID取得
function getCurrentFolderId(){
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
  var spreadsheetId = spreadsheet.getId();
  var parentFolder = DriveApp.getFileById(spreadsheetId).getParents()
  var folderId = parentFolder.next().getId();
  return folderId
}

補足

  • 出力したjson,csはスプレッドシートと同階層に作られます。
  • 初めて実行した際には、編集許可を与えてよいかのダイアログ出ますので許可する必要があります。
  • UnityのJsonUtilityを用いて使われることを想定して作成しております。

参考