Articles Archive
Articles Search
Director Wiki
 

Lingo Collision Detection, Part 3

May 10, 2000
by Gary Rosenzweig

For the last two weeks we have looked at collision detection. We've examined Lingo functions like intersect, intersects, inside and hitTest. We've also looked at mathematical distance formulas. Director 8 offers a new way to do collision detection. Thanks to the getPixel function, we can examine contents of individual pixels.

So, if we take an image, say a character in a game, and examine every pixel in the bitmap, we can make a list of all the points in the image. We can rule out pixels that are the same as the background color of the bitmap. This will create a virtual mask of the image as a series of points.

We can then compare this series of points to other bitmaps on the Stage. This point-by-point comparison will give us an accurate reading of when there is a collision. Better still, it can give us that reading before the sprite is actually moved. This will enable us to detect collisions before they happen and prevent them, rather than acting after the fact as we would have to do with the intersects function.

Let's make a quick behavior that does this. The first step would be to define what is a background color and what is not. Then, to get a list of points in the image that are not transparent. We will use white as the background color, and even name the property pWhite, though another can be used depending on the image.

property pPoints, pWhite

on beginSprite me
  -- define transparent pixels
  pWhite = rgb(255,255,255)
  -- find key points on this sprite
  findPoints(me)
end

This handler looks at all of the points in the image and adds those that are not the background color to a list.

on findPoints me
  
  -- get the image
  image = sprite(me.spriteNum).member.image
  
  -- init the list
  pPoints = []
  
  -- find top and bottom points
  repeat with col = 0 to image.width-1
    
    repeat with row = 0 to image.height-1
      if image.getPixel(col,row) <> pWhite then
        add pPoints, point(col,row)
      end if
    end repeat
    
  end repeat
  
end

This list of points is then used in the next handler to compare with other sprites. To speed things up, only a small range of sprites are compared. The useful mapMemberToStage and mapStageToMember are used to translate points in the character's image to points on the Stage, and then those points to points in the other sprites' image.

on checkCollision me
  
  firstSprite = 1
  lastSprite = 10
  
  repeat with s = firstSprite to lastSprite
    
    if s = me.spriteNum then next repeat
    
    if sprite s intersects me.spriteNum then
      
      -- loop through keypoints
      repeat with i = 1 to pPoints.count
        
        -- get stage location of a keypoint
        myPoint = sprite(me.spriteNum).mapMemberToStage(pPoints[i])
        -- figure out which pixel this corresponds to in other sprite
        otherPoint = sprite(s).mapStageToMember(myPoint)
        
        -- see if it corresponds to any point at all
        if not voidP(otherPoint) then
          -- get color of pixel there
          pixelColor = sprite(s).member.image.getPixel(otherPoint)
          -- if a non-white pixel, then there has been a collision
          if pixelColor <> pWhite then return s
        end if
        
      end repeat
      
    end if
    
  end repeat
  
  -- no collisionw
  return 0
  
end

On every frame, the "on checkCollision" handler is called. In this example, this information is only used to fill a text member. In a real situation, you will want to use this information to prevent movement.

on exitFrame me
  if checkCollision(me) > 0 then
    member("status").text = "Collide!"
  else
    member("status").text = "Nothing"
  end if
end

In the example movie below, the sprite can be moved by dragging. This is enabled by just setting the moveable property of the sprite to TRUE.

Download the Director 8 sample movie in Mac or PC format.

In a real-life situation, you will not want to use the mouse to move such a character. Instead, use the keyboard. This will enable you to get closer to objects. Using the mouse means that the character can quickly jump from one area of the screen to another. So, it could conceivably jump from one side to the other, and place it right in the middle of an object or wall. If you are trying to prevent such collisions, then you would not allow such movement, and the sprite would remain on the other. As to the best collision detection... we still have no definite answers. This week's solution can be very slow for larger sprites, and even slower if you have to adjust it for different character positions and movement animation.

It is important to realize that you will have to create different collision detection solutions depending on what the desired result is. If you only wish to prevent collisions into walls, then rectangle intersections may be enough. However, if you wish to detect a sword thrust through a character, then this week's method may be needed, or at least the sprite intersects function. Keep all of these methods in mind and come up with your own based on an understanding of them.

Gary Rosenzweig's latest book is "Advanced Lingo for Games." In it, you can find the source code for more than 20 complete games. More information about the book can be found at http://clevermedia.com/resources/bookstore/book4.html. It can be purchased there, or in your local bookstore.

Gary Rosenzweig is the Chief Engineer, founder, and owner of CleverMedia, a game and multimedia development company in Denver, Colorado. He is the author of ten books on Macromedia Director and Flash, including his latest, Special Edition Using Director MX.

Copyright 1997-2024, Director Online. Article content copyright by respective authors.