概要
- 初めてアナログゲームを作るにあたり、「テストプレイ用」と「印刷所入稿用」のデータ作成をスクリプトで半自動化した
- テストプレイはユドナリウムを使ってオンラインで何度か行った。テストプレイの度にデータを修正するので、その都度カード画像を書き出す必要があった
- 印刷所への入稿は印刷所指定のIllustratorテンプレートファイルでカード1枚につき1ファイル作成する必要があった
- それら2種類のファイルを1つのマスターファイル(TSV)から繰り返し作成する為の手順の備忘録
作るゲームについて
- 「推しの名前を叫びたい」という、カードに書かれた属性を満たすキャラクターを宣言するゲーム
- 「メガネをかけてる」「ライバルがいる」「髪が青/緑」みたいなキャラクターの特徴が書かれたカードが150枚入ってるいる
- カードには「カテゴリ」「タイトル(キャラクターの特徴)」「補足説明(箇条書き)」「難易度(★)」という4つの要素がある
- 今回はマスターデータからこれらの要素を画像ファイル(テストプレイ)、.aiファイル(入稿)として書き出したい
- (宣伝)ゲームマーケット2023秋 2023年12月9日(土)にて頒布予定
- 詳しいルール等についてはXアカウントにて告知予定です!
#ゲムマ2023秋 用ブースカットを入稿完了!
— Daydream@ゲムマ2023秋(土) (@daydreamori) September 11, 2023
徐々にこのアカウントも動かしていきます pic.twitter.com/6vSct8xmCQ
マスターファイルの準備
...
自信家 そういう描写や言及があればOK 肩書・内面 2 1
自信が無い そういう描写や言及があればOK 肩書・内面 2 1
マザコン/ファザコン/ブラコン/シスコン 肩書・内面 2 1
ギャル/チャラ男 周囲にそう思われてるだけでもOK 肩書・内面 3 1
優等生/真面目 そういう描写や言及があればOK 肩書・内面 2 1
...
- TSVで「タイトル」「説明」「カテゴリ」「難易度(★)」の順に並べただけのファイル
- Googleスプレッドシートで管理してTSVとして書き出す
方針
- 最初は「Imagemagickを使って、ユドナリウム用PNG画像とXMLを書き出すRubyスクリプト」と「Illustratorファイルを書き出すイラレscript」の2つを使っていた
- しかしカードの要素やレイアウトを試行錯誤する度に2つのスクリプトを修正するのは面倒なので、まずイラレファイルを生成し、それを元にPNG画像を書き出すフローに変更。ユドナリウム用XML生成は別途元のRubyスクリプトを使う(こちらはカードの要素やレイアウトの影響を受けないので修正の必要が無い)
マスターファイルからIllustratorデータを作る
テンプレートファイルを作る
- 今回は萬印堂さんに印刷をご依頼するので、萬印堂さん指定のIllustratorテンプレートファイルをこちらからDLする
- そのテンプレートファイルを元に、当ゲーム用テンプレートファイルを作る
- 以下のようにテキストフレームを配置
- スクリプトでテキストフレームの生成や各種設定を行えばこの工程は不要だが、非直感的でわざわざスクリプトでやる意味が無い
Illustratorファイル生成スクリプト
app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;
var csvPath = "/{任意のディレクトリ}/card_csv.csv";
var csvFile = new File(csvPath);
csvFile.open('r');
var templatePath = "/{任意のディレクトリ}/template_card.ai";
var titleIndex = 2;
var descIndex = 1;
var categoryIndex = 3;
var starIndex = 0;
var i = 0;
while (!csvFile.eof) {
i++;
var line = csvFile.readln();
var values = line.split("\t");
var templateFile = new File(templatePath);
app.open(templateFile);
var doc = app.activeDocument;
var category = values[2];
var categoryItem = app.activeDocument.textFrames[categoryIndex];
categoryItem.contents = category;
var starNum = parseInt(values[3]);
var starText = "";
for (var j = 0; j < starNum; j++) {
starText += "★";
}
var starItem = app.activeDocument.textFrames[starIndex];
starItem.contents = starText;
var textItem = app.activeDocument.textFrames[titleIndex];
textItem.contents = values[0];
var desc = app.activeDocument.textFrames[descIndex];
desc.contents = values[1];
var outputFile = new File("/{任意のディレクトリ}/tmp_ai/card_"+i+"_front.ai");
doc.saveAs(outputFile);
doc.close();
}
csvFile.close();
- jsでDOMを操作するようなノリで、要素を取得してフィールドにセットすればOK
- 実際はカードのカテゴリによってテンプレートファイルを変えたり、「補足説明」を箇条書きに変えたりといった細かな処理を足している
- 最終的な入稿用データとしては、機械的なルールでは処理できない部分(フォントサイズの細かな調整等)を手動で行った
ユドナリウム用データを作る
最終的に作りたいもの
- ユドナリウムにはルームデータをzipでインポート/エクスポートできる仕組みがある
- ルームデータはXMLで管理されているので、そのXMLと、XMLから参照される画像データを生成できればルームデータが作れる
- こんな感じで8つの山札が置かれたルームを生成したい(テーブル画像等は後から設定すればOK)
カード画像を作る
- 「方針」の通り、まず入稿用IllustratorファイルからPNG画像を書き出す
- 入稿用ファイルはカード画像以外の要素が色々あるので、「お客様の作業領域」というレイヤーのみをPNG書き出しする
- そのままだと解像度が低いので3倍にスケールさせる
var options = new ExportOptionsPNG24();
options.antiAliasing = true;
options.transparency = true;
options.horizontalScale = 300;
options.verticalScale = 300;
var inputDir = "/{任意のディレクトリ}/fix/";
var outputDir = "/{任意のディレクトリ}/v1/";
for (var i = 1; i <= 150; i++) {
var file = new File(inputDir + "card_" + i + "_front.ai");
var doc = app.open(file);
for (var j = 0; j < doc.layers.length; j++) {
doc.layers[j].visible = false;
}
var targetLayer = doc.layers.getByName("お客様の作業領域");
targetLayer.visible = true;
var outputFile = new File(outputDir + "card_" + i + ".png");
doc.exportFile(outputFile, ExportType.PNG24, options);
doc.close(SaveOptions.DONOTSAVECHANGES);
}
room_template.xml
<?xml version="1.0" encoding="UTF-8"?>
<room>
<game-table name="最初のテーブル" width="20" height="15" gridSize="50" imageIdentifier="testTableBackgroundImage_image" backgroundImageIdentifier="imageIdentifier" backgroundFilterType="" selected="false" gridType="0" gridColor="#000000e6"></game-table>
<card-stack location.name="table" location.x="0.0" location.y="452.8333688050696" posZ="0" rotate="0" zindex="5" owner="" isShowTotal="true">
<data name="card-stack">
<data name="image">
<data type="image" name="imageIdentifier"></data>
</data>
<data name="common">
<data name="name">名前</data>
</data>
<data name="detail"></data>
</data>
<node name="cardRoot">
__card1__
</node>
</card-stack>
<card-stack location.name="table" location.x="300.0" location.y="452.8333688050696" posZ="0" rotate="0" zindex="6" owner="" isShowTotal="true">
<data name="card-stack">
<data name="image">
<data type="image" name="imageIdentifier"></data>
</data>
<data name="common">
<data name="name">年齢</data>
</data>
<data name="detail"></data>
</data>
<node name="cardRoot">
__card2__
</node>
</card-stack>
<card-stack location.name="table" location.x="600.0" location.y="452.8333688050696" posZ="0" rotate="0" zindex="7" owner="" isShowTotal="true">
<data name="card-stack">
<data name="image">
<data type="image" name="imageIdentifier"></data>
</data>
<data name="common">
<data name="name">特徴</data>
</data>
<data name="detail"></data>
</data>
<node name="cardRoot">
__card3__
</node>
</card-stack>
<card-stack location.name="table" location.x="0.0" location.y="660" posZ="0" rotate="0" zindex="5" owner="" isShowTotal="true">
<data name="card-stack">
<data name="image">
<data type="image" name="imageIdentifier"></data>
</data>
<data name="common">
<data name="name">名前</data>
</data>
<data name="detail"></data>
</data>
<node name="cardRoot">
__card4__
</node>
</card-stack>
<card-stack location.name="table" location.x="300.0" location.y="660" posZ="0" rotate="0" zindex="6" owner="" isShowTotal="true">
<data name="card-stack">
<data name="image">
<data type="image" name="imageIdentifier"></data>
</data>
<data name="common">
<data name="name">年齢</data>
</data>
<data name="detail"></data>
</data>
<node name="cardRoot">
__card5__
</node>
</card-stack>
<card-stack location.name="table" location.x="600.0" location.y="660" posZ="0" rotate="0" zindex="7" owner="" isShowTotal="true">
<data name="card-stack">
<data name="image">
<data type="image" name="imageIdentifier"></data>
</data>
<data name="common">
<data name="name">特徴</data>
</data>
<data name="detail"></data>
</data>
<node name="cardRoot">
__card6__
</node>
</card-stack>
<card-stack location.name="table" location.x="0.0" location.y="960" posZ="0" rotate="0" zindex="5" owner="" isShowTotal="true">
<data name="card-stack">
<data name="image">
<data type="image" name="imageIdentifier"></data>
</data>
<data name="common">
<data name="name">名前</data>
</data>
<data name="detail"></data>
</data>
<node name="cardRoot">
__card7__
</node>
</card-stack>
<card-stack location.name="table" location.x="300.0" location.y="960" posZ="0" rotate="0" zindex="6" owner="" isShowTotal="true">
<data name="card-stack">
<data name="image">
<data type="image" name="imageIdentifier"></data>
</data>
<data name="common">
<data name="name">年齢</data>
</data>
<data name="detail"></data>
</data>
<node name="cardRoot">
__card8__
</node>
</card-stack>
</room>
ルームデータを作る
- マスターファイルに従ってXMLを作る
- ドキュメント等は無いのでエクスポートしたファイルを見てみた所、ユドナリウムでは「data.xml」というファイルにルームデータを記述すれば良いようなので、そのようにzipファイルを作る
- 画像アセットをzipに含める方法は分からなかった(そもそもできるのかな?)ので、画像はクラウドストレージに置いてそのURLをXMLに記述する
- 後はユドナリウムを開いて「ZIP読み込み」すればOK!