ゲームエンジンへの書き出し

書き出しウィンドウ

書き出し(Ctrl+E)は、ウィンドウのヘッダーで読み込みのすぐ後にあり、読み込みと同じ作りのウィンドウを開きます(D51):

  1. アセットパイプライン:最初は、ヘッダーのアセット選択に表示されているもの。書き出す内容はアセットグラフで設計します。
  2. 書き出し先フォルダー:パスを入力するか、選択…で選びます。プロジェクトに保存されます。
  3. 今すぐ書き出し:開いているプロジェクトを、保存済みかどうかにかかわらずそのまま書き出します。書き出しで保存されることはなく、無題のプロジェクトもそのまま書き出せます。

書き出しウィンドウ:アセットパイプライン、フォルダー、書き出されるファイル、書き出しボタン

書き出しを押す前に、ウィンドウには書き出されるすべてのファイルが、pxr namesの名前で一覧表示されます:それぞれフォルダー内のパスと種類(スプライトシート、JSON、GIF…)、フォルダーにすでにあるファイルには置き換えます。アセットパイプライン、フォルダー、プロジェクトを変更すると、一覧は再取得されます(小さなローダーが出るだけで、ウィンドウが止まることはありません)。そのままでは書き出せないパイプライン(2つの出力が1つのファイルに書き込むなど)は、そこに理由が表示され、修正されるまで書き出しは待機します。

押すと、同じ行がその場所のまま書き出しになります。書き出されるまではローダーが表示され、終わるとチェックマーク、サイズ、開く(システムがそのファイルを開くプログラムで)、フォルダーを表示が表示されます。問題が起きたら、止まったファイルの上に表示されます。停止で書き出しを途中で終了できます。書き出し済みのものは残り、書き出し中のファイルは削除されます。実行中にウィンドウを閉じてもかまいません。書き出しで再び開きます。

コマンドラインと同じものを実行します:pxr render PROJECT --pipeline NAME -o FOLDERを独自のプロセスで実行するので、Pixorは応答し続けます。対象は開いているプロジェクトのスナップショットで、生成フォルダーに書き出され(ファイルのパスは絶対パスに変換)、書き出しが終わると削除されます。ファイル名は、pxr renderがそのプロジェクトに付ける名前になります:プロジェクト名、無題ならモデル名です。すべてのファイルはパイプラインから新しく書き出され、アセットグラフのノードが生成したものがコピーされることはありません。

すべてのファイルはフォルダーに入ります。シートのファイル名はプロジェクトのPNGノードから取ります(out/hero.pngはFOLDER/hero.pngに書き出されます)。シートのフォルダーの下にあるパスのファイルはFOLDERの下で同じ位置を保ち、それ以外のパス(アーカイブ、アニメーション、別の場所に名前を付けたアイコン)は最後の部分だけが残ります。

書き出しで書かれるもの

どの書き出しもシートを書き出します。アクションと向きごとに1行、フレームごとに1列で、すべてのセルが同じサイズ、ピボットはどのセルでも同じピクセルです。アセットグラフでシートにファイルノードを追加する(またはコマンドラインで--exportを使う)と、同じ名前のファイルが隣に書き出されます。それぞれがシートから接続されたノードで、独自の設定を持ちます。--export allは以下のすべての形式を書き出しますが、p8とvideoは名前を指定したときだけ書き出されます。カートリッジはそのために作ったシートにしか合わず、全クリップと全方向の非圧縮動画は数ギガバイトにもなるからです。2つの形式が同じファイルを書くことはありません。そうなるプロジェクトはpxr namesが拒否します。

フォーマット ファイル 用途
png name.png シート。インデックスカラーのPNG(インデックス0が透明)
json name.json セルの矩形、フレームの表示時間、アクションと向きごとのタグ、ピボット。Asepriteの「array」レイアウトなので、Aseprite向けのインポーターで読み込めます
aseprite name.aseprite インデックスカラーのAsepriteファイル:final、lines、flatのレイヤーと、スタイルグラフの追加の出力ごとに1つのレイヤー。アクションと向きごとのタグ、フレームごとの表示時間、pivotスライス
法線 name_normal.png 2Dライティング用の、同じレイアウトのノーマルマップ
深度 name_depth.png 同じレイアウトの深度:最も近いものが白、最も遠いものが暗く、シート全体で1つのスケール(meta.pixor.depthが両方をメートルで示します)。何もない場所は透明。並べ替え、フォグ、2.5Dのライティングに
発光 name_emission.png 発光するピクセルを、その発光の強さで独自の色に(発光マテリアル、またはモデルのエミッシブマップ)。それ以外は透明。グローやブルームに
レイヤー name_lines.png、name_flat.png、そしてスタイルグラフの追加出力ごとのname_LAYER.png シートとしての線とフラットな色のレイヤー、そしてスタイルグラフが指定する場合はノードの画像
パレット name.hex, name.gpl パレット(LospecとGIMPの形式)
godot name.tres Godot 4のSpriteFramesリソース
gif name_walk_Southeast.gif, … アクションと向きごとに1つのアニメーションGIF。ホールドはフレームの遅延として。共有やプレビューに
apng name_walk_Southeast.apng, … 同じものをアニメーションPNGで。ミリ秒単位の正確な遅延付き
動画 name_walk_Southeast.avi, … アクションと向きごとに1つの動画:すべてのピクセルをそのまま収めた非圧縮AVI。トレーラーやストアページに
light-kit name_ramps.png、name_normal.png、name_light.png、シェーダー エンジンでのパレットに忠実なライティング(下記を参照)
レポート name_report.png すべてのチェックの結果を印したシート(下記を参照)
p8 name.p8 PICO-8のカートリッジ(ゲーム機モードを参照)
zip ノード上のパス アセットグラフのZIPアーカイブノード:接続したファイル、または書き出したすべてのもの

