投稿記事

2020年 02月の記事 (3)

Heliodor 2020/02/24 19:08

Luaプリプロセッサ

以前もちらっと話した気がするのですが、うちではゲームに埋め込むためのスクリプトとして LUA を利用しています。
(簡単なコマンドラインツールを作るときは PYTHON ですが)
ステージ進行スクリプトとしてはもちろん、単なる INI ファイルの代わりとして使ったり、XML ファイルにプリプロセッサ機能を追加するのに使っています。

この「XML ファイルにプリプロセッサ機能を追加する」利用法について少しお話しします。


もともとデータファイルの形式としては YAML を使っていたのですが、YAML ってあまりにも人間フレンドリーになっているせいで逆に正規表現での検索がすごくやりにくいんですよね。インデントとか考慮しないといけませんし。

例えばこんな XML 形式のデータがあったとして

<Resources>
 <Texture file="player.png">
   <Sprite x="0" y="0" w="256 h="256" />
   <Sprite x="0" y="256" w="256 h="256" />
   <Sprite x="0" y="512" w="256 h="256" />
   <Sprite x="0" y="768" w="256 h="256" />
 </Texture>
</Resources>

これの YAML 版はこんな感じになると思うのですが、

Resources:
 Texture:
  file: "player.png"
  sprites:
   - {x: 0, y: 0, w: 256, h: 256}
   - {x: 0, y: 256, w: 256, h: 256}
   - {x: 0, y: 512, w: 256, h: 256}
   - {x: 0, y: 768, w: 256, h: 256}

後者って何かのミスでインデントを解除して保存とかやっちゃうと、もうファイルを見ただけでは間違いが分からなくなってしまうんですよ。

XMLだと行頭のスペースをうっかり削除しても、カッコを消してしまってもある程度自力で復元できますからね。
あと、いつも思うのはコメントアウトの怖さです。インデントを2文字とか短めに設定していると特にそうなんですが、

Resources:
Texture:
  file: "player.png"
