Colliding Sprites

Our last installment introduced using the BrightScript 2D API to create sprites and animations.  In this article we will look at how to add collisions to sprite animations.  Many 2D games use these techniques to simulate real world physics.  Ricocheting bullets, bouncing balls, and birds crashing into pigs are some examples of scenarios that you might want to include in a 2D application.

To demonstrate and help you better understand these topics, I have included a sample BrightScript channel which can be downloaded here.  The sample animates up to five bouncing and colliding billiard balls.  It starts with just one ball, but you can add up to a total of five by pressing the remote control right arrow key.  A screen shot of the channel in action is shown below.

Detecting Sprite Collisions

The BrightScript ifSprite interface, supported by the roSprite component, includes functions for detecting collisions.  The simplest of these is CheckCollision():

Object CheckCollision()

This function returns the first sprite that this calling sprite collides with.  A collision is basically defined by overlap between the bounding rectangles of the two sprites.  There is actually more to the formal technical definition of a collision in BrightScript which we will get into shortly.  But for now, this definition will suffice.

Closely related to CheckCollision() is the CheckMultipleCollisions() function:

Object CheckMultipleCollisions()

This function returns an array containing all sprites that collide with the calling sprite.  This is useful in situations where there may be many objects colliding with a given sprite at a given time.

A channel with an array of sprites could check for collisions at a given instant in time as follows:

for each sprite in sprites
    dx = sprite x velocity
    dy = sprite y velocity
    sprite.MoveTo( (sprite.GetX() + dx), (sprite.GetY() + dy) )
    collidingSprite = sprite.CheckCollision()
    if (collidingSprite  invalid)
        ‘Move the sprites according to a collision equation
    endif                    
end for

Another interesting sprite feature that can be used in conjunction with collisions is the ability to specify whether a sprite is drawable. This property is set with the SetDrawableFlag() function:

Void SetDrawableFlag(Boolean enable)

Setting this flag on a sprite makes the sprite invisible. However, it can still be moved, animated, and checked for collisions using CheckCollision() and CheckMultipleCollisions(). A 2D game with cloaking device enabled space ships or characters with powers of invisibility lying in wait come to mind! To try out what an invisible collidable sprite can mean, add the indicated line to the sample channel code:

if ((id = codes.BUTTON_RIGHT_PRESSED) AND (spriteCount <= 5))
    spriteCount = spriteCount + 1
    sprites[spriteCount] = compositor.NewAnimatedSprite(Rnd(screenWidth-ballSize),
      Rnd(screenHeight-ballSize), balls[Rnd(5)-1])
    sprites[spriteCount].SetDrawableFlag(false) ‘ADD THIS LINE
    sprites[spriteCount].SetData( {dx: Rnd(20)+10, dy: Rnd(20)+10,
      index: spriteCount} )
endif

Sprite Collidablity

By default, all sprites that you create on a compositor are “collidable.” If their bounding rectangles overlap the check collision functions report a collision. However, you can control the collidability of specific sprites through BrightScript. To see how this is done, we need to talk about sprite member and collidability flags. There are two ifSprite functions for setting these flags:

Void SetCollidableFlags(Integer flags)
Void SetMemberFlags(Integer flags)

The flags argument passed to these functions is interpreted as a set of individual bits. For a sprite to be collidable, not only does its bounding region need to intersect that of another sprite. The corresponding bits in both the collision and member flags value for that sprite must be set. In other words, the bitwise AND of the sprite’s collision and member flags must be non-zero.

At first it might appear that this scheme is overkill. Why not just use a single Boolean flag to specify whether a sprite is collidable? The answer is that this design allows for more complex collision scenarios. You might be building a game that only lets certain types or numbers of projectiles to penetrate a character’s armor, or that lets bullets bounce off another character if he happened to fire a different weapon at the same time. Using the member flag scheme, complex bit masks can be designed that allow such collisions in certain conditions but not others.

Another simpler use of the member flags is to limit collisions with a single sprite to one collision per frame. Channels that use collisions often want to detect collisions and then move the colliding sprites according to a physics algorithm immediately thereafter. This often happens in a loop like the one we showed previously. In cases where there are numerous sprites moving around the screen, updating the location of a sprite after a collision may bring it into collision with another sprite in the same frame. To prevent this, after moving a sprite involved in a collision, you can set the member flags of the colliding sprite to 0. In the next frame, all sprites can have their member flags set back to 1 allowing them to participate in collisions again. Here is an example of how this can be implemented:

while true
    for each sprite in sprites
        sprite.SetMemberFlags(1)
    end for
    event = port.GetMessage()
    if (event = invalid)
        for each sprite in sprites
            collidingSprite = sprite.CheckCollision()
            if (collidingSprite  invalid)
                handleCollision(sprite) ‘Channel specific code to move sprite…
            endif                    
        end for                
    endif
end while
    …
Function handleCollision(sprite as object) as void
    … ‘Move the sprite according to collision physics
    sprite.SetMemberFlags(0) ‘sprite can’t collide again until flags is set to 1 in next frame
End Function

Grab the sample collision channel from this link and start playing around with colliding animated sprites. With this and the last two articles on the BrightScript 2D API under your belt, you should be publishing games to the Roku Channel Store in no time. Happy coding!

About Robert Burdick

Roku Developer Support Manager
This entry was posted in Uncategorized. Bookmark the permalink.
  • Klimov

    aoeworld.com