Search Tips Advanced Search |
| Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres Wordware Publishing, Inc. ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous | Table of Contents | Next
We can do this by setting our threshold amount to a specific time interval, measured in milliseconds. Our current interval measurement would then be set to the system time of the last increment of the frame number. In the game loop, we then check to see if the amount of time indicated in the threshold has elapsed since the last increment of the frame number. If so, we increment the frame number and reset the current interval measurement. The following example demonstrates this technique. Listing 6-7: Accurate sprite animation timing
    type

      sprite class  TSprite = class

        FXPos, FYPos,                   // tracks the current position

        FXVel, FYVel,                   // tracks the velocity

        FCurFrame,                      // tracks the current frame number

        FFrameNum: Integer;             // tracks the number of frames

        FImages: IDirectDrawSurface4;   // a pointer to the sprite images

        FCurTime,                       // tracks a timing value

        FTimeThreshold: LongWord;       // holds the animation timing threshold

      end;

    procedure TfrmDXAppMain.FormActivate(Sender: TObject);

    var

        .

        .

        .

    begin

        .

        .

        .

      {initialize the sprite object}

      FSprite := TSprite.Create;

      FSprite.FXPos := DXWIDTH - 90;

      FSprite.FYPos := 215;

      FSprite.FXVel  := -1;

      FSprite.FYVel  := 0;

      FSprite.FCurFrame := 0;

      FSprite.FFrameNum := 5;

      FSprite.FImages := FImages;

      {retrieve the current system time to initialize the timing algorithm}

      FSprite.FCurTime := timeGetTime;

      {we want the sprite to animate every 100 milliseconds}

      FSprite.FTimeThreshold := 100;

        .

        .

        .

    end;

    procedure TfrmDXAppMain.DrawSurfaces;

    var

        .

        .

        .

    begin

      {erase the last frame of animation by using a simple color fill to

       ‘black out’ the entire backbuffer surface}

      FillChar(BltFx, SizeOf(TDDBltFx), 0);

      BltFx.dwSize := SizeOf(TDDBltFx);

      BltFx.dwFillColor := 0;

      FBackBuffer.Blt(nil, nil, nil, DDBLT_COLORFILL or DDBLT_WAIT, @BltFx);

      {increment the frame number and update the sprite’s position}

      with FSprite do

      begin

        {if the elapsed time since the last frame number increment

         is greater than our threshold amount...}

        if timeGetTime - FCurTime > FTimeThreshold then

        begin

          {animate the sprite by incrementing the frame number}

          Inc(FCurFrame);

          {make sure we roll over the frame number as necessary}

          if FCurFrame > FFrameNum then

            FCurFrame := 0;

          {store the current system time}

          FCurTime := timeGetTime;

        end;

        .

        .

        .

    end;
