脱出ゲーム制作の作業を効率化
脱出ゲームでは部屋にオブジェクトを配置した後、色々な角度にカメラを配置してレンダリングする必要があります。レンダリング中は操作の必要が無くひたすら待つだけです。ですので作業を自動化して夜間にまとめてレンダリングできたら便利だなと思ったわけです。
BlenderはPythonを利用した自由度の高い操作が可能らしいので今回挑戦しました。
特別な起動方法
Blenderには「Python Console」というインターフェイスがあるので、標準出力の結果はここに表示されるものと思っていました。しかし実際はOSのコンソールに出力されます。WindowsではBlenderのメニューからOSのコンソールを起動できるのですがMacではできません。MacではコンソールからBlenderを起動することで、BLenderからの出力を得ることができます。
出力先を探すのにすごく時間がかかりました。Blender内にコンソールがあるのに何故OSのコンソールに出力するの?
→参考:公式サイト「The Console Window(英語のみ)」
情報ウインドウの隠れた機能
Python自体はメジャーな言語なので情報はネットにたくさんあります。しかしBlenderで利用する場合は、Blender特有の機能を把握しなければなりません(パッケージ名はbpy)。
公式サイトでBlender固有の機能を説明しています。
→参考:公式サイト「Python マニュアル」
英語サイトへ丸投げしているので日本語情報は無い…。
しかしBlenderは公式サイトに頼らずとも簡単に関数やプロパティを知ることができるのです!。普段は最小化されている情報ウインドウですが、これを拡げると実際に操作した内容がPythonのコードとして表示されるのです。下の画像はマテリアルの設定でEmissionのstrengthを5に変更した際に表示されたコードです。
1行目はマテリアルのタブを選択したときのコードで、2行目が値を変更したときのコード。
BlenderのCyclesモードはノードで複雑なシェーダーを作成できるので、それを制御するコードも複雑になります。もし、こんなコードを公式サイトのマニュアルだけを頼りに作成しなければならないと考えたら恐ろしいです…。
Blenderはバージョンアップ毎にPythonのまわりが変更されることが多いらしくネットで情報が探しにくいらしいので、この仕組みは非常に助かります!!
CSVの読込
今回はカメラの座標とライトの強さのリストをCSVファイルとして外部に持ち、その順番通りにレンダリングして画像を自動的に保存するような処理を実現します。ということでPythonのCSV読込について調べたらBlenderでの利用法を説明しているサイトがあったのでメモ。
You can use the csv library to read and parse CSVs. (英語)
PythonにはCSVを扱うモジュールがあり、Blenderでも問題なく利用できるようです。
実際にやってみた
利用するCSVは以下のような感じでカメラの座標と角度、ライトの強さを3箇所分用意しました。
で実行するpythonコードは以下。CSVのモジュールがあるので予想以上にシンプルに書けました。
オブジェクトの角度の設定はUI上では「度」ですがスクリプトでは「ラジアン」で設定します。
import bpy, os, math, csv
filePath = '/Users/designdrill/Desktop/studyBlender/02_python/camera.csv'
with open(filePath) as csvfile:
reader = csv.reader(csvfile)
for i, row in enumerate(reader):
if i == 0: continue #-------------------------Skip column titles
print(row[0])
bpy.data.objects['Camera'].location.x = float(row[1])
bpy.data.objects['Camera'].location.y = float(row[2])
bpy.data.objects['Camera'].location.z = float(row[3])
bpy.data.objects['Camera'].rotation_euler.x = (float(row[4])*math.pi)/180
bpy.data.objects['Camera'].rotation_euler.y = (float(row[5])*math.pi)/180
bpy.data.objects['Camera'].rotation_euler.z = (float(row[6])*math.pi)/180
bpy.data.materials['camera_light'].node_tree.nodes["Emission"].inputs[1].default_value = float(row[7])
bpy.ops.render.render()
bpy.data.images['Render Result'].save_render(filepath = os.path.dirname(bpy.data.filepath) + '/image'+row[0]+'.png')
非同期処理ではない
最近では非同期処理が当たり前すぎて、上記の処理がちょっと違和感でした(いや何も間違っていないのですが…)。赤字の部分でレンダリングをしていてレンダリングが終わるまで次の行は実行されません(当たり前なのですが…)。折角なのでPythonでの非同期処理について調べたのでメモ。以下のサイトが詳しいです。
Pythonにはthreading、multiprocessing、asyncioとどれも並列処理に使えそうなパッケージが3つあります。
でもって、以下は実際にコードを走らせたときの動画です。
プログレスバー
今回の例では順次レンダリング画像を保存していくので「どこまで処理が進んだか」は書き出されたファイルを見れば分かります。しかし1つの処理が非常に時間がかかる場合はプログレスバーが必要だと思うので、ネットで情報を探しておきました。
Progress of script can also be printed and updated with sys module to the console:(英語)
おしまい
twitterを本格的に初めてみた。ブログの更新記事をつぶやくのがメイン。あとCocos2dやUnity、その他アプリ開発に関連するツイッターの方をフォローして情報も集められたらなぁと思います。
— 柳澤@ゲーム作るよ (@designdrill) 2015, 12月 28