Demo 5

すでに Demo5 を実行してみたであろうか。今回からは、残念ながらハネカニモドキは登場しない、新しいシーンとなる。無理矢理ストーリーを捻出できないことも無いが、意味が無いので止めておく。あの続きは自分で作ってみるのも面白い。

さて、今回も新しいコンポーネントが1つ追加されている。DGCStarField がそれだ。このコンポーネントは、星の流れる背景を簡単に描画させることが出来る。DGC のコンポーネントは数が多いと感じるかもしれないが、このうちのいくつかは、あくまでプログラムの手間を省くために用意されてる。もし覚えきれないのであれば、それらのコンポーネントは使わなくてもプログラムは出来る。しかし一度使えば、そのコンポーネントを使うためにゲームを作りたくなるぐらい良く出来ているのである。

このデモプログラムではもう一つ、新しい取り組みが行われている。それは、新しい「スーパースターシップ(仮称)」に移動以外の手段が与えられていることだ。使うキーはなぜか [ctrl] だが、もちろんこれは自由に変更できる。

procedure TForm1.DGCScreen1Initialize(Sender: TObject);
var
   n: Integer;
begin
     //Add the enemy animation
     DGCSpriteMgr1.Animations.Add('8WayShip', True);
     with DGCSpriteMgr1.Animations[0] do
     begin
          SetFrames(0, 100, [14]);
          SetFrames(4, 100, [15]);
          SetFrames(8, 100, [8]);
          SetFrames(12, 100, [9]);
          SetFrames(16, 100, [10]);
          SetFrames(20, 100, [11]);
          SetFrames(24, 100, [12]);
          SetFrames(28, 100, [13]);
     end;

前回のデモプログラムでは、アニメーションとイメージを関連付けるのに、SetAllDirections を使用したのを思い出して欲しい。DGCSpriteMgr はスプライトの向きを最大 32 方向まで扱えるが、任意の向きに対してアニメーションにイメージを関連付けるのに使うのが SetFrames メソッドである。SetFrames のパラメータは順に、スプライトの向き(0-32)、アニメーションの速度、イメージの番号となる。スプライトの向きは、上向きを 0 として、1周を 32 方向とし、時計周りに 31 までの値を取る。

  //Initialise Starfield
  DGCStarfield1.Generate;

では新しいコンポーネントの DGCStarfield の説明をしよう。・・・とその前に、ここでとても重要なことを書き忘れていたことに気がついた。DGCSpriteMgr や DGCStarfield などのコンポーネントは、プロパティとして DGCScreen を持っている。ここではすでに DGCScreen がフォームに乗っていれば、オブジェクトインスペクタでドロップダウンして選ぶだけで良い。簡単ではあるが、これを忘れたら大変な目に会うので注意して欲しい。

Generate メソッドは、DGCStarfield の初期化を行う。このコンポーネントは最初にこれを呼び出せば、次からは Update メソッドを呼び出すだけになる。もちろん、途中でプロパティを変更することは出来るが、このプロパティがちょっと分かり難い。この DGCStarfield については Demo10 で取り上げているので、今回はデフォルトのまま使うことにする(今回のデモプログラムでは、Colored プロパティのみが変更されている)。くれぐれも、DGCScreen を設定し忘れないように。

procedure TForm1.DGCScreen1Flip(Sender: TObject);
var
   n: Integer;
begin
     DGCStarfield1.Update;     //Draw the starfield
     DGCSpriteMgr1.Draw;       //Draw the sprites
     DGCSpriteMgr1.Update;     //Update sprite positions for next time

     //Allow player to fire at enemy
     if DGCScreen1.KeyDown(VK_CONTROL) and not DGCSpriteMgr1.Sprites[1].Enabled then
     begin
          with DGCSpriteMgr1.Sprites[1] do
          begin
               X := DGCSpriteMgr1.Sprites[0].X + 15;
               Y := DGCSpriteMgr1.Sprites[0].Y;
               Enable;
               Show;
          end;
     end;

