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.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.