# sprites:
#  - {x: 0, y: 0, w: 256, h: 256}
#  - {x: 0, y: 256, w: 256, h: 256}
#  - {x: 0, y: 512, w: 256, h: 256}
#  - {x: 0, y: 768, w: 256, h: 256}
(注:こんなのコピペする人居ないと思いますが「#」はCi-enの仕様上使うとややこしいので全角になっています)

こういうコメントアウトの仕方をしていた時に、素直に#を削除すると

Resources:
Texture:
  file: "player.png"
 sprites:
  - {x: 0, y: 0, w: 256, h: 256}
  - {x: 0, y: 256, w: 256, h: 256}
  - {x: 0, y: 512, w: 256, h: 256}
  - {x: 0, y: 768, w: 256, h: 256}

みたいになるんですよ。あるあるですね。
この場合インデントの位置がそろってなくてエラーになると思うのですが、はたして右に移動してインデントを揃えるべきか、左に移動して揃えるべきか、ぱっと見だとよく分からないんですよね。
インデントがもっと深くなっていたり、インデントの頭が何百行も上の方にあったりすると大混乱します。

ここで揃え方を間違えて

Resources:
Texture:
  file: "player.png"
sprites:
 - {x: 0, y: 0, w: 256, h: 256}
 - {x: 0, y: 256, w: 256, h: 256}
 - {x: 0, y: 512, w: 256, h: 256}
 - {x: 0, y: 768, w: 256, h: 256}

みたいにしてしまうと全く意味が変わってきますからね。

そんなわけで YAML あかんわ……となって XML に帰ってきたわけです。
YAML に比べて冗長な情報が多い分だけ安心感があります。


……とはいうものの。
やっぱりタイプ量が多いと面倒なんですよね。

C/C++ のプリプロセッサみたいなのを XML テキストにも適用できないかなあと思っていたのですが、たまたま見つけた LUA によるプリプロセッサが使えそうだったんで、使わせてもらいました。

http://lua-users.org/wiki/SimpleLuaPreprocessor

これを使うと PHP の LUA 版のような事ができます。
例えば上の例にあった

<Texture file="player.png">
  <Sprite x="0" y="0" w="256 h="256" />
  <Sprite x="0" y="256" w="256 h="256" />
  <Sprite x="0" y="512" w="256 h="256" />
  <Sprite x="0" y="768" w="256 h="256" />
</Texture>

というXMLは、以下のように書き換えることができます

<Texture file="player.png">
#for i=0, 3 do
  <Sprite x="0" y="$(256*i)" w="256 h="256" />
#end
</Texture>

行頭が# で始まる部分を lua スクリプトとみなして実行、
$(...) となっている部分の中身はそのまま lua の式として実行して、評価結果で置換する、というものです。

そもそも XML を手書きでつくらずにツールとか使えばよいのでは……という気がしなくもないのですが、なんだかんだいってテキストエディターが一番融通が利いて便利なんですよ。
どんなに古いノートPCでもテキストエディタが起動しないとか重くて使えないってことはないですしね!HAHAHA……。そろそろまともなノートに買い換えたい。


というわけでフォロワー特典はそのサンプル的なものです。

フォロワー以上限定無料

まずは無料プランで様子見を。 お気軽にフォローしてみて下さい。

無料

【 500円 】プラン以上限定 支援額:500円

このバックナンバーを購入すると、このプランの2020/02に投稿された限定特典を閲覧できます。 バックナンバーとは?

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

Heliodor 2020/02/15 15:50

サンダー!

先月ツイッターで公開していた雷エフェクトの製作過程と、その説明です。

モ●ハンワールドをプレイしていて麒麟にボッコボコにやられたとき「あの雷カッコイイ」と思い、自分でも作ってみたくなりました。頭単純ですね。
https://twitter.com/helio_dor/status/1220310552485908480
全部手描きでやれば見栄えは良くなると思うのですが、使い勝手が悪くなるのでプログラムで挑戦。
一応雷に見えますけど、なんか細いしちぢれてるし……うーむむむ。

パクrそっくりにする気はなかったんですが、麒麟の雷は結構太かったので、真似して少し太めにしてみました。
あと、雷が徐々に千切れて消えていく表現がカッコイイなと思ったので、そこも真似してみました。他にも細かい調整色々やってます。
https://twitter.com/helio_dor/status/1220979923113799680
だいぶ良くはなったけど、まだ何か違うかなー、やっぱり白一色では無理かなー、と感じました。
青っぽい色で縁取りをしてぼかせば大分綺麗に見えると思うのですが、単色の方が何かと扱いやすいので、正直あまりやりたくありませんでした。

……まあ結局やったんですが。

https://twitter.com/helio_dor/status/1221755461029916672
やはり真紅の稲妻とかブラックサンダーは厨二精神的に必要ですよね。
よくよく考えてみるとゲーム中で使う予定無いんですが。
どこかのボス戦で使うか……。


おまけ

横向きにも撃てるようにしました。麒麟の横向き雷で何回死んだことか。
余談ですが1面ボスは雷属性です(ネタバレ)。
https://twitter.com/helio_dor/status/1222127956257951745
そして雷の表現とは関係無く主人公が感電しているモーションを褒めてくれている人が何人か居て、仄かにリョナ界隈の闇を見た気がしました。
いや、実は私も結構好(以下自粛)


フォロワー向け特典は、やはり雷とは全然関係無い開発中の(変なバグが出たときの)スクリーンショットです。

フォロワー以上限定無料

まずは無料プランで様子見を。 お気軽にフォローしてみて下さい。

無料

【 500円 】プラン以上限定 支援額:500円

このバックナンバーを購入すると、このプランの2020/02に投稿された限定特典を閲覧できます。 バックナンバーとは?

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

Heliodor 2020/02/05 17:56

たのしいさんすう

数学ってなんの役に立つんスかねえ……。
そう思っていた時期が私にもありました


はい、楽しい算数のお時間です。
今回は画像がいっぱいあります☆

3DCGなどでは投影、移動、回転、拡縮などの操作を4x4の行列で表現していますが、あまり仕組みを理解せずに何となく使っていませんか?
まあ、まさに私の事なのですが。

私はこの変形行列を、この記事の後半あたりに書いてある「真実」に気づくまで全く理解せずに使っていたんですよね(しかもかなり最近まで)。
私と同じように、この「真実」に気が付いた瞬間「ああ、そういうことだったのか!!」とショックを受ける方が他にもいるかもしれませんので、私目線での変形行列解説をしたいと思います。


変形行列とは、ある点を別の点に変換するものです。
この「変形」を「行列」で表すと非常にうまく表現できるので、ある点からある点に座標を変換するという操作は行列。

行列Tに点Aを流し込むと点Bが出てくる、という事を

B = T * A

のように表現します。
例えば3点 A1, A2, A3 から成る三角形に、とある変形Tを施した結果どうなるか?を知りたければ

B1 = T * A1
B2 = T * A2
B3 = T * A3

を計算してやれば、変形後の三角形の各点 B1 B2 B3 が得られます。
ここでいう三角形とは、3DCGで言えばそのままポリゴンの事です。
ゲームの世界における三角形が画面上ではどのように映るのか?という「変形」を行列Tで表現してやれば、ワールド内の全ポリゴンの頂点座標に対して同じ行列Tを当てはめて、画面上での座標を得ることができます。


話を簡単にするために、まずは x 座標だけで考えてみます。
変換前の座標を Xin として、変換後の座標を Xout とした時

<LaTeX>
Xout = a Xin
</LaTeX>

が基本の式になります。
「Xin を a 倍すれば Xout になるよ」ってことです。
a が 1 より大きければ拡大、 a が 1 なら等倍、 a が 1 未満なら縮小ですね。
a がマイナスの値になると今度は左右反転します。


ところで、上の式は x しか使っていないので Yin (変換前の y) や Zin (変換前の z) がどんな値であっても Xout (変換後の x) には影響しませんでした。
もし Yin や Zin の値が Xout に影響するなら

<LaTeX>
Xout = a Xin + b Yin + c Zin
</LaTeX>

という式にします。
変換前の x, y, z それぞれの値が、変換後の Xin にどれだけ影響するかという値を a b c に入れます。


例えば

<LaTeX>
Xout = 1 Xin + 1 Yin + 0 Zin
</LaTeX>

のようにすると、Xin だけでなく Yin が大きくなればなるほど Xout も大きくなります。
Xinが大きくなるほど(変換前の点が右にあるほど)、それに加えて Yin が大きくなるほど(変換前の点が上にあるほど)、Xout が大きくなります(変換後の座標が右にずれていく)。
Yin = 0の高さを境にして、それより高いところにあるモノほど右にずれていき、低いところにあるものほど左にずれますので、変換前に |||| のようになっていたものは //// のように歪むことになります(剪断変形)。


では全体を左右にずらすには?

座標を左右にずらす(平行移動)、という操作は x に「移動量」を加えるのと同じです。
しかも、元の点がどこにあっても(Xin, Yin, Zin がどんな値でも) Xout には Xin に「移動量」を加えた値を入れたいのです。
つまり最終的に欲しい式は Xout = Xin + 移動量 ですから、以下のように定数項を追加します。

<LaTeX>
Xout = a Xin + b Yin + c Zin + d
</LaTeX>

例えば右に 2 だけ位置をずらす、という操作は x に 2 を加えるのと同じですから、次のようにします。

<LaTeX>
Xout = 1 Xin + 0 Yin + 0 Zin + 2
</LaTeX>

さて、これは x 座標を得るための式でした。実際には x だけでなく y も z も得る必要がありますから、x, y, z それぞれに対して同じように式を作ってやると

<LaTeX>
\begin{array}{rrrrrrrrr}
X_{out}&=&aX_{in}&+&bY_{in}&+&cZ_{in}&+&d \\
Y_{out}&=&eX_{in}&+&fY_{in}&+&gZ_{in}&+&h \\
Z_{out}&=&iX_{in}&+&jY_{in}&+&kZ_{in}&+&l \\
\end{array}
</LaTeX>

てな感じになります。この式で、a ~ l の値を適当に変えてやれば、拡大や平行移動などの変形ができるようになります。


ところで、上の式では d, h, l はただの定数になっていますが、これを定数ではなく「第4の座標 W の係数」であると考えると後々の計算が便利になりますので、(これは同次座標というのですが、それについては今度説明します)
とにかくこの第4の座標 W を入れてみることにします。

<LaTeX>
\begin{array}{rrrrrrrrr}
X_{out}&=&aX_{in}&+&bY_{in}&+&cZ_{in}&+&dW_{in} \\
Y_{out}&=&eX_{in}&+&fY_{in}&+&gZ_{in}&+&hW_{in} \\
Z_{out}&=&iX_{in}&+&jY_{in}&+&kZ_{in}&+&lW_{in} \\
\end{array}
</LaTeX>

この式を見れば分かる通り、とりあえず Win=1 としておけば今までの式と何ら変わることはありません。
さらに、Win という入力が増えたのでそれに対応する Wout という出力も得られるよう式を追加しておきます。

<LaTeX>
\begin{array}{rrrrrrrrr}
X_{out}&=&aX_{in}&+&bY_{in}&+&cZ_{in}&+&dW_{in} \\
Y_{out}&=&eX_{in}&+&fY_{in}&+&gZ_{in}&+&hW_{in} \\
Z_{out}&=&iX_{in}&+&jY_{in}&+&kZ_{in}&+&lW_{in} \\
W_{out}&=&mX_{in}&+&nY_{in}&+&oZ_{in}&+&pW_{in} \\
\end{array}
</LaTeX>

さて、ここからが一番重要なところです。
上の式から、係数の部分 a ~ p だけを抜き出してみます。

<LaTeX>
	\begin{pmatrix}
	a & b & c & d \\
	e & f & g & h \\
	i & j & k & l \\
	m & n & o & p \\
	\end{pmatrix}
</LaTeX>

これが変形行列です!!!!
これが 4x4 で並んでいる意味不明な数字の正体です!!

<LaTeX>
	\begin{pmatrix}
	X_{out} \\
	Y_{out} \\
	Z_{out} \\
	W_{out} \\
	\end{pmatrix}
	=
	\begin{pmatrix}
	a & b & c & d \\
	e & f & g & h \\
	i & j & k & l \\
	m & n & o & p \\
	\end{pmatrix}
	\begin{pmatrix}
	X_{in} \\
	Y_{in} \\
	Z_{in} \\
	W_{in} \\
	\end{pmatrix}
</LaTeX>

てことです!


例えば入力座標を変更しないでそのまま出力するには単位行列

<LaTeX>
	\begin{pmatrix}
	1 & 0 & 0 & 0 \\
	0 & 1 & 0 & 0 \\
	0 & 0 & 1 & 0 \\
	0 & 0 & 0 & 1 \\
	\end{pmatrix}
</LaTeX>

を使いますが、これは

<LaTeX>
	\begin{array}{rrrrrrrrr}
	X_{out}&=&1X_{in}&+&0Y_{in}&+&0Z_{in}&+&0W_{in} \\
	Y_{out}&=&0X_{in}&+&1Y_{in}&+&0Z_{in}&+&0W_{in} \\
	Z_{out}&=&0X_{in}&+&0Y_{in}&+&1Z_{in}&+&0W_{in} \\
	W_{out}&=&0X_{in}&+&0Y_{in}&+&0Z_{in}&+&1W_{in} \\
	\end{array}
</LaTeX>

と書くのと同じです。係数が 0 の部分は消えてしまいますから結局は

<LaTeX>
	\begin{array}{rrrrrrrrr}
	X_{out}&=&1X_{in}& &       & &       & &        \\
	Y_{out}&=&       & &1Y_{in}& &       & &        \\
	Z_{out}&=&       & &       & &1Z_{in}& &        \\
	W_{out}&=&       & &       & &       & &1W_{in} \\
	\end{array}
</LaTeX>

という部分だけが残り、

<LaTeX>
	\begin{array}{rcl}
	X_{out}&=&1X_{in} \\
	Y_{out}&=&1Y_{in} \\
	Z_{out}&=&1Z_{in} \\
	W_{out}&=&1W_{in} \\
	\end{array}
</LaTeX>

と同じになるという事が分かると思います。


私はこの「変形行列は変形式の係数部分だけを抜き出したモノである」と気づくまで数年かかりました(泣)。

<LaTeX>
X_{out}=aX_{in}+bY_{in}+cZ_{in}+dW_{in}
</LaTeX>

というのは4元1次方程式なんですね。
一応高校のときの数学で行列の基礎は習ったはずなのですが……。
(数Cってヤツですね。歳がバレるか?と思って今Wikiってみたら、数Cは数Bと数IIIに吸収されて2012年に行列と共に消えたらしいですね。……と思ったら、2022年に復活するらしいです)

その後、線形代数の授業では行列を使って連立1次方程式を解くテストを受けた覚えもあるのですが、当時は自分が何の計算をしているのか全く理解しないまま授業を受けていました。とりあえず先生の言う通りに数字をいじればマルが貰える、みたいな。

学校の授業って習ってる当時は全くありがたみが無いんですけど、どこでどう人生に関わってくるか分からないものですよね。まさかゲーム製作で行列に関わる事になろうとは。

「お前ら数学なんて学校卒業したらもう役に立たないと思ってるんだろ?実際役に立たないよ」と言って総ツッコミ食らっていた先生のこと思い出しました。

エロゲー作るのに役に立ってますよ先生……。(遠い目)



おまけ

今回の記事は数式を多用したので、久々に LaTeX にお世話になりました。ところでLaTeXって読めます?「らてふ」って読むみたいですよ。
以下のサイトで LaTeX のコマンドを入力すると、対応する画像を生成することができますので、興味があったら試してみて下さい。

https://texclip.marutank.net
http://hooktail.maxwell.jp/cgi-bin/mathimg.cgi



え? 画像?
やだなぁ、このLaTeXで作った数式の画像がいっぱいあったじゃないですかー。
(スミマセン)

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

月別アーカイブ

記事のタグから探す

限定特典から探す

記事を検索