このイベントは FlippingEnabled が True の時に自動的に呼び出される。今までは初期設定が終わってからプログラム中でこのプロパティを変更していたが、今回ははじめから True に設定されている。注意していないと見落とすが、これはそれほど致命的なミスではない。ここでは、DGCStarfield.Update 、DGCSpriteMgr.Draw 、DGCSpriteMgr.Update の順に呼ばれている。背景の描画を先に行っていることに注意。

さて、地球防衛群の威信をかけて作り出されたスーパースターシップ・・・といったストーリーはともかく、やっと攻撃の手段を手に入れたマイキャラ。KeyDown メソッドを用いてコントロールキーを調べている。押されていれば、弾が発射される。if 文の意味は、 「[ctrl] が押されており、Sprites[1] が Enabled でなければ」となる。座標が +15 となっているのは、マイキャラとのスプライトサイズの違いのせいである。

     //Check for Collisions with the bullet and enemy
     //only if bullet enabled;
     if DGCSpriteMgr1.Sprites[1].Enabled then
     begin
          for n := 2 to Pred(DGCSpriteMgr1.Sprites.Count) do
          begin
               if DGCSpriteMgr1.Sprites.Collision(1, n) then
               begin
                    with DGCSpriteMgr1.Sprites[n] do
                    begin
                         Animation := 2; //Spinning Enemy Animation
                         Direction := Random(32);
                         AllActions := laStopInside;
                         MaxSpeed := 5; //Increase Max Speed
                    end;
                    DGCSpriteMgr1.Sprites[1].Disable;
                    DGCSpriteMgr1.Sprites[1].Hide;
                    break;
               end;
          end;
     end;

ここでは、敵キャラと弾の衝突判定をしている。弾が当たると、敵はくるくる回りながらランダムな方向で画面の端まで行く。どんなに上手に弾を撃っても、一度に2つの敵は倒せなくなっている。( break があるから)

procedure TForm1.DGCSpriteMgr1SpriteStopped(Sprite: TDGCSprite;
  LimitsSide: TLimitsSide);
begin
     //Note: The sprite engine will stop the sprite by setting it's
     //Stopped property to True. If you want the sprite to continue
     //moving you will need to set Stopped to False or use the Resume
     //method.
     case Sprite.ID of
          1: //Bullet
          begin
               Sprite.Disable;
               Sprite.Hide;
               Sprite.Resume;
          end;
          2: //Spinning Enemy - Change Animation to explosion
          begin
               if Sprite.Animation <> 4 then
               begin
                    Sprite.Animation := 4;
                    Sprite.Direction := 16; //South
                    Sprite.Resume;
               end;
          end;
     end;
end;

見慣れないイベントが登場した。OnSpriteStopped イベントは、AllActions(または各 *Action プロパティ)が、laStopInside か、laStopOutside のとき、領域の端までいくと発生する。この時、Sprite.Stopped プロパティが True にセットされるが、スプライトのタイプが AddBouncer の時は、これを False にしてやるまであらためて動き出すことが出来なくなる。弾のスプライトもこれに当たるが、弾が一発しか出ないのではすべての敵を倒すことは出来ない。そこで、 Resume メソッドを呼び出して Stopped を False にしてやる必要がある。

また、敵キャラは弾に当たると回転して画面の端に向かい、そこで爆発してしまう。爆発パターンは下に向かって流れるのだが、画面の端で止まった時にやはり Stopped が True になってしまうので、ここで Resume しておく。爆発のアニメーションパターンは 4 である。そして爆発した敵キャラが再び画面の端(この場合は下端)にたどり着いたとしても、次は Resume する必要はない。そのスプライトはその後消えてしまい、もう2度と登場しないからである。

procedure TForm1.DGCSpriteMgr1AnimationEnd(Sprite: TDGCSprite);
begin
     Sprite.Hide;
     Sprite.Disable;
end;

爆発した敵キャラのスプライトはどうやって消すのか? それは前回同様、AnimationEnd イベントで処理されている。爆発パターンのアニメーションのみ、Animations.Add する時に LoopAnimation flag が False となっていることを確認して欲しい。

これで Demo5 の説明は終わりとなる。このデモプログラムに少し手を加えるだけで、ゲームとして遊べるものが出来上がるだろう。

もどる