Demo 4

それでは Demo4 の説明に入りたい。このデモプログラムには、新しく3つものコンポーネントが追加されている。DGCSpriteMgr と DGCAudio と DGCSoundLib である。Demo3 プログラムとの大きな違いは後の2つで、これらは DirectSound を利用するために使うコンポーネントである。つまり、「ゲームはやっぱり音が出なきゃ寂しい」のである。

このデモプログラムも、いつものように OnInitialize から始まっている。今回はかなりソースが大きくなっているが、追加されただけではないことに気がついただろうか? Form の private にあったはずの変数も無くなっている。これはなぜ?

犯人は DGCSpriteMgr である。これはお手軽にスプライトを使用するためのコンポーネントで、今まで自分でコードを書いていた部分を自動でやってくれる。お手軽なのはいいのだが、今回のバージョン(β6)ではこのコンポーネントおよび TDGCSprite の説明がほとんどなされていない。それだけお手軽だからともいえるが・・・・

procedure TForm1.DGCScreen1Initialize(Sender: TObject);

        :
        :

  //Create the Animations. Note: Here we are just defining animations
  //not the sprites. The Animation name is optional (the first paramter).
  //The second paramter is LoopAnimation flag.
  DGCSpriteMgr1.Animations.Add('Invader', True);

  //We have added an animation (Index 0) so set the images for the animations.
  //If we want to use the same images for all 32 directions we use the
  //SetAllDirections method. 100=Speed, [1, 2]=Image Numbers
  DGCSpriteMgr1.Animations[0].SetAllDirections(100, [1, 2]);
ここには2つの命令があるが、まず最初の命令から説明しよう。と言っても上に英語で書いてある。Animations.Add は、アニメーションを定義する。アニメーションの名前が最初のパラメータで、2番目はループさせるかどうかのフラグである。False だとアニメーションが終わったときに OnAnimationEnd イベントが発生する。この命令でアニメーションを定義した時点では、アニメーションするスプライトのイメージは定義されていない。

次は Animations.SetAllDirections だ。これはアニメーションに、イメージを関連付けるメソッドだが、もしもスプライトが動く方向によってイメージを変更する場合は、別のメソッドを使用することになる。その説明は、また別のデモプログラムで出来ると思うが、今はこのメソッドしか知らなくても良い。Animations[0] は、先ほど名前を決めた「Invader」に対応している。メソッドの最初のパラメータはアニメーションするスピードで、おそらく単位は ms 。次のパラメータはイメージの番号が並んだ配列で、これは DGCScreen1 の ImageLibrary の中のイメージの番号であり、すなわち DGCImageLib1 の中のイメージの番号である。

  //Define the animations for the player controlled ship and explosion. Again
  //we are using the same images for all 32 directions so we use the
  //SetAllDirections method.
  DGCSpriteMgr1.Animations.Add('Ship', true);
  DGCSpriteMgr1.Animations[1].SetAllDirections(100, [0]);

  //For the explosion event we say not to loop because and event is fired
  //when the animation has reached the last frame. This will be our cue to
  //remove the player controlled ship as it has just been blown up.
  DGCSpriteMgr1.Animations.Add('Explode', False);
  DGCSpriteMgr1.Animations[2].SetAllDirections(100, [3, 4, 5, 6]);
同様にして、スターシップとその爆発パターンのアニメーションを定義している。これでアニメーションの定義付けが済んだ。

  //Now we have defined the animations is is time to create the sprites.
  //Start with the player controlled ship. Note the Allow???? properties
  //are used so the sprite can only move left and right
  //Params=ID, X, Y, Direction (0=North), Speed, Animation# (Defined above)
  DGCSpriteMgr1.Sprites.AddPlayer8(0, 320, 440, 0, 3, 1);
  with DGCSpriteMgr1.Sprites[0] as TDGCPlayer8 do
  begin
    AllowUp := False;        //Not now allow up
    AllowDown := False;      //Do not allow down
    Acceleration := 0.1;     //Do not allow the ship to reach maximum
    Decceleration := 0.1;    //speed and stop instantly.
    //Each sprite can moved within a defined rectangle held in property
    //'Limits'. When the sprite reaches the edge of the rectangle an action
    //can occur. For example, when the sprites reaches the side of the
    //limits rectangle you can make it bounce off the side or just stop.
    //An action can be set for each side of the Limits rectangle.
    //If you want the same action for all four sides of the Limits
    //rectangle use the AllActions property. The Limits rectangle will default
    //to the entire screen. To set an action for just one side use the
    //LeftAction/TopAction,RightAction/BottomAction properties.
    //Note: Some of the Limits Actions values are ignored for player
    //controlled sprites. Try changing the AllActions property for
    //the bouncer sprite created next.
    AllActions := laWrap; //laWrap        = Wrap from one side to the other
                          //laBounce      = Bounce off the side
                          //laReverse     = Reverse direction
                          //laStopInside  = Stop inside the rectangle
                          //laStopOutside = Stop outside the rectangle
                          //laEvent       = Fire the OnSpriteEvent

  end;
