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
Of special consideration is the part in the rendering loop (the DrawSurfaces method) where the pointers to the backbuffer and sprite surface memory are incremented to point to the next row:
    {move to the next row in the sprite and backbuffer surface memory}

    Inc(BackMem, BackSurfaceDesc.lPitch);

    Inc(SpriteMem, SpriteSurfaceDesc.lPitch);
Among the values returned by the call to the Lock method is the surface’s pitch, or stride. This value is the actual distance between two pixels that are vertically adjacent. It is a result of storing memory that is logically arranged in rectangular fashion, but is actually accessed linearly. Often, the pitch is the same as the width of the surface, as one might expect. However, never assume that the pitch is constant, even for different surfaces in the same video memory. Always use the lPitch value to move vertically between rows.
| Caution: Some display cards can organize memory differently than others, and the resolution and color depth can have dramatic effects on how memory is accessed directly. Therefore, always increment the row by using the lPitch value.
Unlocking a Surface With every call to the Lock method, a call to the Unlock method must be made. This allows the locked surface to be accessed again by other threads and processes, and most importantly by the hardware. A surface cannot be blitted or otherwise manipulated by the hardware unless it has been unlocked. After calling Unlock, any pointers to the surface memory will no longer be valid. Therefore, it is necessary to call Lock again during the next rendering cycle to retrieve a valid pointer to surface memory. The Unlock method of IDirectDrawSurface4 is defined as:
    function Unlock (

    lpRect: PRect                     // the previously locked rectangular area

    : HResult;                        // returns a DirectX error code
The only parameter defines the rectangular area that was passed to the initial Lock method. It is possible to lock multiple rectangular, overlapping areas simultaneously. Thus, DirectDraw needs to know which rectangular area to unlock, and the values for this parameter must match those passed to the original Lock method. Pass nil here if you used nil in the Lock method to lock the entire surface.
| Caution: When accessing surface memory, it is very important to complete rendering quickly and unlock the surface as soon as possible. This is because Windows is effectively stopped between the calls to Lock and Unlock. This can cause things to get backed up rather quickly, causing all sorts of nasty side effects, like system hangs. This won’t occur when locking surfaces explicitly created in surface memory, but always try to use tight, efficient code when locking a surface and always remember to call the Unlock method when you no longer require direct memory access.
### Collision Detection All games, at some point, must deal with the situation in which one sprite touches another. For some games, this may be when a bullet strikes an alien or when an animated mouth collides with a glowing white pellet to eat it. A game application must determine when these situations happen, and this is where collision detection comes in. There exist as many different methods to perform collision detection as there do methods to draw a sprite. Many articles have been written on this subject, exploring the nuances of checking large amounts of sprites against each other and the optimizations required to perform such techniques in a timely fashion. In most cases, however, sprite collision can be detected using one of two techniques: bounding boxes and pixel overlap comparisons. #### Bounding Boxes The bounding box method of collision detection works by defining a rectangular region around the sprite, its “bounding box.” To determine if a collision with another sprite has occurred, the application simply determines if their bounding box rectangles intersect. If indeed the rectangles intersect, a collision has occurred and the game application can respond accordingly, as shown in the following illustration. Figure 6-7: Sprite bounding boxes Notice that in Figure 6-7b, the intersected rectangles would indicate that a collision has occurred, although clearly the sprites are not touching. This is the inherent drawback to using bounding boxes, in that many times during close calls a collision may be flagged when in fact the sprites never touched. Shrinking the size of the bounding boxes to where they do not encapsulate the entire sprite diminishes this error, as shown in Figure 6-7c. However, the reverse effect can occur, where two sprites have collided although no collision was detected. For the most part, it is better to err on the side of the player and allow the infrequent true collisions to go unresolved. However, for some games where precise collision detection is required, it is necessary to actually check the pixels of one image with the pixels of another to see if they overlap, as we examine shortly.
| Tip: It is possible to use two or more bounding boxes for a sprite, thereby providing a more accurate approximation of the sprite’s surface. The GDI also contains many functions to create regions of different shapes and sizes, including circular and polygonal, which can then be used to detect collisions more accurately.
Windows has highly optimized routines that make manipulating rectangles a breeze. In particular, the IntersectRect function determines if two rectangles overlap and returns a rectangle that defines the area of intersection. The IntersectRect function is defined as:
    IntersectRect(

    var lprcDst: TRect;      // the rectangle receiving the intersection coordinates

    const lprcSrc1: TRect;   // the first rectangle

    const lprcSrc2: TRect    // the second rectangle

    : BOOL;                  // returns TRUE or FALSE
The first parameter is a rectangle that will receive the coordinates of the intersection between the rectangles specified in the last two parameters. If the rectangles do not intersect, the first parameter will be set to all zeroes and the function returns FALSE. The following example demonstrates how to use bounding boxes to determine if two sprites have collided. Listing 6-5: Bounding box collision detection
    procedure TfrmDXAppMain.DrawSurfaces;

    var

      Intersection, Area: TRect;

      iCount: Integer;

      {this function moves and draws a sprite}

      procedure MoveSprite(var Sprite: TSprite);

      var

        SrfcDC: HDC;

        TempCanvas: TCanvas;

      begin

        {move the sprite, checking for horizontal and vertical screen boundaries}

        Sprite.FXPos := Sprite.FXPos + Sprite.FXVel;

            .

            .

            .

        {update the sprite’s bounding box based on its new position}

        Sprite.Boundary := Rect(Sprite.FXPos, Sprite.FYPos,

                                Sprite.FXPos+Sprite.Width,

                                Sprite.FYPos+Sprite.Height);

        {draw the sprite transparently}

        Area := Rect (0, 0,  Sprite.Width, Sprite.Heigh);

        FBackBuffer.BltFast(Sprite.FXPos, Sprite.FYPos, Sprite.FImages,

                            Area, DDBLTFAST_SRCCOLORKEY or DDBLTFAST_WAIT);

        {draw a red border around the sprite to display its bounding box}

            .

            .

            .

    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 and draw the sprites}

      for iCount := 0 to 1 do

        MoveSprite(Sprites[iCount]);

      {if any of the sprite’s bounding boxes intersect, signal a collision. real

       world examples would generally require checking many rectangles against

       each other}

      if IntersectRect(Intersection, Sprites[0].Boundary, Sprites[1].Boundary) then

        Messagebeep(0);

    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.