「ゲームプログラミング/RPG」の版間の差分

削除された内容 追加された内容
→‎画像関係: バグが発生するが裏画面を複数の関数で受け渡しできる方法が見つかったので参考例として追記
画像処理関係のプログラムは、かなり難解なので、後半に回す。
819 行
学校の授業だと関数や構造体などによる階層化の手法を習うので、初心者はついつい階層化したくなりがちなので、ソレを意識的に封印する必要があります。制作開始の直後はまだ、階層化のタイミングではないのです。
 
 
== 画像関係 ==
=== 半透明のレイヤー合成を使いたい場合 ===
==== 概要 ====
たとえばRPGでは、戦闘に入ったときなど、マップ画面の上に半透明の黒レイヤーでマップをやや隠して、その上に戦闘画面の画像を描画したいような場合もあるかと思います。
 
RPGツクール作品とかウディタ作品とかの戦闘シーンでも、そういう描画のゲームがよくあります。
 
もし、自前のプログラムでこういう半透明レイヤーを描画したい場合は、GDI+ のインクルードで出来ます。または、最初から同等の命令が組み込んである C# などを使えば出来ます。
 
Win32API で半透明レイヤーを使いたい場合の方法については、wikibooks では『[[Windows API/画像の操作]]』に説明があります。
 
Direct X を使わなくても、Windows 製のゲームで半透明レイヤーの画像合成をする事は可能です。
 
 
なお、実際にそのまま半透明レイヤーを乗せると、描画に少し時間(コンマ数秒ほど)が掛かります。
 
 
このため、もしも、キー入力するたびに背景ごと毎回描画してしまうと、画面がチラついてしまいます。
 
 
なので、対策として、たとえば、
:裏画面の描画を使うとか、
:または、(win32 API やGDI + だけのプログラミングをあきらめて) Direct X を使うなど、
いろいろと対策しましょう。
 
==== 具体的な解決策 ====
半透明レイヤーを重ねるコードを書く際、どうコードを書けばいいかというと、答えは直感に反しますが、答えは、レイヤーで隠される画像側のプログラムのほうにコードを書くことになるかと思います。(隠す側の画像のプログラムではないのです。)
 
たとえば、戦闘中に背景でマップ画面が半透明の黒レイヤーで隠されている場合、戦闘画面用コードではなく、マップ画面の描画コード側に、半透明処理を描くことになります。このため、フラグ管理をする事になり、たとえば「レイヤー変数 reiyaa が 1なら、このマップ画像に半透明レイヤーを載せる」のようなコードをマップ側に書く必要があるのです。
 
コレはなぜそうなるかというと、理由は、windowsでは何故かHDCで宣言した画像ハンドルをグローバル変数としては保管できないからです。形式的にグローバル変数として宣言すれば、コンパイルは通りますが、しかし関数の終了時にwindowsが勝手に画像データを消去してしまうので、このときに半透明処理の演算結果も処理されてしまいます。(後述の別の節でも説明しますが、つまりwindowsでは、何故か画像データをグローバル変数的に別の関数から呼び出すことが出来ないのです。)
 
winodwsには、こういった制約があるので、なので戦闘画面の背景マップの例の場合なら、どうすれば背景マップだけ黒色の半透明レイヤーで隠せるかというと、結果的にマップ画面側に黒色の半透明レイヤー処理を書かなければなりません。(でないと、レイヤーが無効になったりします。)
 
そして、半透明レイヤーで演算したあとに、最後に裏画面を本画面に転写すればいいのです。
 
 
さて、フラグ変数の切り替えのコードを置く位置については、戦闘モードのコードの最初にフラグ変数をオン(1)にするコードを書いて、マップ移動モードの最初にフラグ変数をオフ(0)にするコードを書けばいいのです。
 
フラグによる方式は、初心者には「回りくどい」と感じるかもしれませんが、しかし利点として、重複してレイヤーを重ねてしまうミスを防ぐという利点もあります。
 
「レイヤーの重複ミス」とは、たとえば半透明レイヤー1枚で明度を半分にしたい場合、もしミスでレイヤーを2回重ねてしまうと、半分ではなく4分の1の明度になってしまうような現象です。上述の隠されるマップ側にレイヤー処理のコードと、フラグ方式をあわせることで、レイヤー重複ミスを未然に防止できます。
 
