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
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.