本記事は、白鷺祭ブログリレー2022 作品紹介記事です。
TL;DR
- 論理回路をモチーフにしたパズルゲームを作成し、白鷺祭2022で展示しました
- プログラム、グラフィック、サウンド、UIなど、それぞれこだわりポイントを解説しています
あいさつ
こんにちは。ゲームはFPSしかしないのでアイデア難真っ只中のM!KAです。今日は、私が白鷺祭2022で展示したゲーム、「Logically!」について紹介と解説をしようと思います。
ゲームプレイ
この節では、Logically!のゲームプレイについて解説していきます。
ゲーム内容
ゲーム画面は、このように、方眼紙状のマップと、張り巡らされた回路部品、そして棒人間「A君」で構成されています。プレイヤーは、A君がゴール(この面では旗マークのマス)まで行けるように、道程上にある閉じたドアを開けることを目指します。このために、回路上のスイッチを操作して、ドアに電流を供給していきます。
カーソルキー(←, ↑, →, ↓)で画面上にあるカーソルを操作し、スイッチのところでZキーを押すと、ON/OFFが切り替わります。この画面では、もともとOFFだったスイッチをONにすることにより、電流が流れ、ドアが開きました。これでA君は旗マスまで移動できるようになったため、旗に向かって歩きはじめました。このようになると面クリアです。
続いて移動した面には、ドアが3つありますが、やることは一緒です。ただし今回は、全てのスイッチをONにすれば開くというわけではありません。 スイッチとドアの間には、論理素子が接続されています。これらの入力端子(青色矢印)の部分に適切な入力をしなければ、出力端子(黄色矢印)に電流が流れず、ドアにも電流は流れていきません。
解答例は上図のようになります。 一番下の、赤色の論理素子が「AND素子」です。2つある入力端子の両方に電流を流さないと、出力端子に電流が流れません。 真ん中右側の、緑色の論理素子が「OR素子」です。2つある入力端子の最低1個に電流を流すことで、出力端子に電流が流すことができます。 一番上の、青色の論理素子が、「NOT素子」です。こちらはANDやORと違い、入力端子が1個になっています。入力端子に電流を流すと出力端子には入力が流れず、逆に入力しなければ出力されます。
この面をクリアすると、上図、3面目に移ります。この面では、AND、ORの先にそれぞれNOTが付いていますが、どのようにしたらドアが開くでしょうか...? このように、ステップを踏んで、論理回路がどのように動いているかを学んでいくゲーム内容となっています。
途中からはカエルが道を塞ぎますが、やることは一緒です。電流を流せばカエルは感電し、退散してくれます。なんで頬に電気マークがついているんですか?
後半になると、D-FFといった、複雑な回路が出てきます。回路から挙動を推測するのが難しくなっていきます。
メインのゲームモードとは別に、メインメニュー内の「EDITOR」を選択すると、エディターモードで遊べます。このモードでは、様々な回路素子を使って、自分だけの面を作ることができます。また、ESCメニューから、その場で試遊してみることもできます。
技術的説明
プログラム
このゲームのプログラミングには、「Hot Soup Processor 3.6」を使用しました。この言語は、Windowsで軽いGUIアプリケーションやゲームを作成することに向いているスクリプト言語です。コメント行込でメインプログラムは1741行、サウンドドライバは59行で書くことができました。とても簡潔でよい。 とはいえ、すべてのコードを解説することは困難なので、このゲーム特有のこだわりの点をいくつかご紹介しようと思います。
電流・クリア判定
電線に電流がどのように流れているか、また、A君が旗まで行く経路を確立できているかどうかの判定には、途中曲がりくねっていたり、分岐があったりする経路を辿る必要があります。この経路探索には、「今いる場所を中心に、上下左右に電線または素子が繋がっているかを、順番に検査していく」という動作が必要となります。 このためのアルゴリズムに、「深さ優先探索」を用いました。この方法では、電流源となる素子からスタートして、出力端子に線がつながっているかを検査します(これを①とする)。線が繋がっていれば、①をその線の位置から呼び出して処理を続けます。再帰呼び出しを行う必要がありますが、探索処理のリエントラント性を保つ限りは比較的簡単に書くことができます。
//1498行目
*nestableproc_reach
if sublev=31:return
repeat 4
if map_type(sx(si-1)-1,sy(si-1) )=1 & map_state(sx(si-1)-1,sy(si-1) )=0 { map_state(sx(si-1)-1,sy(si-1) )=1 : sx(si)=sx(si-1)-1 : sy(si)=sy(si-1) : si++ : gosub *nestableproc_reach}
if map_type(sx(si-1) ,sy(si-1)-1)=1 & map_state(sx(si-1) ,sy(si-1)-1)=0 { map_state(sx(si-1) ,sy(si-1)-1)=1 : sx(si)=sx(si-1) : sy(si)=sy(si-1)-1 : si++ : gosub *nestableproc_reach}
if map_type(sx(si-1)+1,sy(si-1) )=1 & map_state(sx(si-1)+1,sy(si-1) )=0 { map_state(sx(si-1)+1,sy(si-1) )=1 : sx(si)=sx(si-1)+1 : sy(si)=sy(si-1) : si++ : gosub *nestableproc_reach}
if map_type(sx(si-1) ,sy(si-1)+1)=1 & map_state(sx(si-1) ,sy(si-1)+1)=0 { map_state(sx(si-1) ,sy(si-1)+1)=1 : sx(si)=sx(si-1) : sy(si)=sy(si-1)+1 : si++ : gosub *nestableproc_reach}
loop
si--
return
上図が、電線のON状態を伝播するためのアルゴリズムです。変数map_type (int[128][128])
が、各ブロックのON/OFF状態を格納しているので、隣り合うmap_type
の内容を検査しながら、0であれば1にしてそこから再帰を飛ばします。
ただし、今回は、ユーザー定義関数ではなく、gosub
命令によるサブルーチンを自己呼び出しすることによって、深さ優先探索を実現しました。この場合、サブルーチン内部で定義した変数はすべてグローバル変数扱いとなり、このままでは再帰可能性が確保できません。そのため、各変数をスタックと呼ばれる構造として扱うようにしました。
スタックとは、「データを後ろから追加、取り出すときも後ろから」というモノで、その名の通りデータを上に積み上げるというイメージで簡単に想像できます。たいていのプログラミング言語においては基本的なデータ型として扱われており、有名所だとC++のstd::stack
や、Pythonのlist
もスタックとして扱えるメソッドがあります。
ただし、HSPにはスタック型が無いので、配列変数とインデックス変数を用いて疑似スタックを実現しました。変数sx (int[])
、sy (int[])
に現在検査中の座標が、変数si (int)
に現在のサブルーチンで使用中の、sx
とsy
の要素番号が格納されています。また、再帰呼び出し時には、再帰中で検査する予定の座標を格納し、si
をインクリメントします。サブルーチンをreturnで抜ける際には、si
をデクリメントします。
サウンドドライバ
ゲームエンジンはモジュールとして分離できませんでした(これまでの作品はアクションゲームだったので、モジュールに必要な機能がほとんど固定だったため、分離していました。今回はメインプログラムとごっちゃです)が、サウンドドライバは自作しました。モジュール化してメインプログラムの外に出しています。
//logically_snd.hsp (サウンドドライバモジュール) (内容略)
#module logically_soundmng
#deffunc snd_init
//Procedure...
return
#deffunc snd_load int id, str name, int category, str filename
//Procedure...
return
#deffunc snd_play str name
//Procedure...
return
#deffunc snd_allstop
//Procedure...
return
#deffunc snd_playasid int id
//Procedure...
return
#defcfunc snd_getnamebyid int id
return nm(id)
#global
//49行目、 サウンドドライバの初期化とロード
snd_init
snd_load 25,"chippop",1,"chippop.mp3"
snd_load 0,"title",1,"logically_title.mp3"
snd_load 1,"menu",1,"logically_menu.mp3"
snd_load 2,"level1",1,"logically_level1.mp3"
snd_load 3,"level2",1,"logically_level2.mp3"
snd_load 4,"turn",0,"turn.wav"
snd_load 5,"invalid",0,"invalid.wav"
snd_load 6,"clear",0,"clear.wav"
snd_load 29,"scblaze_salamander",1,"salamander.mp3"
snd_load 31,"scblaze_sfc",1,"sfc.mp3"
snd_load 30,"scblaze_fasterandfaster",1,"fasterandfaster.mp3"
snd_allstop
//477行目、タイトル画面の描画処理。"if pscr = 0 { }" 中は、全体状態の遷移時に1回だけ実行される
*dmng8
if pscr=0{
pscr=1
snd_play "title"
}
サウンド素材をドライバに登録するsnd_load
命令に、int category
という引数があると思います。これは、サウンドドライバ内部で、同じカテゴリに登録されているサウンドを同時に鳴らさない(先に鳴っているモノを止めてから、次のサウンドの再生を開始する)ようにするために使用しています。
今回は、BGMには1
を、SEには0
をセットすることで、BGM、SEが、それぞれ同時に2つ以上鳴らないようにしています。そのうえで、BGMとSEの共存は許容しています。
//logically_snd.hsp 16行目
#deffunc snd_load int id, str name, int category, str filename
mci "open "+filename+" alias C"+id+" type mpegvideo"
//logically_snd.hsp 51行目
#deffunc snd_playasid int id
//Procedure...
mci "play C"+id+" from 0"
if cat(id)=1{
mci "setaudio C"+id+" volume to 400"
}
また、サウンドドライバ内部では、発音制御にMedia Control Interfaceを使用しています。
経路探索処理のインターリーブ
//867行目(コメント行略)
if gamestat=100{
gosub *proc_editor_control
}else{
if f\2=1{
gosub *proc_eleboy
}
if gamestat=1{
gosub *proc_control
}
}
//878行目
if f\2=0{
gosub *proc_recalculate
}
このゲームは、30FPSで動作しています。ただし、経路探索処理は再帰呼び出しを含むこともあり、計算時間が長いです。HSPはインタプリタ言語であり、計算処理が非常に遅いですから、これだとマップによっては、処理時間が延長して、30FPSを維持できない可能性があります。そのため、このゲームでは、「A君のドア到達判定」「電流の到達範囲計算」と、2つある経路探索動作を、それぞれ15FPSで、交互に動かすことにより、1フレームあたりの処理時間を抑制しています。 ユーザー操作や描画、アニメーションなど、経路探索以外の動作は30FPSで動作しているため、見かけや触った感じはカクカクしていませんが、内部ではこのように、コマ撮りレベルのスピードで処理が進んでいます。
反省点
1741行が簡潔だと最初に言いましたが、普段はもっと短くコードを書くようにしており、これは恐らく、今まで書いたコードの中で最も多い行数です。ゲームエンジン部分をモジュールにしなかったため、コードが無駄に冗長になってしまいました。ノリとやる気でイッキに書いたため、これからモードを増やしたりなどの保守が難しくなってしまいました。ゲームとしては悪くないですが、プログラムとしては最悪ですね。
また、経路探索アルゴリズムには本来、「幅優先探索」を用いるべきでした。深さ優先探索は、再帰呼び出しを行うことで簡潔に書けるというメリットがありますが、HSPのコールスタックのネストレベルは、最大32段であるため、gosub
で素朴に実装した深さ優先探索では、約25ブロック以上離れたところにあるブロックを探索できません(A君から旗までが一直線になっている理由にはこれがあります。これもあり、各面が無個性に、単調になってしまいました)。
グラフィック
グラフィックは、すべて自作しました。テクスチャの制作にはドットペインターALFARを使用しています。
まずそもそも、画面構成を考えるにあたって、ゲーム全体の雰囲気を決める必要がありました。前回出展した友好祭2022のときは、8bit風シューティングゲームを制作したため、今回は16bit風にしようと考えました。16bit時代の家庭用ゲーム機(ここでは第4世代と呼称する)のグラフィック仕様を簡単にまとめると、以下のようになります(出典1、出典2、出典3)。
ゲーム機 | 画面解像度 | 使用可能な色数 |
---|---|---|
PCエンジン | 512 x 240 または 256 x 224 | 512 |
メガドライブ | 320x224 または 256 x 224 | 512 |
スーパーファミコン | 256 x 224 | 32768 |
今回は、「解像度: 512 x 384、色数 4096色」の画面を目標にしてグラフィックを作ることにしました。解像度が参考に対して少し大きめな理由は、解像度が小さすぎると1画面に表示できるタイルの数が少なくなり、パズルゲームとしてのインパクトが減ってしまうと考えたからです。また、色数も、512と32768の中間のような値になっていますが、これは色作りの簡単さと表現力を天秤に掛けた結果です。RGB各値が16段階なので、
また、タイル1枚のサイズは「31 x 31」としました。80年代~90年代のゲーム機では、画面を構成するタイル1枚のサイズは、8の倍数であることが多かったためです。8 x 8や16 x 16では視認性の問題があると感じたため、今回はそれらよりも大きい、32 x 32を基本サイズとしました。各マップのマスには枠線があるので、そのぶんを引いて絵は31 x 31です。
テクスチャ
タイルの一覧は先程の画像でお見せしましたが、いくつかの気を使ったポイントを紹介します。
まず、タイルの色使いに関して、今回は明るい印象を与えたかったため、彩度低めの色を多く使いました。
例えば、ANDのタイルは「#F64
」、ORのタイルは「#6F6
」、NOTのタイルは「#46F
」(いずれも16進数 #RGB表記)という色になっています。少し白っぽい赤・緑・青となっているわけです。パステルな色遣いにすることで、親しみやすさを出そうと精一杯の努力をしています。
あと親しみやすさでいうと、カエル(サイズ: 64 x 64)。かわいい!!!(自画自賛)
また、状態変化のあるブロックについては、分かり易さも考慮しました。 上図はスイッチとドアのテクスチャです。スイッチなら倒れる向き、ドアなら開いているか閉まっているかが、ブロック状態の本質となりますが、それとは別に、ON状態を明確にするために、背景を状態によって変化させています。これにより、ブロックの状態が直感的に分かりやすくなる効果を期待しています。当たり前だと思うかもしれませんが、これを意識するとしないとでは、ユーザーエクスペリエンスが大分変わってくると思います。
ポップアップのアニメーション
さらに、今回始めて挑戦したことの一つに、ポップアップのアニメーションがあります。「LEVEL 1」だったり「CLEAR!」だったりを真ん中に表示するポップアップは、下から出現し、2秒ほど真ん中に留まり、また上に移動して消えていきます。また、横長の白枠が半透明になっています。
//1692行目(コメント行略)
pal512 5,5,5
gmode 3,0,0,192
grect 256,192+mbox_yo,0.0,512,64+2
半透明四角形の描画には、grect
命令とgmode
命令を組み合わせた、半透明矩形描画機能を使用しています。
//1682行目
if mbox_time<6{
mbox_yo=int(6.1*((6-mbox_time)*(6-mbox_time)))
}
if mbox_time>=6 & mbox_time<=60{
mbox_yo=0
}
if mbox_time>60{
mbox_yo=-1*int(6.1*((mbox_time-60)*(mbox_time-60)))
}
下から出現して上に逃げていくロジックは、ポップアップ出現からの内部時間を保持する変数mbox_time (int)
を用いて、2次関数により、変化量を曲線にして実現しています。これにより、等速直線運動とは違う、「スッと入ってスッと抜けていく」ようなアニメーションを実現できます。
メニュー画面背景
今回始めて取り組んだ要素のひとつに、メニュー画面の背景アニメーションがあります。
メニュー画面では、ゲーム中に登場するタイルが、上からランダムな方向、ランダムな数で降ってきて、画面下部で跳ねながら画面外にフェードアウトしていきます。また、枠線がグルグル移動していきます。このような、やや派手な表現にしたのは、タイトル画面が最初に見る画面だから、印象を良くしたいと考えたためです。
//300行目
gmode 3,0,0,192
color 0,0,0
grect 256,192,0.0,512,384
また、タイルや枠線をそのまま描画しただけでは、主張が強すぎ、メインコンテンツである「メニュー画面のテキスト」を分かりにくくしてしまうため、少し明るさを下げています。このためにもgrect
命令を使用しています。
反省点
まず、上で述べたポップアップアニメーションの半透明ですが、レトロゲーム的表現とは離れており、一貫性に欠けています。さらに、ゲーム無いのビジュアルエフェクトがほとんど実装出来ておらず、スクロールなどの動きもないため、全体的に地味な画面になってしまいました。 それらほどの重要性は無いですが、メニュー画面の背景も、ガラケーのゲームみたいな感じになってしまい、ここでも時代感の一貫性の無さが出てしまいました。
サウンド
サウンドは、効果音を含めすべて自作しました。制作にはFL Studio 21を使用しています。
BGM
ゲーム内BGMはこちらから試聴できます。
グラフィック節で述べたように、このゲームのテクスチャの色遣いは、16bit世代のゲーム機をイメージしたものとなっております。そのため、BGMの雰囲気も、違和感が無いようにする必要があります。今回は、メガドライブの仕様を参考に、音の感じにレトロ味を持たせられるようにしました。
メガドライブのサウンド部分に関する仕様は、以下のようになっています(出典1、出典2)。
チップ名称 | 種類 | 同時発音数 |
---|---|---|
YM2612 | 4オペレータ FM音源 | FM 6(CH6 can be used as a DAC) |
SN76496 | DCSG音源 | Pulse 3, Noise 1 |
この仕様にだいたい適合するように、BGMを作成することにしました。
シンセサイザーには、FL Studioに標準付属しているFMシンセサイザープラグイン「Sytrus」と、有償WTシンセサイザープラグイン「Serum」を用いて音作りを行いました。
Sytrus(FM音源, ノイズ)
「Sytrus」は、FM音源と、DCSG音源のノイズとの音色を再現するために採用しました。いわゆるサウンドエフェクトの機能を持たないYM2612に合わせて、リバーブやコーラスなどの機能は使わず、FMシンセサイザー部分のみを使用しました。
上図がSytrusの画面で、この音色はタイトルBGMのコードシンセの設定です。上部のタブでオペレータ(発振器)を選択して、中央左側でオペレータの波形、周波数倍率(YM2161の仕様に合わせて整数倍のみを使用)、エンベロープなどを設定します。そして、中央右側で、オペレータ同士の接続を設定します。この接続により、FM変調の種類や度合いが決定されます。
Serum(矩形波)
一方で、「Serum」を採用した最大の理由は、「よく使っていて慣れているから」ですが、普段やっているSerumの音作りとは少し違った音作りをしています。こちらは、DCSG音源のようなパルス音を使用するために採用しました。
DCSG音源が発音できるのは、「50:50パルス波を、最大3和音」のみです。画面中央やや左側、「OSC A」の部分のこの四角い波形が、50:50のパルス波です。また、画面右下、「POLY」と書かれた部分が「3」にセットされていると思います。これは、このシンセサイザーの最大同時発音数が3になっていることを表しています。これで、DCSGの音源仕様に合わせることができました(実際には、Serumの音色は数学的に正確なモノであるため、SN76496とは波形に多少の差異があります)
ドラムなど
この時代のゲーム機でドラムを再現するのに使われる音源は、「FM音源」「PCM音源(DPCM, ADPCM含む)」がありますが、今回は簡便さのために普通のドラム音(wav形式)を使い、発音のしかたを工夫することで雰囲気を出す方向を模索しました。
上図は、今回使用したスネアドラムです。使用サンプル「FPC Snare 6」は、FL Studioに同梱されているサンプルパックから使用しましたが、まずこの音色はやや古風なエレクトロスネアの音で、80年代~90年代前半に好んで使われていた音色に類似していると感じたため使用しました。
また、音量のエンベロープについてですが、アタック、サステイン、リリースが0に設定されていると思います。当時のゲーム機では、PCMサンプルで鳴る音に細かいエフェクトやエンベロープを設定できないことが多かった(出典1、出典2)ため、それに合わせた形となります。
さらに、当時のゲーム機では、PCMサンプルを鳴らすのに使えたチャンネルは1個程度でした(出典上同)。その挙動を再現するために、FL StudioのSamplerについている、「Group」と呼ばれる機能を使用しました。この機能を使うと、あるグループに属する音の発音を、同じ/別のグループの発音によって制御することができます。上図左中央の「Group」とついている枠がそれです。 この枠内の「Cut」で、その音の発音時に止める音のグループ番号を、「By」で、どのCutが発動されたときにその音の発音を止めるかを設定します。これを同じ値にすると、ポリフォニーモードをMonoにしたのと同じ効果が得られます。 更に、別のドラムの音(キックドラムなど)も同じグループに設定すると、どちらか片方が鳴っているときはもう片方は鳴らない、という状態を作り出すことができます。実際の16bit世代のゲーム機では、別のパーカッションが鳴り始めると先に発音していたパーカッションが発音停止するので、この挙動はそれの再現となります。 たとえば、「Stage 2」の0:05あたりで、スネアドラムを2回叩いた後にすぐキックドラムが入ることで、スネアの後半が完全に消えています。Groupの設定により、このような挙動を実現することができます。
楽曲
楽曲は、「90年代の」「パズルゲーム」ということで、主に、コンパイル時代の「ぷよぷよ」の楽曲を参考に作成しました。例えば、ステージ2のBGM「Stage 2」は、ぷよぷよ通(1994)の「へいき、へいき」を参考にして作っています(ただ、1曲3時間程度で雑に仕上げたのもあり、基本的に推敲が足りていないし、展開も不足しています)。
サウンドエフェクト
サウンドエフェクトに使用した音も、同様の理由でFM音源のものを使いました(一部ノイズ音源も使用しています)。基本はSytrusのみで音作りをし、「ドラムなど」と同じく、複数の種類の音が被らないようにしています。
反省点
雰囲気が合ってない...かなりポップな感じに寄った楽曲となりましたが、この点が、画面変化が少なくリアルタイム性の少ない(このへんがぷよぷよとは違う)ゲームとのミスマッチになってしまいました。ビジュアルエフェクトの乏しいこのゲームにおいては、もう少し静かなBGMでもよかったかなと思います。エフェクトに関しては、音量調整が適当なのと、「エフェクトが鳴るときにBGMの一部が消える」などの挙動は再現困難だったため、時代感の再現が曖昧になってしまい、中途半端になってしまったかなと思います。
ユーザーインターフェース
今回の展示は、私がコンピュータハウスランダムに入部してから2回目となります(1回目は友好祭2022)。また、このゲームは、論理回路の教育を志向している節があり、不特定多数の人間にプレイしてもらいたいモノとなっています。そのため、ユーザーインターフェースについても、友好祭の反省を活かして、よりプレイしやすくなるように調整しました。
キー操作ガイド
そもそも文化祭におけるゲーム展示では、各々が全く違うゲームを、全く違う技術を用いて制作して、ひとつの場所で展示します。そのため、操作体系がタイトル毎に全く異なるという状況が生まれます。現状はゲームランチャー(どのゲームを遊ぶかを、来場者が選択できる)に操作説明を書いていることが多いですが、それだけでは不十分だと感じ、「ゲーム中にもしばしば操作方法が登場する」ようにしました。 例えば、上図のメニュー画面。「上下キーで選択、Zキーで決定」という操作ですが、これをメニュー画面中では常に画面下部に表示するようにしました。
1面では、画面中央のスイッチをONにすればクリアとなりますが、スイッチの右上に矢印が表示されています。カーソルキーでの操作をメニュー画面から知っているプレイヤーは、このことにより、カーソルキーで動く紫色のカーソル(画面中央)を、スイッチの上にカーソルを持っていけばいいことが分かります。
さらに、スイッチの上にカーソルを持っていくと、矢印が「Zキーを押し込むアニメーション」に変化します。プレイヤーがZキーを押すと、スイッチがONになり、ドアが開き、A君は旗に向かって歩きはじめます。これにより、プレイヤーは、スイッチ操作によってドアを開ければいいことが分かります。 このように、操作説明テキストに戻ること無く、操作方法を理解しやすいレベルデザインを目指しています。
2種類の論理素子ブロック
これまで、論理素子ブロックは、「AND」なら「&」、「OR」なら「||」、「NOT」なら「!」というような記号が付けられていました。これは、大抵のプログラミング言語における論理演算子とほぼ同じであり、プログラミング経験者には一目で分かる内容となっています。ただし、プログラミング非経験者には記号対応がやや分かりにくいため、論理演算子記号の他に、「漢字ブロック」を選択できるようにしました。
この設定は、メインメニューの「OPTION」内から設定できます。
反省点
そもそも、レベルデザインが良くなく、論理回路のファーストタッチの題材としてはややイマイチになってしまいました。「適当にスイッチをON/OFFするだけでクリアできてしまう」「A君が歩き出すことが分りづらく、そのうえ画面がすぐ遷移するため、クリア基準がわかりにくい」など、UXがあまり作り込めませんでした。
まとめ
なんかいろいろがんばったけど、ちょっと惜しいかなっておもいます
次からはけいかくてきにゲームをつくろうとおもいます
謝辞
白鷺祭展示前に、テストプレイとヒアリングに協力して頂いた若干名の方々、ありがとうございました。