ここからがスプライトの定義となる。まずはプレイヤーが操作するスターシップのスプライトの追加だが、AddPlayer8 メソッドが使われている。これはキー操作で8方向に動かせるキャラクターであることを意味している。Sprite の Add メソッドは、スプライトのタイプ別に他にもあるが、スプライトの動きを自動で制御してくれるのはありがたい。

その他の Add メソッド
  AddBouncer    
  AddRaceCar    
  AddThruster   
  AddStatic     
  AddJumper     
それぞれのパラメータは、スプライトの ID 、スプライトの初期座標、スプライトの向き、スプライトの動く速度(同時に最大速度も決まる)、スプライトが使うアニメーションの ID (先ほど定義したもの)である。今回は全方向とも同じイメージを使うようにアニメーションを指定しているので、スプライトの向きはいくつでも関係ない。また、先ほど定義したスターシップのアニメーションは Animations[1] だったので、最後は1となる。

  with DGCSpriteMgr1.Sprites[0] as TDGCPlayer8 do
ここは重要なのだ。DGCSpriteMgr はスプライトの管理をしているが、そのスプライトを参照する場合は、この様に正しいスプライトのタイプにキャストしてやる必要が出てくるので、注意が必要である。ちなみに、あるスプライトが任意のタイプであるかどうかを調べるには、次のようにすれば良い。

  if DGCSpriteMgr1.Sprites[0] is TDGCPlayer8 do
では、Sprite のプロパティを見てみよう。まず AllowUp は、上に動けるかどうかを決める。True ならカーソルの上を押した時に上に動く。AllowDown は同様に、下に動けるかどうかである。ここでは定義していないが、AllowLeft、AllowRight ももちろんあり、全てディフォルトは True 。Acceleration はキーを押した方向への加速度。最大速度はすでに決定しているが、それよりも小さくすることによって移動の加速度を決めることが出来る。Decceleration は減速度で、キーを押していない時の減速具合を指定する。

最後に AllActions である。まとめて意訳してみると、
  laWrap        = 領域の端までいくと、反対側にワープする。
  laBounce      = 領域の端までいくと、スプリングがあり、跳ね返される。
  laReverse     = 領域の端までいくと、その方向の速度が反転し、跳ね返る。
  laStopInside  = 領域の内側までいくと止まる。
  laStopOutside = 領域の外側までいくと止まる。(要注意)
  laEvent       = 領域を出たところで OnSprite イベントが発生する。
laBounce については、説明しにくいので実際に試してみると良い。なお、ここで領域とは、Sprites の Limits プロパティであり、デフォルトでは全画面が選択されている。また、AllActions プロパティの代わりに LeftAction/TopAction/RightAction/BottomAction を使うことによって、それぞれの領域での挙動を指定できる。

  //Create the Invader sprite (TDGCBouncer)
  DGCSpriteMgr1.Sprites.AddBouncer(0, 90, 300, 14, 5, 0);
  with DGCSpriteMgr1.Sprites[1] as TDGCBouncer do
  begin
    Limits := Rect(0, 200, 640, 468);
    Acceleration := 0.01;   //Invader will increase speed slowly
    AllActions := laBounce; //try changing this to laWrap, laReverse,
                            //laStopinside or laStopOutside.
  end;
ここまで説明してきたことが解っていれば、すべて理解できるはずである。重要なのは、Limits プロパティの定義を行っているところぐらいである。

  //Set the clipping region to the area we are drawing the sprite in
  DGCScreen1.ClipRect := Rect(0, 200, 640, 468);

  TraceString('Initialise');

  //Now Start the page flipping
  DGCScreen1.FlippingEnabled := True;
ここでまた新しいプロパティが現れた。ClipRect プロパティは DGCScreen のクリッピングリージョンを指定するプロパティだ。これは、DGCScreen の Draw 、Scroll、Tile のメソッドを使用する時に参照され、この範囲以内でしか描画を行わない。画面の一部分がスクロールするようなゲームでとても有効な機能で、使い道は他にもいろいろある。しかし、今回のデモプログラムではあまり効果が無い。

  TraceString('Initialise');