なので、ぜひ、このフラグ管理によるレイヤー合成のテクニックを覚えましょう。
 
一方、フラグ管理の方式でないと、レイヤー重複ミスをしがちです。しました(実体験)。ボタンを押すたびに背景画面がドンドン暗くなるバグとか、フラグ管理でない方式だと、やりがちです。でした(実体験)。
 
 
=== Windowsによる画面消去 ===
半透明レイヤー合成に限らず、Windowsではプログラマーが画面クリアの命令を書いてなくても、OSがなんらかの理由で強制的にいったん画面クリアする場合があります。
 
典型的なのが、アプリをウィンドウ右上の最小化ボタンを押してタスクバーに格納すると、いったん画面が消去されてから、WM_PAINTを実行するという再描画が始まります。
 
 
モードの管理方式だと、この際、前のモードの描画はすべて消えてしまいます。
 
 
たとえば、(ドラクエ1みたいに)戦闘モード中にマップ画面を背景で表示しようと思って、マップモードの画像を残していても、しかしWindowsの場合、なんらかの理由でWindowsが強制的に画面クリアすることがあり、そのあとにはマップモード時に描画された画像は、このままでは消去されてしまい再描画されません。
 
 
こういったWindowsによる強制消去の対策としては、戦闘画面モード側にもマップ背景の描画をコード記述するしか、方法はありません。(他の方法があるかどうかは、一般には知られていません。)
 
 
結局、それぞれのモードで、それぞれ別個に、同じ背景画面の描画を指定することになります。
 
なお、関数などを使えば、背景などの描画はコードの使いまわしが出来ます。
 
 
たとえば、戦闘画面モードで、マップ画面描画の関数を呼び出せば、追加する必要のあるコードはその呼出し命令だけで済むという事です。
 
 
==== 方法1(非推奨) ====
バグが発生しやすいので非推奨な方法ですが、もし異なる関数どうしで、裏画面(ダブルバッファ)用の画像を受け渡しする場合、下記のような方法で画像の受け渡しが一応は出来ます。しかし、他の画像関連の関数の描画時に原因不明のバグ画像が発生する事が多く、あまりオススメできないです。
 
参考前に、下記の方法を紹介しておきます。
 
