2011年2月9日水曜日

進捗情報(ホーミングレーザー等)

2Dシューティングに機能追加しました。
ホーミングレーザーと自機爆発エフェクトの実験中
  • 敵のホーミングレーザーの表示を追加しました。
  • 仮に自機が爆発した際のエフェクトを実験中。
ホーミングレーザー
ホーミングレーザーは色々なホームページをみたのですが、板ポリゴン?を繋げる方法が一般的らしいということだそうです。この方法はポリゴンゲームがでた当時から変わってなさそうな感じなのですね。
実装方法は、カスタムエフェクトを作成して、2D座標で指定したレーザーを表示するようにしました。注意した点としては、今は128本のレーザーを表示できるようにしているのですが、レーザー1本ごとにDrawIndexedPrimitives()を呼ぶのは速度的にダメダメなはずなので、SpriteBatchの挙動のように、Update()時に頂点情報を貯めこんで、全部のレーザーを1度のDrawIndexedPrimitives()で行えるようにしました。後、レーザーの色の推移に関しては、Color.Lerp()というのが、補完色を簡単に作成できてべんりでした。
板ポリゴンは3D座標に表示するのが普通なので、2D座標にポリゴンを表示するのは、どうするのがいいのかなと考えました。結果としては、シェーダー側でポリゴン位置を2D位置から3D位置に表示してもらえると、管理が簡単なのでそうしました。
2D座標にポリゴンを表示したい方もいらっしゃるかもしれないので、ポイントだけメモしておきます。以下のマトリクス mat2DtoViewport を、シェーダープログラム側のグローバル変数に設定して、シェーダーのPOSITION変数に、mul(mul(mul(pos, world), view), proj)の代わりに、mul(pos, mat2DtoViewport)みたいに変更することで、2D座標で指定した位置にポリゴンの点を打つことができるかと思います。

   1:  Viewport vp = GraphicsDevice.Viewport;
   2:  Matrix mat2DtoViewport
   3:    = Matrix.CreateTranslation(new Vector3(-0.5f, -0.5f, 0.0f))
   4:    * Matrix.CreateTranslation(
                      new Vector3(-vp.Width / 2, -vp.Height / 2, 0))
   5:    * Matrix.CreateScale(
                      new Vector3((float)1 / (vp.Width / 2), 
                                  (float)1 / -(vp.Height / 2), 1));


一応、お役に立つこともあるかもしれませんので、2D座標で指定した位置にポリゴンを表示したいという場合に必要となった知識もメモしておきたいと思います。
  • 頂点シェーダーが最終的に欲しがるPOSITION(位置)は、ビューポート変換用の座標だそうです。
  • ビューポート変換用座標とは、左上が(-1.0,+1.0)、右下が(+1.0,-1.0)の座標になります。なお、画面の中央は(+0,0,+0,0)になるようです。
  • Z座標は(0.0~1.0)の範囲で。2D座標のZに指定した値を、そのまま頂点シェーダーに渡せば、ポリゴンの表示優先順位になってくれます。(Zバッファを有効にした場合に限りです。)
仮に2D画面の大きさを(0,0)-(1024,720)とします。この場合、2D座標の値(0,0,0)を入力したら、頂点シェーダーのPOSITION出力には(-1, +1, 0)を、2D座標の(1280, 720, 0)を入力したら、頂点シェーダーのPOSITION出力を(+1, -1, 0)を、2D座標の(640, 360, 0)を入力したら、頂点シェーダーのPOSITION出力を(0, 0, 0)に変換すれば良いことになります。今回調べたおかげで頂点シェーダーの動きがよく理解できました。あと、最初の謎のマトリクス変換 Matrix.CreateTranslation(new Vector3(-0.5f, -0.5f, 0.0f)) なのですが、これに関してはピクセルとボクセルのずれを補正しました。この0.5ピクセルの影響で、実際には上の説明のようには行かず、2D座標(0,0,0)を入力したら(-1,+1,0)ではなくちょっとずれた値になることにご注意ください。
余談 じつは、これを調べた後、ひにけに先生のブログをよく見てみると、関連することが書かれてました。ひにけに先生の説明を理解?した結果、上記と同じプログラムは以下のように書き直せるらしきことが分かりました。

   1:  Viewport vp = GraphicsDevice.Viewport;
   2:  Rectangle rcScreen = new Rectangle(0, 0, vp.Width, vp.Height);
   3:  Matrix mat2D
   4:    = Matrix.CreateTranslation(new Vector3(-0.5f, -0.5f, 0))
   5:    * Matrix.CreateOrthographicOffCenter(
   6:      rcScreen.Left, rcScreen.Right, 
        rcScreen.Bottom, rcScreen.Top, 0, 1);


Matrix.CreateOrthographicOffCenter()と関数が、上側のCreateTranslation()とCreateScale()を組み合わせたものと同じ意味になるのだと思います。CreateOrthographicOffCenter()の説明は、「カスタマイズした正射影行列を作成します。」だそうで、2D座標と正射影行列の関係に今頃気づきました。
さらに余談。Matrix.CreateOrthographicOffCenter()を使ったのですが、座標変換後のZ値に関してあれ?と思う挙動がありました。CreateOrthographicOffCenter()で生成したマトリクスに2D座標(1280,720,1)を渡すと、(+1.0, -1.0, –1)とZ値にマイナス1(-1)が帰ってきます。ちなみに関数の説明では0~+1が返ると書かれてます。ひにけに先生達が作った関数がバグっているともなかなか考えにくいように思うのですが…どうなんでしょう。

自機爆発エフェクト作成中 自機が爆発する際のエフェクトを検討中です。敵が破壊されるときみたいに円形のエフェクトをだしつつ、画面全体をラジアルブラー?というのを入れようと思っています。現状は、自機を中心に円形にブラーしてるのですが、爆発して円形に拡大した後、円形に縮小して戻るはなんか見た目すごくおかしいので、拡大しつつ拡大中のブラーが次第に薄く消えていくようにすれば見た目納得いくような仕上がりになるのではないかなと妄想してます。
昔作ったゲームでは、ブラーするのにSpriteBatchで重ねてたのですが、画面全体にスプライトを数回書くのはやはり恐ろしく重かったです。今回はシェーダーもある程度分かってきたので、シェーダーを使って高速に処理できそうです。(ただ、前回も書きましたが、2Dゲームでシェーダー使うと、古いコンピュータの方からは2Dのくせに重くて遊べないゲームだとか文句がでそうで怖い気もします。)

P.S. そろそろ3D(2.5D)シューティングも進めたいですが、なかなか時間が取れません。2D側を先に終わらせようか悩み中です。

0 件のコメント:

コメントを投稿