本題のプログラムの動作とは直接は関係ないが、この部分で何をやっているか簡単に説明してみる。詳しく知りたい人はヘルプの TraceWin のところにヒントがあるので見ていただきたい。TraceString はデバッグ用の機能で、事前に DGC をインストールしたディレクトリの下の Tools ディレクトリにあるプロジェクトをコンパイルし、実行しておく必要がある。この状態で Demo4 を実行し、終了すると、デバッグウインドウに Initialise の文字が表示されているはずである。このように、DirectDraw を全画面で使用した際などに、プログラムの任意の場所での状態を調べるのに使うことができるのである。このデバッグウインドウは他にも使い方があると思うが、まだ試していない。また TraceString の実体は、同じく DGC をインストールしたディレクトリの下の Bin の中にある、Trace.pas にある。ただ、この命令が無くても問題なく動くのと、これ以外のデモプログラムの中では使われていないので、これ以上の説明はここではしない事にする。

OnInitialize イベントは、この後 FlippingEnabled を True に設定したところで終わっている。すべての初期設定が終わったこの場所が、Flipping を始めるのに最適な位置である。

procedure TForm1.DGCScreen1Flip(Sender: TObject);
var
   ShipHit: Boolean;
begin
     //Erase the area theat the sprites will move in
     //This is also our screen clipping region
     DGCScreen1.Back.EraseRect(Rect(0, 200, 640, 468), 0);

     //Draw the sprites
     DGCSpriteMgr1.Draw;

     //Update the sprites
     DGCSpriteMgr1.Update;
ではその Flipping の中身である。DGCSpriteMgr1.Draw は、すべてのスプライトを画面に描画する。ここでは EraseRect の直後に書かれているが、スプライトの背景がある場合は先に背景を描いておかないといけない。DGCSpriteMgr1.Update は、スプライトの状態を更新するメソッドで、速度、加速度などにしたがって座標などのプロパティを更新している。

  //Is there a collision between sprite 0 and 1 (the invader and player ship)?
  if (DGCSpriteMgr1.Sprites.Collision(0, 1)) and
     (DGCSpriteMgr1.Sprites[0].Animation <> 2) then
  begin
    //There is a collision so make the player sprite explode
    with DGCSpriteMgr1.Sprites[0] as TDGCPlayer8 do
    begin
      //A Player8 sprite has two types of animations
      //one for when it's moving when the keys are
      //help down and one when it's idle.
      ActiveAnimation := 2;
      IdleAnimation := 2;
      Automatic := False; //Disable automatic key checks
    end;

    //Make an explosion noise. Replay is the same as
    //using the Stop, Position := 0, and Play commands.
    DGCAudio1.Sound[0].Replay;

    //As well as making the player explode lets send the invader
    //wizzing off in a random direction. The AllActions property
    //is set to laStopOutside which means the invader will stop
    //when is completely leavs the LimitsRect.
    with DGCSpriteMgr1.Sprites[1] as TDGCBouncer do
    begin
      Randomize;
      Direction := Random(32);
      MaxSpeed := 5;               //Set the maximum speed
      Speed := 5;                  //Set the current speed
      AllActions := laStopOutSide; 
    end;
  end;
また新しいメソッドの登場である。Sprites.Collision は2つのスプライトが衝突しているかどうかを調べる。内部では CollisionTest が呼び出されており、最後の引数は True 、つまり常に透過部分どうしの衝突は無視される。また、Sprite には Enabled プロパティが存在する。このプロパティが False の場合は画面には表示されず、Collision も発生しない。if 文の意味は、「ハネカニモドキとスターシップが衝突しており、なおかつスターシップが爆発中でなければ」となる。

Player8 タイプのスプライトは、アニメーションパターンを2つ持っている。最初に AddPlayer8 で追加された時点では、二つとも同じ値が代入されているが、もしもアニメーションパターンを変更したくなった時に注意が必要である。ActiveAnimation はキーが押されている状態のパターンで、IdleAnimation はそうでない時のパターンとなる。

Automatic プロパティは、Player8 タイプのスプライトが行っているキー入力の判定を行うかどうかの設定をする。このデモプログラムでは、衝突した時点でキー入力が出来なくなるようにしてある。

  //Make an explosion noise. Replay is the same as
  //using the Stop, Position := 0, and Play commands.
  DGCAudio1.Sound[0].Replay;