たとえば、戦闘画面の背景にマップ画面を書く場合、コード例として下記
<syntaxhighlight lang="C">
// マップ描画側の関数
void map2_Draw(HDC hdc,HDC hbackDC) {
// hbackDC上で 画像の作成のコードを個々に書いておく
 
// ここにマップ描画の関数を書くが、本画面(hdc)に転送 BitBlt しないでおく
// BitBlt(hdc, 0, 0, 700, 500, hbackDC, 0, 0, SRCCOPY); // なのでコレはコメントアウト
}
 
 
void battle_Draw(HDC hdc) {
filterFlag = 1; // 背景にフィルターをかぶせるのでフラグのセット
 
static HDC hbackDC = CreateCompatibleDC(hdc); // 裏画面用のハンドル
map2_Draw(hdc, hbackDC);
 
BitBlt(hdc, 0, 0, 700, 500, hbackDC, 0, 0, SRCCOPY);
// ここまでマップ画面の記述
// 後略
</syntaxhighlight>
 
 
 
のようにすれば、一応は、異なる関数どうしで裏画面のデバイスコンテキストの受け渡しが出来ます。
 
つまり、呼び出しの戦闘関数側で、引数をhdcだけでなく、裏画面用の hbackDC も引数として含めて、
map2_Draw(hdc, hbackDC);
のような2引数以上の関数にして呼び出す必要があります。
 
また、このため、呼び出されるマップ側にも、引数として
void map2_Draw(HDC hdc,HDC hbackDC) {
のように引数を増やす必要があります。
 
 
このため、既存のマップ描画関数では引数がhdcの一つだけなので、そのままでは使いまわし出来ないです。よって、戦闘画面の背景用に書き換える必要があるので、なので既存の map1_Draw(HDC hdc) をコピーペーストして新しく map2_Draw(HDC hdc,HDC hbackDC) 関数を作らなければなりません。
 
このため、この方式では、あまりコード行数は減らないし、むしろ行数が増えます。
 
 
しかも、せっかく作ったコードなのに、実際にテストプレイしてみると、街頭の戦闘シーンだけは画像は正常に表示されるのですが、上述のコード例の場合、なぜか戦闘後のマップ移動画面がバグりました。
 
このwikiを書いた著者の戦闘画面の描画プログラムが単に欠陥なのか、それともwindowsの仕様上の問題があるのかは当wiki著者には分かりません。
 
ですが、どちらにしろ、上記の方式では、たとえ異なる関数どうしの間で裏画面の受け渡しが出来たとしても、デバッグやメンテナンスがかなり難しくなると予想されるので、なるべくこの方式は避けるほうが安全でしょう。
 
 
ネットでさらっと検索して調べてみても、この問題の対処法は全然見当たりません。ネット上にいるゲームプログラマー志望の皆さん、どうもここまで実験してないようです。
 
なので、前の節で述べたように、マップ側で本画面に転写するなど、裏画面を作成した関数の側で、本画面にも転写してしまうのが、バグが発生しないで安全です。
 
 
=== windowsでは出来ない事 ===
上述のような問題が発生する背景として、windowsでは下記のような、機能不足の仕様があるという理由があります。
 
 
;グローバル変数では画像を保管できない
グローバル変数などで画像を保有する方法は、試みても、、うまくいかないです。(標準c言語の文法どおりの仕様には、Windows APIの仕様は従っていないのです。)
 
たとえば、あらかじめグローバル変数として(コード冒頭の部分の)グローバル領域で
HDC hbackDC;
など、裏画面用の画面変数を宣言しておいて、ためしにローカル関数サイドでグローバル変数のhbitmapやHDCなどに代入コピーしようとしてイコール記号「=」でプログラム中で代入コピーしても、(コンパイルはできるのですが、)なぜかモード変更時にはコピー内容を残せない仕様になっています。(おそらく、メモリのオーバーブローを防ぐための仕様として、Winodowsが自動で勝手に、代入命令の意味をメモリ内容の複製ではなく(コピーではなく)、参照リンクにしていると思われます。)
 
(なお、HDCは型名である。Windowsではデバイスコンテキストの型名がこう決まっている。 hbackDCは単なる変数名なので、自由に命名できるし、別の変数名でも構わない。)
 
どうやらWindowsAPIにおけるhbitmapやHDCといった型のグローバル変数宣言は、実態は単に、各ローカル関数ごとに宣言をする手間を減らすために、一括でグローバル領域で宣言できるという仕様でしかないようです。
 
もし、上述の説明とは別のうまい方法があったとしても、つまり複数のローカル関数どうしでhbitmapやHDCの内容を共有できる方法があったとしても、その方法はけっして、上述のような直感的な方法ではないので(上述のような直感的な(標準C言語のような)方法では、共有不可能です)、メンテナンス上の理由であまり用いないほうが安全でしょう。
 
このようなWindowsAPIの非直感的な仕様のため、どうしても背景を複数のモードで使いまわしたい場合にはWindowsの場合、やむを得ず、CPUに負担は掛かってしまうのですが、結局は前の節で説明したように、関数などを用いて各モードごとに同じ描画をする方法しか、IT業界ですら、この関数の方法しか世間的にはよく知られていません。
 
 
;static でも保存できない
静的変数 static としてグローバル変数として、
static HDC hbackDC;
とグローバル領域で宣言しても、同様の結果です(共有できない)。なお、これとは別件で、ローカル関数側で static HDC と宣言しても、何も表示されなくなりますので、ローカルで static 宣言しても役立ちません。
 
 
;ポインタ変数でも保存できない
画像の描画の際、グローバル変数でアドレス変数やポインタ変数を作成しても、通常変数の場合と同様に、異なるモード間では一行に画像を共有できないです。
 
たとえば、下記のようにポインタなどを使ったコードを書いてもコンパイルは出来るのですが、しかし画像がモード遷移時に消去され、複数のモード間では画像を共有できないです。
 
例えばグローバル変数としてグローバル領域で
 
HDC* point_dc;
と宣言してみて、
 
描画時に
point_dc = &hbackDC;
などと宣言をしてみて、
 
BitBlt(hdc, 0, 0, 600, 400, *point_dc, 0, 0, SRCCOPY);
 
としても、単に普通に通常変数で描画した場合と同様のことが起きるだけで(つまり、宣言したモード内だけで描画が出来るだけ)、異なるモード間では何も画像は共有されません。
 
また、
HBITMAP*
で試しても、同様の結果です。
 
== 2Dマップ ==
1,896 ⟶ 1,718行目:
 
とにかく、RPG作家は、道具屋の商品メニューなどでのコマンド追加方式はさけるのが安全です。
 
 
 
== 画像関係 ==
=== 半透明のレイヤー合成を使いたい場合 ===
==== 概要 ====
たとえばRPGでは、戦闘に入ったときなど、マップ画面の上に半透明の黒レイヤーでマップをやや隠して、その上に戦闘画面の画像を描画したいような場合もあるかと思います。
 
RPGツクール作品とかウディタ作品とかの戦闘シーンでも、そういう描画のゲームがよくあります。
 
もし、自前のプログラムでこういう半透明レイヤーを描画したい場合は、GDI+ のインクルードで出来ます。または、最初から同等の命令が組み込んである C# などを使えば出来ます。
 
Win32API で半透明レイヤーを使いたい場合の方法については、wikibooks では『[[Windows API/画像の操作]]』に説明があります。
 
Direct X を使わなくても、Windows 製のゲームで半透明レイヤーの画像合成をする事は可能です。
 
 
なお、実際にそのまま半透明レイヤーを乗せると、描画に少し時間(コンマ数秒ほど)が掛かります。
 
 
このため、もしも、キー入力するたびに背景ごと毎回描画してしまうと、画面がチラついてしまいます。
 
 
なので、対策として、たとえば、
:裏画面の描画を使うとか、
:または、(win32 API やGDI + だけのプログラミングをあきらめて) Direct X を使うなど、
いろいろと対策しましょう。
 
==== 具体的な解決策 ====
半透明レイヤーを重ねるコードを書く際、どうコードを書けばいいかというと、答えは直感に反しますが、答えは、レイヤーで隠される画像側のプログラムのほうにコードを書くことになるかと思います。(隠す側の画像のプログラムではないのです。)
 
たとえば、戦闘中に背景でマップ画面が半透明の黒レイヤーで隠されている場合、戦闘画面用コードではなく、マップ画面の描画コード側に、半透明処理を描くことになります。このため、フラグ管理をする事になり、たとえば「レイヤー変数 reiyaa が 1なら、このマップ画像に半透明レイヤーを載せる」のようなコードをマップ側に書く必要があるのです。
 
コレはなぜそうなるかというと、理由は、windowsでは何故かHDCで宣言した画像ハンドルをグローバル変数としては保管できないからです。形式的にグローバル変数として宣言すれば、コンパイルは通りますが、しかし関数の終了時にwindowsが勝手に画像データを消去してしまうので、このときに半透明処理の演算結果も処理されてしまいます。(後述の別の節でも説明しますが、つまりwindowsでは、何故か画像データをグローバル変数的に別の関数から呼び出すことが出来ないのです。)
 
winodwsには、こういった制約があるので、なので戦闘画面の背景マップの例の場合なら、どうすれば背景マップだけ黒色の半透明レイヤーで隠せるかというと、結果的にマップ画面側に黒色の半透明レイヤー処理を書かなければなりません。(でないと、レイヤーが無効になったりします。)
 
そして、半透明レイヤーで演算したあとに、最後に裏画面を本画面に転写すればいいのです。
 
 
さて、フラグ変数の切り替えのコードを置く位置については、戦闘モードのコードの最初にフラグ変数をオン(1)にするコードを書いて、マップ移動モードの最初にフラグ変数をオフ(0)にするコードを書けばいいのです。
 
フラグによる方式は、初心者には「回りくどい」と感じるかもしれませんが、しかし利点として、重複してレイヤーを重ねてしまうミスを防ぐという利点もあります。
 
「レイヤーの重複ミス」とは、たとえば半透明レイヤー1枚で明度を半分にしたい場合、もしミスでレイヤーを2回重ねてしまうと、半分ではなく4分の1の明度になってしまうような現象です。上述の隠されるマップ側にレイヤー処理のコードと、フラグ方式をあわせることで、レイヤー重複ミスを未然に防止できます。
 
なので、ぜひ、このフラグ管理によるレイヤー合成のテクニックを覚えましょう。
 
一方、フラグ管理の方式でないと、レイヤー重複ミスをしがちです。しました(実体験)。ボタンを押すたびに背景画面がドンドン暗くなるバグとか、フラグ管理でない方式だと、やりがちです。でした(実体験)。
 
 
=== Windowsによる画面消去 ===
半透明レイヤー合成に限らず、Windowsではプログラマーが画面クリアの命令を書いてなくても、OSがなんらかの理由で強制的にいったん画面クリアする場合があります。
 
典型的なのが、アプリをウィンドウ右上の最小化ボタンを押してタスクバーに格納すると、いったん画面が消去されてから、WM_PAINTを実行するという再描画が始まります。
 
 
モードの管理方式だと、この際、前のモードの描画はすべて消えてしまいます。
 
 
たとえば、(ドラクエ1みたいに)戦闘モード中にマップ画面を背景で表示しようと思って、マップモードの画像を残していても、しかしWindowsの場合、なんらかの理由でWindowsが強制的に画面クリアすることがあり、そのあとにはマップモード時に描画された画像は、このままでは消去されてしまい再描画されません。
 
 
こういったWindowsによる強制消去の対策としては、戦闘画面モード側にもマップ背景の描画をコード記述するしか、方法はありません。(他の方法があるかどうかは、一般には知られていません。)
 
 
結局、それぞれのモードで、それぞれ別個に、同じ背景画面の描画を指定することになります。
 
なお、関数などを使えば、背景などの描画はコードの使いまわしが出来ます。
 
 
たとえば、戦闘画面モードで、マップ画面描画の関数を呼び出せば、追加する必要のあるコードはその呼出し命令だけで済むという事です。
 
 
==== 方法1(非推奨) ====
バグが発生しやすいので非推奨な方法ですが、もし異なる関数どうしで、裏画面(ダブルバッファ)用の画像を受け渡しする場合、下記のような方法で画像の受け渡しが一応は出来ます。しかし、他の画像関連の関数の描画時に原因不明のバグ画像が発生する事が多く、あまりオススメできないです。
 
参考前に、下記の方法を紹介しておきます。
 
たとえば、戦闘画面の背景にマップ画面を書く場合、コード例として下記
<syntaxhighlight lang="C">
// マップ描画側の関数
void map2_Draw(HDC hdc,HDC hbackDC) {
// hbackDC上で 画像の作成のコードを個々に書いておく
 
// ここにマップ描画の関数を書くが、本画面(hdc)に転送 BitBlt しないでおく
// BitBlt(hdc, 0, 0, 700, 500, hbackDC, 0, 0, SRCCOPY); // なのでコレはコメントアウト
}
 
 
void battle_Draw(HDC hdc) {
filterFlag = 1; // 背景にフィルターをかぶせるのでフラグのセット
 
static HDC hbackDC = CreateCompatibleDC(hdc); // 裏画面用のハンドル
map2_Draw(hdc, hbackDC);
 
BitBlt(hdc, 0, 0, 700, 500, hbackDC, 0, 0, SRCCOPY);
// ここまでマップ画面の記述
// 後略
</syntaxhighlight>
 
 
 
のようにすれば、一応は、異なる関数どうしで裏画面のデバイスコンテキストの受け渡しが出来ます。
 
つまり、呼び出しの戦闘関数側で、引数をhdcだけでなく、裏画面用の hbackDC も引数として含めて、
map2_Draw(hdc, hbackDC);
のような2引数以上の関数にして呼び出す必要があります。
 
また、このため、呼び出されるマップ側にも、引数として
void map2_Draw(HDC hdc,HDC hbackDC) {
のように引数を増やす必要があります。
 
 
このため、既存のマップ描画関数では引数がhdcの一つだけなので、そのままでは使いまわし出来ないです。よって、戦闘画面の背景用に書き換える必要があるので、なので既存の map1_Draw(HDC hdc) をコピーペーストして新しく map2_Draw(HDC hdc,HDC hbackDC) 関数を作らなければなりません。
 
このため、この方式では、あまりコード行数は減らないし、むしろ行数が増えます。
 
 
しかも、せっかく作ったコードなのに、実際にテストプレイしてみると、街頭の戦闘シーンだけは画像は正常に表示されるのですが、上述のコード例の場合、なぜか戦闘後のマップ移動画面がバグりました。
 
このwikiを書いた著者の戦闘画面の描画プログラムが単に欠陥なのか、それともwindowsの仕様上の問題があるのかは当wiki著者には分かりません。
 
ですが、どちらにしろ、上記の方式では、たとえ異なる関数どうしの間で裏画面の受け渡しが出来たとしても、デバッグやメンテナンスがかなり難しくなると予想されるので、なるべくこの方式は避けるほうが安全でしょう。
 
 
ネットでさらっと検索して調べてみても、この問題の対処法は全然見当たりません。ネット上にいるゲームプログラマー志望の皆さん、どうもここまで実験してないようです。
 
なので、前の節で述べたように、マップ側で本画面に転写するなど、裏画面を作成した関数の側で、本画面にも転写してしまうのが、バグが発生しないで安全です。
 
 
=== windowsでは出来ない事 ===
上述のような問題が発生する背景として、windowsでは下記のような、機能不足の仕様があるという理由があります。
 
 
;グローバル変数では画像を保管できない
グローバル変数などで画像を保有する方法は、試みても、、うまくいかないです。(標準c言語の文法どおりの仕様には、Windows APIの仕様は従っていないのです。)
 
たとえば、あらかじめグローバル変数として(コード冒頭の部分の)グローバル領域で
HDC hbackDC;
など、裏画面用の画面変数を宣言しておいて、ためしにローカル関数サイドでグローバル変数のhbitmapやHDCなどに代入コピーしようとしてイコール記号「=」でプログラム中で代入コピーしても、(コンパイルはできるのですが、)なぜかモード変更時にはコピー内容を残せない仕様になっています。(おそらく、メモリのオーバーブローを防ぐための仕様として、Winodowsが自動で勝手に、代入命令の意味をメモリ内容の複製ではなく(コピーではなく)、参照リンクにしていると思われます。)
 
(なお、HDCは型名である。Windowsではデバイスコンテキストの型名がこう決まっている。 hbackDCは単なる変数名なので、自由に命名できるし、別の変数名でも構わない。)
 
どうやらWindowsAPIにおけるhbitmapやHDCといった型のグローバル変数宣言は、実態は単に、各ローカル関数ごとに宣言をする手間を減らすために、一括でグローバル領域で宣言できるという仕様でしかないようです。
 
もし、上述の説明とは別のうまい方法があったとしても、つまり複数のローカル関数どうしでhbitmapやHDCの内容を共有できる方法があったとしても、その方法はけっして、上述のような直感的な方法ではないので(上述のような直感的な(標準C言語のような)方法では、共有不可能です)、メンテナンス上の理由であまり用いないほうが安全でしょう。
 
このようなWindowsAPIの非直感的な仕様のため、どうしても背景を複数のモードで使いまわしたい場合にはWindowsの場合、やむを得ず、CPUに負担は掛かってしまうのですが、結局は前の節で説明したように、関数などを用いて各モードごとに同じ描画をする方法しか、IT業界ですら、この関数の方法しか世間的にはよく知られていません。
 
 
;static でも保存できない
静的変数 static としてグローバル変数として、
static HDC hbackDC;
とグローバル領域で宣言しても、同様の結果です(共有できない)。なお、これとは別件で、ローカル関数側で static HDC と宣言しても、何も表示されなくなりますので、ローカルで static 宣言しても役立ちません。
 
 
;ポインタ変数でも保存できない
画像の描画の際、グローバル変数でアドレス変数やポインタ変数を作成しても、通常変数の場合と同様に、異なるモード間では一行に画像を共有できないです。
 
たとえば、下記のようにポインタなどを使ったコードを書いてもコンパイルは出来るのですが、しかし画像がモード遷移時に消去され、複数のモード間では画像を共有できないです。
 
例えばグローバル変数としてグローバル領域で
 
HDC* point_dc;
と宣言してみて、
 
描画時に
point_dc = &hbackDC;
などと宣言をしてみて、
 
BitBlt(hdc, 0, 0, 600, 400, *point_dc, 0, 0, SRCCOPY);
 
としても、単に普通に通常変数で描画した場合と同様のことが起きるだけで(つまり、宣言したモード内だけで描画が出来るだけ)、異なるモード間では何も画像は共有されません。
 
また、
HBITMAP*
で試しても、同様の結果です。
 
 
== (※: 未確認)戦闘中のモンスター隊列 ==