行にはアクションと向きの名前が付きます:walk_Southeast。

シートがどのフレームを取り込み、どう並べ、1回のレンダリングでいくつファイルを書き出すかはアセットグラフが決めます:つなぎ替えるまでは、1つのシートノードとそのファイルノードです。

フレームの名前と中身

シートの各フレームには名前とidがあり、それぞれ別の問いに答えます。

名前はテンプレートです。プロジェクト自身の言葉に当てはめるパターンなので、素直な既定値は初心者の望みどおりになり、スタジオは自分で選んだのではない規約にも従えます:

{project}_{action}_{side}_{index:2}.{ext}     hero_walk_East_00.png

使える語は{project}、{object}、{action}、{side}、{layer}、{set}(スプライトノードがそのフレームを描いたパーツセット)、{size}、{index}、{ext}です。{index:3}は数値をゼロで3桁に埋めます。{{と}}は波括弧を書きます。Pixorが知らない語は、hero_{genre}_00.pngというファイルになるのではなく、テンプレートを入力した時点でエラーになります。また、中身が空の語はその前の区切り文字も一緒に消すので、向きのないスプライトはhero_Static_00.pngになります。

ファイル名に使えない値(/ \ : * ? " < > |)は、黙って置き換えずに拒否されます。空白は_になります。唯一の例外はPixor自身の言葉です。lib:walkというライブラリのクリップは、名前ではlib-walkになります。そのコロンはあなたのものではなくPixorのものだからです。

2つのフレームが同じ名前になる場合は、何もレンダリングする前に拒否されます。コマンドラインでは--namesで設定するか、プロジェクトに保存します:

pxr project set hero.pixor --names '{object}/{action}-{side}-{index:3}.{ext}'
pxr names hero.pixor                  # what it will write, without rendering
pxr names hero.pixor --json           # the same, for a build script

IDはフレームが何かを示し、どこに置かれたかは示しません。オブジェクト、アクション、向き、アクション内の時刻、レイヤーのハッシュで、JSONの各フレームの横と.asepriteのユーザーデータに書き込まれます。シートを詰め直したり、クリップを追加したり、余白を変えたりすると矩形は動きますが、IDは変わりません。そのため、IDでフレームを参照するビルドは、パッカーが走るたびに壊れることはありません。クリップを別のフレームレートでサンプリングし直すと、IDは変わります。別のフレームになるからです。

より小さいシート

アセットグラフの配置ノードで(またはコマンドラインで):

  • 余白をトリミング(--trim)は、各フレームをピクセルのある範囲まで切り詰めます。JSONは各フレームの元のセル内での位置(spriteSourceSize、trimmed: true)を記載し、Godotのファイルは各フレームのmarginを設定するので、エンジンは引き続きすべてのフレームをピボットに合わせて配置できます。
  • 繰り返すフレームを1回だけ保存(--dedupe):まったく同じフレーム(ホールドしたポーズ、静止したオブジェクトの各向き)は1つの場所を共有します。
  • シートの最大の幅(--max-width 4096)は、シートがそれより広くなる前に新しい行を始めます。テクスチャーサイズに制限のあるエンジンやGPU向けです。
  • シートの最大の高さ(--max-height 2048)は、それより高いシートを、クリップ単位でページに分割します:hero_1.png、hero_2.png。それぞれ独自のJSONとほかのファイルが横に置かれます。
  • 1行あたりのフレーム数(--columns 8)は、1行にその数のフレームを並べ、すべてのクリップを前のクリップに続けて配置します:コンタクトシートや、エンジンのインポーターが求める固定グリッドになります。
  • 別の向きから描いた向き(アセットグラフの反転ノード):左右対称のモデルは、左から見ても右からの反転と同じに見えるので、西側はレンダリングする必要がありません。東のカメラのスプライトを取り、西として反転すれば、シートにはwalk_Eastから描いたwalk_Westが入り、ピクセル、法線、ボックス、ルートモーションが反転されます。違いのあるカメラだけをレンダリングし、残りは反転してください。
  • 各向きの角度はJSONに入っています:各フレーム(pixor.degrees)と、まとめてmeta.pixor.sidesの下に({"South": 0, "Southeast": 45, ...})。正面からの角度を、上から見て反時計回りに、--sidesが受け取る形式で記録します。ゲームはこの表で角度を行に変換します。

影のレイヤー

影のレイヤー(--shadow contactまたは--shadow drop)は、シートと同じ配置でname_shadow.pngを書き出します。影が落ちる所は黒、それ以外は透明です。Contactは足元の塊で、モデルの接地面の大きさに合わせます。落ち影は、キーライトと反対側の地面に落ちるシルエットです。好きな不透明度でスプライトの下に描くか、別に並べ替えてください。アセットグラフのLayerノードでshadowを指定でき、Overlayで1枚のシートのスプライトの下に置けます(アセットグラフ)。

実行時のカラーサイクル

サイクルするマテリアルがある場合、JSONにはmeta.pixor.cyclesの下にそれぞれが記録され("material"と、パレット内の"indices"をサイクル順に)、Pixorはパレットのインデックスとしてのシートname_index.pngを書き出します。エンジンは、フレームを増やして保存する代わりに、ティックごとにそのパレットのエントリーを回せます。同じ階調を共有するほかのマテリアルも、一般的なパレットサイクルと同様に一緒にサイクルします。分けたい場合は、サイクルするマテリアルに独自の色を与えてください。

複数のモデルのシーンとパララックス背景

プロジェクトは複数のものを持てます。地面に並べたモデルを1枚の絵としてレンダリングし、1つのライトと、モデル同士の影を落とします。村の一角、ダンジョンの部屋、野営地、無料ライブラリを使ったストアページのアートなど。

それぞれがアイテムです。モデル、その立ち位置、向き、大きさで構成され、シーングラフのアイテムノードとしてチェーンの末尾に追加されます。その後にリングや散布を置けば繰り返されます。コマンドラインでは、--item1つが1アイテムです:

pxr project new camp.pixor \
  --item tent.glb@-1.8,-1.6,25,1.2 \
  --item campfire.pixoritem@0,0 \
  --item barrel.glb@2,1,0,1,2 \
  --ground 7,5,#5d7a45 \
  --size 200
pxr render camp.pixor

@x,z[,yaw[,scale[,layer]]]はアイテムの位置です(メートル単位、xは右、zはカメラ方向)。--ground W,D[,#rrggbb]はその下に地面を置きます。:clip[,phase]を付けると、そのアイテムは独自のクリップを独自の位置から再生し、隣のアイテムは静止したままです。パスはプロジェクトからの相対パスです。

パララックスのレイヤー(パララックスのレイヤーのFileノード、2〜8枚のレイヤー、または--parallax N)は、画像を奥行きでレイヤーに分け、スクロールする背景にします。各レイヤーは同じステージとパレットで単独にレンダリングされ、遠いレイヤーほど、距離感を出すようにかすみの色(--haze)へ向かって暗く青くなります(遠くのレイヤーにかすみをオフ、または--haze noneにすると、パレットが固定のゲーム向けに、どのレイヤーもパレットのままになります)。アイテムのlayerでそのアイテムを置くレイヤーを決め(0が最も遠い)、ほかは奥行きで振り分けられます。Pixorはname_layer0.png、name_layer1.png、…、name_parallax.json(各レイヤーの画像と、最も遠いもので0.25、最も近いもので1となる推奨スクロール速度)、そしてレイヤーを重ねたname_parallax.pngを書き出します。複数のモデルからなるシーンに限らず、どのモデルも分割できます。アセットグラフのLayerノードは、layer0、layer1、…とレイヤーを名前で指定し、独自に作るシートに載せます(アセットグラフ)。pxr project set --parallax N --haze #rrggbb|noneでプロジェクトに両方を設定します。

コンソールのモード

自作ゲーム機やファンタジーコンソール向けには、スタイルでコンソールを選びます(または--console)。するとPixorはそのゲーム機自身の色と制限を使います:

コンソール 色 スプライトごと メモ
ゲームボーイ 4階調の緑 3+透明 明度による階調
NES 54色のマスターパレット 3+透明 8 × 8のタイルごとにも確認
PICO-8 その16 すべての16 .p8カートリッジを書き出す(p8にチェック。シートは128 × 128に収まる必要があるので、最大幅を128に設定します)
TIC-80 その16(Sweetie 16) すべての16 TIC-80のimport spritesでPNGを読み込む
C64 その16 3+透明 マルチカラースプライト:ピクセルの幅が2倍。幅は偶数にします(C64のスプライトは24px)

スプライトに3色しか使えない場合、Pixorはモデルの影、ベースカラー、光を最もよく表すゲーム機の3色を選ぶので、すべてのフレームと向きで同じ3色を使います。スタイルのセクションでは、画面上のフレームが収まっているかが表示されます。pxr console check sheet.pngはシート全体(色、フレームごとやタイルごとの色数、横長ピクセルのペア、サイズ)を確認し、シート内のレシピからゲーム機を読み取ります。ゲーム機モードでのレンダリングはすべて、書き出したシートで同じ確認を行い、収まらない場合は失敗します(終了ステータス1、問題を一覧表示)。独自のパレットはゲーム機モードでもゲーム機のパレットに置き換えず保持するので、ゲームボーイで16色のパレットを使えば、黙って4色の緑に差し替えるのではなく、はっきりと指摘されます。NESの色はゲーム機の映像信号からPixorが計算し、ほかはゲーム機の公表値です。

エンジンでのパレットに正しいライティング

エンジンはノーマルマップからスプライトを滑らかなシェーディングで照らすので、パレットになかった色が入り込みます。ライトキット(または--export light-kit)にチェックを入れると、スプライトはゲーム内の光に反応しつつ、照らされたピクセルはすべて、Pixorの帯、色相シフト、ハイライトを保ったまま、自分のカラーランプの段にとどまります。Pixorはシートの隣に次のものを書き出します:

ファイル 概要
name_ramps.png 各ピクセルのカラーランプ(赤にランプ番号 + 1。線のように光が当たらないピクセルは0)
name_normal.png 法線、ビュー空間:xが右、yが上、zがカメラ方向
name_light.png ルックアップ:ランプごとに1行、光のレベルごとに1列、次にハイライトの色、そして色ではない2列(そのランプ独自のハイライトのしきい値。光沢や金属のマテリアルは、つや消しのものよりハイライトが付きやすいため)
pixor_palette_light.gdshader, name_light.tres Godotのシェーダーと、すべてのテクスチャーを設定したマテリアル:マテリアルをSprite2DまたはAnimatedSprite2Dに設定します
PixorPaletteLight.shader Unityのシェーダー:これでSpriteRenderer用のマテリアルを作り、3つのマップを設定します
pixor_palette_light.fsh, .vsh GameMakerのシェーダー。コメントにマップの割り当て方があります

シェーダーは、1つのキーライト(light_dir、ライトへ向かう方向、法線の空間)、プリセットのフィルライト、そしてPixor自身と同様にランプごとにルックアップテクスチャーからしきい値を取るスペキュラーとリムのハイライトで照らします。specularやrimに-1から1の数値を設定すると、スプライト全体でそれを上書きします。1より大きいとそのハイライトをオフにし、-1未満(デフォルト)ではルックアップの値を使います。ハイライトは、隣接する4ピクセルのどれかもハイライトになる場所にだけ描かれます。これは孤立した明るいピクセルを防ぐPixor自身のルールで、min_highlight 1でオフになります。ライトはスクリプトから動かせます。たとえば松明の方へ:material.set_shader_parameter("light_dir", dir)。3つのマップは、フィルタリングなし、圧縮なし、ミップマップなしで読み込んでください。書き出す前に確認するには、スプライト上で2Dライティングのプレビュー(L)をオンにして光の扱い:ライトキットを選びます:ライトがポインターの方を向き、フレームはシェーダーと同じルックアップ、同じルール(ハイライトは別のハイライトの隣だけ、ランプのないピクセルはそのまま)で照らされます。レンダリングのライトなら、フレームはPixorが描いたとおりに見えます(例外は帯の境界の数ピクセル)。落ち影、ディザリング、アニメーションシートのちらつき抑制はPixorだけのものなので、エンジンで照らすシートでは落ち影をオフにしてください。JSONにはmeta.pixor.light_kitの下にファイルとライトが記録されます。

パレットのバリエーション

バリアントを追加(--variant NAME、複数指定可)は、同じシートを別の色で書き出します。Pixorはパレットのインデックスで描くので、バリアントで変わるのはパレットだけです:すべてのピクセル、線、階調はそのままです。

  • エフェクト:hit-flash、frozen、poisoned、petrified、burning、silhouette、selected、時間帯のdawn、noon、dusk、night、季節のautumnとwinter(緑がオレンジに、または淡く雪をかぶった色に変わります)。
  • チームカラー:team:blue(またはred、green、yellow、purple、orange、teal、pink、white、black、またはteam:#3050e0)はチームカラー(--teamまたはマテリアルで指定したマテリアルや色)だけを塗り替え、肌、鋼、金はそのままにします。各階調は明るさを保つので、ランプはそのまま読み取れます。1つのスプライトから4チーム:--team cloth --variant team:red --variant team:blue --variant team:green --variant team:yellow。実行時に下のルックアップテクスチャで色を塗り替えるエンジン向けに、JSONはチームのパレットインデックスをmeta.pixor.teamに記載します。
  • すべての色を回転:hue:120は、色の付いたすべての階調を120度回転します。灰色は灰色のままです。
  • ほかのパレット:palette:pixor-8やpalette:my-colours.hexは、明暗を分けたまま、すべての色をそのパレットの最も近い色に移します。

各バリアントはname_<variant>.pngです。バリアントがあると、Pixorは実行時にパレットを入れ替えるためのファイルも2つ書き出すので、1枚のシートですべての段階やチームに対応できます:

  • name_index.png:各ピクセルのパレットインデックスを灰色の値にしたシート(0は透明)。
  • name_lut.png:幅256ピクセルのルックアップテクスチャ。パレットごとに1行(0行目はシート自身、k行目はバリアントk)。

シェーダーはlut(index / 255, row)を参照します。JSONにはバリアントとその行がmeta.pixor.variantsの下に記載されます。

すべての書き出しに入っているレシピ

Pixorが書き出すすべてのファイルは、それを作ったプロジェクトを覚えています。シート、そのレイヤーシートとマップ(_lines、_flat、_normal、_depth、_emission、_shadow)、パレットのバリアントとインデックスシート、.aseprite、各GIF(コメント内)とAPNGです。どれかをPixorにドロップする(またはプロジェクトを開くで選ぶ)と、プロジェクトが戻ってきます。.pixorファイルがまだあればそれが、なければレシピから作り直した新しいプロジェクトが開きます。パスは書き出したファイルからの相対パスで保存され、相対にできないパスはファイル名だけを残すので、共有したシートからフォルダー名が見えることはありません。ファイル内のレシピのチェックを外す(または--no-recipeを渡す)と、レシピを含めません。

Godot 4

  1. name.pngとname.tresを、並べてプロジェクトにコピーします。
  2. .tresはres://name.pngからシートを読み込みます。別の場所に置く場合は、コマンドラインで書き出すときに--godot-path res://path/name.pngを渡すか、.tresの先頭にあるパスを編集してください。
  3. AnimatedSprite2Dを作成し、そのSprite Framesにname.tresを設定します。各アクションと向きがループするアニメーションになり、フレームホールドも含まれます。
  4. ピクセルをくっきりさせるには、Texture > FilterをNearestに設定します。

Unity

  • Aseprite Importerパッケージを使う場合(2D Aseprite Importer):name.asepriteをAssetsにドロップします。各タグがアニメーションクリップになります。finalレイヤーを使ってください。
  • 使わない場合:name.pngをSprite(Multiple)として読み込み、Filter ModeをPoint、CompressionをNoneにして、セルサイズでスライスします(セルサイズはname.jsonのmeta.pixor.cellにあります)。ピボットはmeta.pixor.pivotにあり、セルの左上からのピクセル数です。

GameMaker

name.pngをスプライトストリップとして読み込みます。アニメーションごとにストリップレイアウトで書き出す(--layout strip)か、セルサイズでグリッドを切り分けます。原点はname.jsonのピボットに合わせます。

Aseprite

name.asepriteを開くか、Asepriteファイルノードの開くをクリックします(PixorはPATHや通常のSteamとitchのフォルダーからAsepriteを探し、見つからなければ場所を尋ねます)。タグにはすべてのアクションと向きが並びます。finalレイヤーが表示され、linesとflatは手で仕上げるための非表示の補助レイヤーです。スタイルグラフが名前を付けた追加出力も同様に、finalの上にあります。ファイルはPixorのパレットを使ったインデックスモードです。

上から描いて、作業を残せます。独自のレイヤーを追加して描き、Pixorでモデルやルックを変えてから、再ベイクします:

pxr rebake out/hero.aseprite

Pixorが管理するのは自分で書き出したものだけです。再ベイクはプロジェクトをもう一度レンダリングし、次のようにします:

  • Pixorが作ったレイヤー(final、lines、flat)のピクセルを、誰も描き込んでいない場所で置き換えます。
  • あなたのレイヤーをそのまま残します。移動、名前の変更、並べ替え、色の変更は一切せず、描いたセルは描いたフレームにとどまります。どのフレームにもその内容(アクション、向き、時間)から作るIDがあるので、歩きの途中に3フレーム増えても、描いた目は新しいポーズにずれず、元のポーズにとどまります。再ベイクはそれをフレームごとに報告します:moved your paint on frame 1 is on frame 2 now、そしてどのフレームが新しいか(--jsonではmovedとnew_at)。
  • 塗ったすべての色をそのインデックスに保ち、新しい色はパレットの末尾に追加します。AsepriteでグレースケールやRGBに変えたシートでは、それに合わせてレンダリング結果を灰色やカラーで書き込みます。
  • スライスと、そのキーを、元のフレームがどこへ移っても、そのフレームに保ちます。
  • モデルが描いた部分の下から動いてしまったフレームにタグpxr checkを付け、描いた部分はその場に残します。

単独では決めず、あなたが決めるまで両方を残しておくものが2つあります:

何が起きたか 再ベイクで行われること
Pixor自身のレイヤーに描いた場合 Pixorのレイヤーには新しいレンダリングが入り、あなたの編集はそのすぐ上の独自のレイヤーfinal editsに移り、フレームにはpxr conflictのタグが付きます
描いたフレームが新しいレンダリングにない(クリップが短くなった) フレームは描いたものと一緒に末尾に残され、pxr removedのタグが付きます

どちらを選んでも何も失われません。pxr rebakeは、どちらにするかを指定するまで終了コード7で終了します。--resolve keepは編集を独自のレイヤーとして残し、--resolve pixorはPixorのレンダリングを採用して編集を捨てます。--dry-runは何も書き込まずに再ベイクで何が起こるかを表示し、--jsonはそれをツール向けに出力します。拡張機能はダイアログで同じことを尋ねます。

GIFとAPNG

各アクションと向きが、それぞれループするファイルになります(ループしないクリップは1回だけ再生されます)。フレームはスプライトのパレットを使い、インデックス0が透明です。GIFまたはAPNGノードのスケール(--anim-scale K)は、1から16までの整数倍で拡大するので、64 pxのスプライトを、すべてのピクセルがくっきりしたまま256 pxのGIFとして書き出せます。

GIFは遅延を100分の1秒単位で保存するので、各フレームの遅延は丸められますが、クリップ全体の長さは保たれます。ブラウザーは20 ms未満の遅延を100 msとして表示するので、GIFのフレームが20 msより短くなることはありません。50 fpsを超えると、GIFはそれより短く表示されるはずのフレームを省き、クリップと同じ長さで再生します。APNGはミリ秒を正確に保存し、JSONと同様に累計で丸めるので、12 fpsのクリップのフレームは83 msと84 msになります。サイトがPNGしか受け付けない場合は、.apngファイルの名前を.pngに変えてください。アセットグラフのレイヤーノードが描いたクリップは、シートの表示どおりにそのレイヤー(線だけ、フラットカラーなど)をアニメーションさせます。ノーマルマップ、深度マップ、発光マップのレイヤーではスプライトがアニメーションします。GIFはパレット色しか持てないからです。

動画

クリップごとの動画は、各アクションと向きをname_walk_Southeast.aviとして書き出します。24ビットカラーの非圧縮AVIで、どのエディターや変換ツールでも開け、何も失われません。ピクセルを滲ませるコーデックはありません。ノードには3つの設定があります:

  • スケール(1〜16、初期値は4):アートの1ピクセルが動画の何ピクセルになるか。
  • 1秒あたりのフレーム数(1~100、初期値30):動画のレートは1つなので、スプライトのフレームはその長さの分だけ繰り返し書き込まれ、クリップの長さは動画1フレーム以内の誤差に保たれます。
  • 背景:スプライトの背後にあるもの。動画には透明度がありません。

コマンドラインの--export videoは初期値を使うか、--video-scale K、--video-fps N、--video-background #RRGGBBを使います。--export allは動画を含めないので、動画は名前で指定してください。

圧縮されていないので大きくなります。64 pxのスプライトをスケール4にすると1フレームあたり約200 KBです。1ギガバイトを超える動画は書き出されずに拒否されます。オンラインに載せるには変換してください:ffmpeg -i hero_walk_South.avi -crf 0 hero_walk_South.mp4。

シンプルなエンジンと自作コード

name.jsonを読みます:frames[i].frameはセルの矩形、frames[i].durationはそのホールド時間(ミリ秒)です。meta.frameTagsはフレームをアニメーションにまとめ、ループせず1回だけ再生するタグには、Asepriteと同じく"repeat": "1"が付きます。frames[i].pixorには、フレームのtag(フレームタグの名前)、その中でのindex、actionとside、id、そしてpivotが入ります。pivotはエンジンがスプライトを立たせるセル内の点で、タグノードがクリップに独自のものを与えていなければシートのものです。meta.slicesのピボットのスライスには、ピボットが変わるところにキーがあり、.asepriteファイルのピボットのスライスにも同じキーがあります。

ルートモーション

その場でで書き出したクリップには、フレームごとに値が2つ追加されます。端数を保ったピクセル単位です(xは右、yは下):

  • frames[i].pixor.root:モデルのルートがあるはずの位置。クリップの開始時の位置から測ります。
  • frames[i].pixor.root_delta:次のフレームまでにルートがどれだけ動くか。最後のフレームではクリップの終わりまでの移動量なので、ループする歩行は同じ速さで進み続けます。

クリップのとおりにキャラクターを動かすには、フレームが終わるたびにスプライトの位置にroot_deltaを加えます。

インベントリのアイコン

pxr icons(またはアセットグラフのアイコンノード。プロジェクトの書き出し時に作られます)は、モデルを揃いのインベントリアイコンにします。どのモデルもStudioライトリグの下、同じ¾の角度から、16、24、32、48ピクセル(または--sizes)で描き、シルエットをレアリティの色(--rarity common|uncommon|rare|epic|legendary)にし、--framedを付けるとインベントリのスロットのような枠付きタイルに載せます。各アイコンはPNGで、サイズごとにアトラスicons_N.pngと、各アイコンの位置を示すicons_N.jsonができます。フォルダーを指定すれば、パック全体をアイコンセットにできます。アプリでは、フォルダーをウィンドウにドロップしてスプライトの代わりにアイコンをオンにします。サイズ、レアリティ(選ばなければ各ファイル名から)、枠がそこにあり、レシピはアイコンの横にicons.pixoriconsとして保存されます:

pxr icons library/pickups --out icons --rarity epic --framed

--rarity from-nameは、ファイル名に含まれる語が示すレアリティを各モデルに与えます(potion_rare.glb、Sword-Epic.glb。該当する語がなければcommon)。JSONには各アイコンのレアリティが記録されます。ファイルは名前順に処理されるので、フォルダーの並び順にかかわらずアトラスは同じになります。

レシピでパックを保存できます:--save pack.pixoriconsはフォルダーと設定(パスはレシピからの相対パス)を書き出し、pxr icons pack.pixoriconsでパックをバイト単位で同じに作り直せます。フォルダーに追加した小道具は次の実行に加わり、レシピの後に付けたフラグはその実行だけ設定を1つ変えます(pxr icons pack.pixoricons --sizes 64)。

ボクセルとスプライトスタック

MagicaVoxelの.voxは他のモデルと同じように開けます:シーン内のすべてのモデルがシーングラフの指定どおりの位置に置かれ(MagicaVoxelでの表示どおりに回転・移動)、パレットも読み込まれ、発光マテリアルのボクセルは光ります。1ボクセルは0.1メートル、+Zが上として読まれます。

pxr voxels MODEL -o hero.vox --height 32は、任意のモデルを高さ32のボクセルに変換します:表面はテクスチャーと色からサンプリングし、内部は埋め、すべてのボクセルをルック自身のパレット(--preset、--paletteなどのルックのオプション)にするので、ボクセルがスプライトと一致します。--stackを付けるとスプライトスタックも書き出します:hero_stack.pngはモデルを水平にスライスして下から順に横に並べ、それぞれを上から見たもので、hero_stack.jsonにはスライスのサイズと枚数が入ります。ゲームはスライスkをスライスk - 1の1ピクセル上に描き、全体をまとめて回転させます。これがスプライトスタッキングで3Dを擬似的に表現する方法です。

pxr voxels knight.glb -o knight.vox --height 32 --stack --preset selout

すべてが一致しているか確認する

pxr audit folder --style game.pixorkitは、フォルダー内の書き出したすべてのPNGと.asepriteファイルを読み、各ファイル内のレシピを使ってキットと照合します。プリセット、ライトリグ、ビュー、カメラのピッチ、キーライト、明暗の帯、線、パレット、メートルあたりのピクセル数、そして使っている色がすべてキットのパレットにあるか。キットがなければ、最初のファイルが基準になります。違いはそれぞれ一覧にされ、違いが1つでもあれば終了ステータスが1になるので、ビルドの番人にできます。

アプリでは、一貫性ボード(3人の人物のボタン、一貫性ボード:すべての向きを一度に、またはB)が、現在のフレームをすべての向きから1本の地面の線の上に並べて表示し、各シルエットの頂点に印を付け、その下に幅、高さ、色数を表示します。ボタンの下には:

  • シルエットは、すべての向きを形だけ1色で描きます。シルエットとして読み取れないポーズは、ゲームのサイズでも読み取れません。
  • ピボットは、各向きが立ち、回転の中心とするピクセルを示します。足がそこからずれている向きは、スプライトが向きを変えるときに滑ります。
  • 色数は各向きの色数を書き出します。向きの間で高さが1ピクセル以上違うとき(向きによって高さが…異なります)や、ある向きの色数がほかより大幅に多いとき(たいていは1つの向きだけに当たる光が原因)、ボードに表示されます。

上のpxr auditは、フォルダー全体を対象にする半分です。すべてのシートを1つのキットと照合します。

チェックで見つかったことを、該当するピクセル上に

Pixorが行うすべてのチェック(孤立ピクセル、1ピクセルで描かれた線の角、太い線、パレット外の色、ゲーム機モードの許容より多い色を持つタイルやフレーム)は、書き出し時にテキストを1行出すだけでなく、該当するピクセルに印を付けます。

  • アプリ内では:スプライトの上のチェックボタン(警告の三角形)でフレームに印が描かれ、スタイルとアセットのステップのレポートセクションには、これまでにレンダリングした全フレームのすべての指摘が一覧表示されます。1つをクリックすると、Pixorは印を付けた状態でそのフレームと向きに移動します。一覧の下では、指摘の種類ごとにどの設定がそれを決めるかが示されます(孤立ピクセルはクリーンアップ、ギザギザの角は線、色はパレット、ゲーム機の制限はコンソールモード、書き出しの反転と合わない反転した向き)。設定へ移動でその設定が開きます。
  • 反転した向き:アセットグラフに反転ノードがあると、反転して描かれる向きは反転元の向きと比較され、モデルの対称性が足りない場所では変わるピクセルに印が付きます。書き出し時の警告だけでなく、選んでいる最中にも確認できます。
  • 書き出し時:reportにチェックを入れる(または--export report)と、Pixorはシートの横にname_report.pngを書き出します:すべての指摘箇所に印を付けたシートです。単独のピクセルは塗りつぶし、タイルやフレームは枠で囲むので、対象がそのまま見えます。
色 内容
シアン 孤立ピクセル(オーファン)
黄色 1ピクセルで角を曲がる線
マゼンタ 2×2の線のブロック:太い線
赤 パレット外のピクセル
オレンジ ゲーム機の上限より多い色を使う8 x 8のタイル
濃いオレンジ ゲーム機の上限より多い色を使うフレーム
ピンク ゲーム機にない色
紫 反転元の向きと異なるピクセル
緑の箱 テクスチャーファイルが見つからず無地の色で描かれたパーツ:ファイルをモデルの隣に置いてください

ピクセルエフェクト

シーングラフのエフェクトノード(アウトライナーではエフェクトの下)は、3Dパーティクルで描くエフェクトを追加します:爆発、ヒットスパーク、斬撃、魔法の炸裂、回復、ポータル、煙、砂ぼこり、炎のループ、雨、雪、ホタル、落ち葉。それぞれが独自のアクション(fx-explosion、…)で、モデルと同じパイプラインでレンダリングされるので、スプライトのパレット、ピクセルサイズ、光、線を受け、同じゲームの一部に見えます。フレーム数、サイズ、エネルギー、位置、シード(同じシードからは常に同じピクセル)を設定します。ボーンに追従させ(手からの斬撃、銃口の閃光)、クリップに重ねて再生します(斬撃の下の振り、またはPixorでキーを打ったクリップ)。エフェクトのみにチェックを入れると、モデルなしで、キャラクターのシートに揃えた独自のレイヤーとして描きます。ヒットフレームは攻撃が当たるフレームに印を付け、JSONではmeta.pixor.hitsに記載されます。コマンドラインでは--effect fireflies:12 --effect-clip shatter --effect-hit 6。クリップはPixorで作ったもの(きらめきの下で飛び散る破片にキーを打ったもの)でも構いません。空のプロジェクトの画面で読み込み…と無料モデルを見るの横にあるエフェクトを作成(またはpxr effect explosion -o fx.png)は、モデルなしでエフェクトを作ります。

ヒットボックス、ハートボックス、ソケット

インスペクターのパーツのボックス(または--hitbox、--hurtbox)で、そのパーツをヒットボックス(攻撃を当てるもの:剣)またはハートボックス(攻撃を受けるもの:体)にし、ボーンのJSONのソケット(または--socket)でソケット(手、銃口)にします。すると各フレームのJSONにpixor.boxes(part、kind、x、y、w、h。セルのピクセル単位で、各パーツが実際に覆うピクセルから)とpixor.sockets(セルのピクセル単位の各ボーンの位置)が入るので、ゲームのコードは推測せずに当たり判定やエフェクトの取り付けができます。モデルファイルでもパーツに印を付けられます(pxr.box)。

ペーパードールのレイヤー

小道具を付けている場合、ペーパードールのレイヤー(--paper-doll)は、同じキャンバス上の小道具なしのモデルname_base.pngと、小道具ごとに1つのレイヤー(name_sword.png:見えているピクセルとその周りの線)も書き出します。ベースを一番下にして重ねると完全なシートになり、小道具のレイヤーを差し替えると装備が替わります。name_stacked.pngは、エンジンが重ねるのと同じように重ねたもので、ずれがないことを確認するプレビューです。どのパーツのピクセルもシート自身のものです。ベースはパーツなしで描いた体なので、シートと異なるのはパーツの縁から数ピクセル以内と、パーツが体に影を落とす場所だけです。ぴったり重なるレイヤーにするには、落ち影をオフ(--no-shadow)にしてください。スプライトノードがパーツセットで描いた行は、レイヤーではなくそのノード自身のシートに属します。(アプリは保存したプロジェクトからpxrでそれらを作ります。変更があれば先に保存するので、レイヤーは画面上のものと同じです。)

モデルの一部である装備(小道具として取り付けるのではなく、キャラクターと一緒にモデリングされた兜、盾、マント)はピースです:

pxr render knight.glb --clip all --paper-doll --piece helmet:Knight_Helmet --piece gear:Round_Shield,1H_Sword
pxr project set knight.pixor --paper-doll --piece helmet:Knight_Helmet

各ピースは構成するパーツを(アウトライナーに並ぶ名前で)指定し、小道具と同じく独自のレイヤーname_helmet.pngになります(アプリでは、パーツの独自のペーパードールレイヤーでそのパーツ名のレイヤーになります)。見えている部分のピクセルだけなので、体の後ろの盾は体に隠れる部分が隠れます。ベースはすべてのピースと小道具なしでレンダリングされるので、ヘルメットが覆っていた部分(その下の頭)もベースに描かれます。--no-piecesでプロジェクトからピースを消せます。アセットグラフのレイヤーノードは、baseまたはピースや小道具の名前をレイヤーとして指定し、レイヤーを好きに並べられます(アセットグラフ)。

このページで説明していることを、自分のモデルで試してみましょう。ブラウザーで無料で使えます。

ブラウザーで試す Pixorを入手