やっと今回の目玉の登場である。DGCSpriteMgr はプログラムを作る側にとって非常に便利なコンポーネントであるが、遊ぶ側は意識していない。それに対して DGCAudio は、遊ぶ側にこそ重要な機能を提供する。それはもちろん、「音」である。DGCAudio は、DGCSoundLib と対にして使うことになる。これは Direct Sound を使用するためであり、二つの関係はちょうど DGCScreen と DGCImageLib の関係に似ている。また、DGCSoundLib は専用のサウンドライブラリを読み込むことと、そのサウンドライブラリを作るのにサウンドライブラリエディタが提供されているところも良く似ている。

サウンドライブラリエディタの使い方もきわめて簡単である。Tools ディレクトリにある Sndlib.exe を実行し、真ん中にある「add」ボタンを押して wav ファイルを追加する。いくつか追加し終わったら、セーブする。これだけだ。これらのツール一つを見ても、DGC の素晴らしさが伺えるというものだ。

実際に使用する際には少し注意が必要になる。Direct Sound の性質上、同時に8音まで鳴らすことが出来るが、同じ wav を2つ同時に鳴らしたければ、2つライブラリに登録しておく必要がある。また、DGCAudio の SoundLibrary に DGCSoundLib を設定しておくことを忘れてはいけない。これらのコンポーネントは、通常の Delphi アプリケーションでも使用することが出来る。実行ファイルに wav を含めることが出来るのと、同時に音が鳴らせるので使いやすい。ただし、ウインドウがアクティブでないと音は消える。これも Direct Sound の性質である。

それでは Sound[0].Replay メソッドである。これはサウンドライブラリの0番の音を鳴らすのに使用している。音を鳴らすメソッドはもう一つ、Play があるが、違いは Replayの場合はもしも0番が再生中であればいったん中断し、サウンドの先頭に戻ってから鳴らす。Play は再生中なら鳴らさないである(あまり正確な表現ではないが)。したがって今回のデモプログラムでは、Play を使用しても同じ結果が得られる。

    //As well as making the player explode lets send the invader
    //wizzing off in a random direction. The AllActions property
    //is set to laStopOutside which means the invader will stop
    //when is completely leavs the LimitsRect.
    with DGCSpriteMgr1.Sprites[1] as TDGCBouncer do
    begin
      Randomize;
      Direction := Random(32);
      MaxSpeed := 5;               //Set the maximum speed
      Speed := 5;                  //Set the current speed
      AllActions := laStopOutSide; 
    end;
今回のスターシップは最大移動速度は上昇したものの、慣性に影響されて操作性はダウン。更にスピードアップと耐久性がトレードオフになったのか、衝突後は爆発してしまう(前回のデモではつぶれただけ)。対して、宿敵ハネカニモドキは音は出るわアニメーションするわでかなりのパワーアップがなされている。果たして、地球の運命や如何に!!

それはそうと、ここには新しい命令はない。スターシップと衝突した後のハネカニモドキは、ランダムな方向で画面の外(正確には Limits の外)に消える。

procedure TForm1.DGCSpriteMgr1AnimationEnd(Sprite: TDGCSprite);
begin
     //In this particular demo this event is called when the explosion
     //animation as reached the end. The OnAnimationEnd event is only
     //called if the animation is not looped. The only thing that blows
     //up in this demo is the player so hide and disable it. Although
     //you would normally decrease the player lives and do stuff like that.
     DGCSpriteMgr1.Sprites[0].Hide;
     DGCSpriteMgr1.Sprites[0].Disable;
end;
前半でも触れているが、アニメーションのループプロパティが False の場合、この OnAnimationEnd イベントが発生する。ここではスターシップの(今は爆発中)スプライトを隠し、見えなくしている。

procedure TForm1.DGCSpriteMgr1SpriteDirChange(Sprite: TDGCSprite;
  LimitsSide: TLimitsSide);
begin
     //Make a boing! noise when the invader hits the limits rectangle
     if Sprite is TDGCBouncer then
          DGCAudio1.Sound[1].Replay;
end;
さて、DGCSpriteMgr のイベントは全部で7つ定義されている。そのうちの1つ、OnSpriteDirChange は、Bouncer タイプのスプライトが領域の端で跳ね返った時に(も?)発生する。ここでは、ハネカニモドキが跳ね返る時の音を鳴らしている。

以上が Demo4 の説明である。今回は新しいコンポーネントが3つもあったので、説明が長くなったが、どれもゲームを作るためには重要なものなので、ぜひマスターして欲しい。(偉そうに言うな!)

もどる