#### Z-Order As stated above, the z-order of a sprite is its position along an imaginary third- dimensional axis. Some games may feature foreground sprites and background sprites, while others may have sprites that move closer to and farther from the user, scaling as appropriate in a pseudo three-dimensional viewpoint. In games like these, drawing sprites in the correct order becomes an important issue. However, actually accomplishing this illusion of depth is quite simple. Essentially, the application needs to draw the sprites starting with the farthest from the user and ending with the closest. As you draw the nearer sprites, their images will obscure those farther away, giving the illusion of depth. This is known as the painter’s algorithm; the technique is illustrated below. Figure 6-9: The painter’s algorithm We’ve already implemented the painter’s algorithm in a very simplistic way by drawing the background behind the sprites before drawing them. In a more complex, real-world application, the z-order of the sprites themselves must be taken into account. We can accomplish this by simply adding another member to our sprite object for tracking this z-order. In practice, we’ll need to move the sprites and then sort them by z-order before drawing. The sorting step is necessary in order to draw the sprites in the correct order. Any well-documented, high-performance sorting algorithm will do. In the following example, we don’t employ any sort of real sorting algorithm for the sake of code simplicity. However, the example does demonstrate a simple z-ordering mechanism. Sprites that are moving downward have a higher, or closer, z-order than those moving upward. Implementing scaling to draw the sprites larger when they are closer and smaller when they are farther away would improve the illusion. Listing 6-8: Drawing sprites according to z-order
    type

       .

       .

       .

    {the sprite class}

      TSprite = record

        FXPos, FYPos,                   // tracks the current position

        FXVel, FYVel,                   // tracks the velocity

        Height, Width,                  // sprite frame height and width

        FCurFrame,                      // tracks the current frame number

        FFrameNum: Integer;             // tracks the number of frames

        FImages: IDirectDrawSurface4;   // a pointer to the sprite images

        FCurTime,                       // tracks a timing value

        FTimeThreshold: LongWord;       // holds the animation timing threshold

        ZOrder: Integer;                // the sprite depth

      end;

    var

       .

       .

       .

      Sprites: array[0..1] of TSprite;

      ZOrderIndex: array[0..1] of Integer;

    implementation

       .

       .

       .

    procedure TfrmDXAppMain.FormActivate(Sender: TObject);

    begin

       .

       .

       .

    {initialize the first spaceship}

      with Sprites[0] do

      begin

        Width := 61;

        Height := 56;

        FXPos := DXWIDTH div 2;

        FYPos := DXHEIGHT;

        FYVel := 3;

        FImages := FShip1;

        ZOrder := 0;

      end;

      {initialize the second spaceship}

      with Sprites[1] do

      begin

        Width := 60;

        Height := 74;

        FXPos := DXWIDTH div 2;

        FYPos := 0;

        FYVel := -3;

        FImages := FShip2;

        ZOrder := 1;

      end;

       .

       .

       .

    end;

       .

       .

       .

    procedure TfrmDXAppMain.DrawSurfaces;

    var

      iCount: Integer;

      Area: TRect;

      {this function moves a sprite}

      procedure MoveSprite(var Sprite: TSprite);

      begin

        Sprite.FYPos := Sprite.FYPos + Sprite.FYVel;

        if Sprite.FYPos<0 then

        begin

          Sprite.FYPos := 0;

          Sprite.FYVel := 0-Sprite.FYVel;

          {we’ve only got 2 z-order values, so flip it when the sprite

           hits a boundary}

          Sprite.ZOrder := 1-Sprite.ZOrder;

        end

        else

        if Sprite.FYPos>DXHEIGHT-Sprite.Height then

        begin

          Sprite.FYPos := DXHEIGHT-Sprite.Height;

          Sprite.FYVel := 0-Sprite.FYVel;

          Sprite.ZOrder := 1-Sprite.ZOrder;

        end;

      end;

    begin

      {erase the previous animation frame}

      Area := Rect(0, 0, DXWIDTH, DXHEIGHT);

      FBackBuffer.BltFast(0, 0, FBackground, Area,

                          DDBLTFAST_NOCOLORKEY or DDBLTFAST_WAIT);

      {move the sprites}

      for iCount := 0 to 1 do

        MoveSprite(Sprites[iCount]);

      {perform some really simplistic z-order sorting}

      if Sprites[0].ZOrder > Sprites[1].ZOrder then

      begin

        ZOrderIndex[0] := 0;

        ZOrderIndex[1] := 1;

      end

      else

      begin

        ZOrderIndex[0] := 1;

        ZOrderIndex[1] := 0;

      end;

      {now that the sprites are sorted per their z-order, draw them starting

       with the farthest sprite. this will result in a layered graphic with

       distant sprites being obscured by closer sprites}

      for iCount := 0 to 1 do

        FBackBuffer.BltFast(Sprites[ZOrderIndex[iCount]].FXPos,

                            Sprites[ZOrderIndex[iCount]].FYPos,

                            Sprites[ZOrderIndex[iCount]].FImages,

                            Rect(0, 0, Sprites[ZOrderIndex[iCount]].Width,

                                 Sprites[ZOrderIndex[iCount]].Height),

                            DDBLTFAST_SRCCOLORKEY or DDBLTFAST_WAIT);

    end;
Previous | Table of Contents | Next

Products | Contact Us | About Us | Privacy | Ad Info | Home Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.