PSD.jsを使ってPhotoshopのレイヤー命名規則のチェックを行う
ごぶさたしております。ちょびえです。
最近はblender2.8のベータが出始めたのでモデリングやらレンダリングやって遊んでいます。
前々からZBrushやらhoudiniで遊んでましたが、ポリゴンフローの良し悪しとか言われてもよくわからん、という事でモデリングに手を出し始めたんですがやってみると楽しいですねぇ。
2.8ではレンダリングエンジンにEeveeが入ったのでわりとゲームに近い描画方法となってきていますし、デフォルトが黒系のテーマで見慣れた画面ですし、ポストエフェクトなどもつらつらいじって試せるので手つけておくのオススメですよ。
(近いといってもゲームのshadingの考えでnode組み立てられるか、というとそうではないので戸惑ったりもしますが)
さて、今日はゲームのアセット制作をしていくと気になってくるPSDファイルの命名規則チェックについて書いてみようかと思います。
背景
ゲームではいわゆる量産系と呼ばれる画像アセットがあり、概ね同じ仕様で制作してあとで自動変換等の処理をしていく事になると思います。
手動でちまちま変換しても1枚あたり数分で終わるのでスケジュールに余裕があれば対応できますが、ふいに全コンバートしてくれとか言われると数量が多くてだいぶしんどい作業となってしまいます。そういうのは辛いんで先にいろいろ準備しておくと捗るってわけですね。
で、この自動処理をやっていく上で問題になってくるのが、期待する仕様と一致しない構成になっているファイルです。
よくある例としてレイヤー名や、レイヤーセット構造が期待したとおりでないファイルが紛れ込んでいる、という状況です。
数が10や20程度だったらまだ人の目でチェック等も可能ではありますが、数や種類が増えれば増えるほど目視でのチェックは難しくなっていきます。
また、時間が経過していくとメンバー異動などによる引き継ぎでチェックすべき項目が引き継がれなかったりしてしまったり、とても忙しい時に限って確認ミスって時間がかかるimportやり直し、とかもありがちです。
こういったものは可能であればプログラム側でチェックすべき仕様として残しておきたいものです。
こういった事例の場合、よくあるPhotoshopでの解決方法としてjsxを使ったスクリプティングでのチェックがあります。しかしながら、納品されていくpsdファイルはたいてい複雑、巨大になっていくのでphotoshopを操作するAPIの都合上jsxでやっているとレイヤー巡回等に時間がかかってしまうという課題もあります。
そこで今日はPSD.jsというライブラリを使ってレイヤー構成のチェックをするスクリプトについて簡単に解説してみたいと思います。
対象とする読者
- node.jsがチョット分かってDCCツール周りに興味がある方(インストールしてnpmたたければ大丈夫)
- こんなチェック目視でやりたくないんだ!という方
レイヤセット構成
チェックを行うにはルールが必要です。今回はキャラクター絵のpsdファイルを処理する、という体で・・・
・キャラクター絵のpsdファイルには事前に決められた各部位ごとに「~.png」とついたレイヤーがあり、後工程の自動変換でパーツごとに書き出ししたい。
こんなお題にしておきます。
これをやる前段のチェック処理として、事前に定義した命名規約にそったレイヤ構成でファイルが作られているかをチェックするぞ!というのが今回の記事の範囲です。
ややこしいですね。そいではいろいろ必要なことを考えていきます。
大雑把なPSDファイルのルール
- suffixとして.pngとついているレイヤー/レイヤーセットは後続の処理でパーツ書き出しとして利用される(今回の範囲の対象外)
- 命名の先頭に#(もしくは全角#(注:全角も許容したいことはよくある))があるレイヤ/レイヤセットはチェックをスキップする(非表示用)
- hair,face,bodyは必須レイヤーセットとして存在していることを期待する(今回は説明の省略で必須かというのはチェックしないことにします)
- 各部位には必須レイヤーセット(faceであればnormal.png、bodyであればbody.png)を必要とする(今回は説明の省略で必須かというのはチェックしないことにします)
- 各部位にはoptionalなレイヤー/レイヤーセットとして登録された名称であれば使っても良い(faceであればnormal.png, lough.png)
- psdフォーマットは互換性を優先にチェックをつけている(ライブラリ側でparseできなかったりします)
※その他部分に関しては今回最小のサンプルなのでチェックしません。
基準となるレイヤーセット構成サンプル
それではpsdファイルの簡単なサンプルを示したいと思います。
テキストで書き下すと概ね下記のような構成となっています。実際のゲーム開発プロジェクトだと概ね数倍以上の複雑さがあると思っていただければ相違ないかと。
1 2 3 4 5 6 7 8 9 |
# コメントアウト用レイヤ/レイヤセット `hair `front_hair.png `face `normal.png `lough.png `body `body.png `body_b.png |
上記のサンプルpsdファイルとしてGithubにダウンロードファイルを用意してあります。
(ご自身の責任でご利用ください)
チェックスクリプトに期待する機能
チェックが行える最小のサンプルとして次のような機能とします
・コマンドラインから実行する
・引数から単一のファイル名を取得して、事前に定義されたレイヤ名のルールチェックを行う
・エラーが見つかった場合は標準エラーに出力を行い、終了する
・重複したレイヤ名があった場合はエラーとする
・デバッグ表示として現在処理中のレイヤ名等を出力する
・レイヤセットなどの順番は気にしないことにする
・再帰的にレイヤを辿ってチェックを行う。なお親は直前の親までは引数に渡す
コード処理
まずはお約束としてこういう処理を試す場合は
必ずローカルにデータのバックアップを作成して、ローカルファイルに対して処理して問題がないかを検証しきる
ということを心がけてください。コードミスってて本データをうっかり上書きしちゃった日にゃぁ目も当てられねぇです。
そいではさくさくとコードを書いていきます。
package.json
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "name": "psd-validator", "version": "1.0.0", "description": "", "main": "validate.js", "dependencies": { "psd": "^3.2.0" }, "devDependencies": {}, "author": "chobi_e", "license": "CC0" } |
validate.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
var path = require('path'); var PSD = require('psd'); // 初期化 var errors = []; // node validate.js example.psdでやるのでargvは2。適切に変えてね。 var file = process.argv[2]; var file_path = path.resolve(file); var layerset_rules = ["hair", "face", "body"]; var per_rules = { "hair": [ "front_hair.png" ], "face": [ "normal.png", "lough.png" ], "body": [ "body.png", "body_b.png" ] } // ファイルの読み込み console.log("# loading " + file_path) var psd = PSD.fromFile(file_path); // psdのparse処理 var data = null; try { psd.parse(); data = psd.tree().export(); } catch (e) { console.error("[ERROR] parsing file failed: " + file); return; } /////////////////////// // validation実施 /////////////////////// // 稀に同じレイヤ名が重複していることもあるのでチェックはいれておこう! var dupcheck = {}; traverse(data.children, function(layer, parent) { if (parent == undefined) { // 親がない、ということはtoplevelなので var failed = true; for (var n in layerset_rules) { if (layerset_rules[n] == layer.name) { failed = false; break; } } if (failed) { console.error("[ERROR] " + layer.name + " doesn't match layerset rule"); return false; } } else { var failed = true; for (var n in per_rules[parent.name]) { if (per_rules[parent.name][n] == layer.name) { failed = false; break; } } if (failed) { console.error("[ERROR] " + layer.name + " doesn't match layer name rule"); return false; } } return true; }); // psdの巡回 function traverse(layers, callback, parent) { for (var n in layers) { // skip comment layer: #もしくは#が先頭の場合は処理をスキップ。 if (layers[n].name.indexOf("#") == 0 || layers[n].name.indexOf("#") == 0) { continue; } // DEBUG用に名前だしておこう。必要なければ削ってね。 console.log("# " + layers[n].name); // 直前の親までしかとってないので必要があればよしなに if (callback(layers[n], parent) == false) { // failed: エラー見つけても続行したい場合はreturnを外そう。 return; } if (!dupcheck[layers[n].name]) { //重複チェックもやっておくといいよ! dupcheck[layers[n].name] = true; } else { console.error("[ERROR] found duplicated: " + layers[n].name); } if (layers[n].children != undefined) { traverse(layers[n].children, callback, layers[n]) } } } |
今回はサンプルなんでこんなんで十分でしょう。
実行
それではコード書き終わったので試してみます。
1 2 3 4 |
# package.jsonにかかれたdependenciesをインストール(初回のみ) npm install # 実際にルールチェックを行う node validate.js example.psd |
出力内容
1 2 3 4 5 6 7 8 9 10 11 |
# loading C:\Users\chobie\Desktop\validate\example.psd # hair # front_hair.png # face # lough.png # normal.png # body # body_b.png [ERROR] body_b.png doesn't match layer name rule # body [ERROR] found duplicated: body |
おーっと、body_b.pngであるところが全角のbになっていますね。
ついでにbodyというレイヤセットが重複しているようです。
それではpsdをなおしてリトライしてみます。
1 2 3 4 5 6 7 8 9 10 11 |
node validate.js example.psd # loading C:\Users\chobie\Desktop\validate\example2.psd # hair # front_hair.png # face # lough.png # normal.png # body # body_b.png # body.png |
今度はデバッグ用の表示のみで、特にエラーがでなくなりました。
自動チェックしたいレイヤ名称部分のチェックができていますね!
まとめ
ということで、node.jsとPSD.jsを使ってpsdファイルのレイヤセットのルールチェックについて解説してみました。
簡易な説明ではありますが、実際に使ってみるまでの取っ掛かりにはなるかと思います。
プロジェクトで実用する場合は発注、納品、検品フローなども加味しつつどこになんのチェックを挟むのか、という判断をしていきます。
毎回スクリプトを手動で実行するのは手間なので、大抵の場合指定のファイル置き場にファイルが配置されたら自動でkickしてチェック実施や問題が発生した場合の通知などの処理をはさんでいきます。
すごく地味な処理で目立たない部分ですが、こういったプログラムで処理ができる部分を積極的に自動化していけばチェック担当の負担が減って本来注力すべきゲーム開発部分に力が注げます。
こういった細かいスクリプトであれば片手間でやれる範囲ですし、がっつり組まなければ後腐れも少ないので、常日頃からアーティストが使うツールをいじったり雑談して何をやればチームやメンバーがどういう方向を目指せるのか、というのをエンジニアリング部分から考えると良いかと思います。
(餅は餅屋なんで担当の方が居てくれたらおまかせしちゃう部分ではありますが)
それではよい開発を~
補足
・自分が使った中ではPSD.jsが一番手っ取り早く文字コード関連の問題もなく動作させられました
・jsxは外部関連のやりとりをし始めるとだいぶ辛いものがあるので他の言語使って操作したほうが良いと思っています(個人的に)
・変換系の処理を行う場合は外部ツールだとサポートしていない機能があったりするのでPhotoshopを操作する系のほうがアーティストと同じ作業が再現しやすいので都合良いです。psdparseとかも便利ですが適材適所
・なんかよくわからんことあったらtwitterのchobi_eまでお気軽にどうぞ