4. SPRITES
Now it's time to really spruce up the place by turning our cone into a textured mountain, adding 500 trees, turn our water into a lush meadow, and make our player character animated! We will be using simple animation strips like this one:

This sprite strip is 128 x 48 pixels and since there are 4 frames that makes each frame 32 x 48 pixels. To keep things simple we will only be using 1 view for our animated sprite sets. We'll use the back view for our player character and the front view for our other characters in the next lesson.

If you haven't already downloaded the tutorial files do so now, unzip to your desktop and OPEN up lesson4.bb, functions4.bb, and loadworld4.bb (below) and RUN / LANUCH the lesson4.bb program by pressing the ROCKET ICON

and you should see this...

NEW CODE is BOLD.

Here's the CODE!
lesson4.bb

Include "functions4.bb"

Global escape_key = 1
Global screen_width = 640, screen_height = 480

Graphics3D screen_width, screen_height, 16, 2
SetBuffer BackBuffer()

AmbientLight 200, 200, 200
light1 = CreateLight(2)

cam1 = CreateCamera()
CameraViewport cam1, 0, 0, screen_width, screen_height

Include "loadworld4.bb"

LoadMusic()
LoadSFX()

MoveMouse screen_width/2, screen_height/2

While Not KeyHit( escape_key )


    If run_vol# > 0 Then AnimSprite( player, anim_tex, 200, 4 )

    object_key_control( player )

    ChannelVolume runchannel, run_vol#
    PositionEntity sky, EntityX( player ), EntityY( player ), EntityZ( player )

    UpdateWorld
    RenderWorld

    
; 2D here


Flip
Wend

End


functions4.bb

. . .
Function LoadRandomSprites(sprite$, total, x1, x2, z1, z2)
    temp_sprite = LoadSprite( sprite$ , 7 )
    HandleSprite temp_sprite, 0, -1
    EntityAutoFade temp_sprite, 100, 120

    For s = 1 To total
        sx# = Rnd( x1 , x2 ) : sz# = Rnd( z1 , z2 )
        copied_sprite = CopyEntity( temp_sprite )
        PositionEntity copied_sprite, sx, 0, sz
        ScaleSprite copied_sprite, Rand( 2, 3 ), Rand( 4, 6 )
    Next
    FreeEntity temp_sprite
End Function


Function AnimSprite( obj, atex, d, f )
    frame = MilliSecs( ) / d Mod f
    EntityTexture obj, atex, frame
End Function

. . .


loadworld4.bb

LoadRandomSprites( "tree.bmp", 500, -250, 250, -250, 250 )

player = CreateSprite()
anim_tex=LoadAnimTexture( "char_back.bmp", 7, 32, 48, 0, 4 )
EntityTexture player, anim_tex, 1
HandleSprite player, 0, -1
ScaleSprite player, 2, 2

PositionEntity player, 0, 0, 10
EntityParent cam1, player


; Make the ground! Create, texture and scale a plane
plane1=CreatePlane()
texture1=LoadTexture("terrain_tex.jpg")
ScaleTexture texture1, 10, 10
EntityTexture plane1,texture1

; Make a pyramid! Create, position, scale and texture a cone
cone1 = CreateCone()
PositionEntity cone1, 0, 0, 50
ScaleEntity cone1, 30, 30, 30
texture2 = LoadTexture("rough.jpg")
ScaleTexture texture2, .2, .2
EntityTexture cone1, texture2



NEW COMMANDS
lesson4.bb
. . .
   ; Use the Player "RUN" sound volume to toggle Player Sprite animation
    If run_vol# > 0 Then AnimSprite( player, anim_tex, 200, 4)

    object_key_control( player )

. . .
    PositionEntity sky, EntityX( player ), EntityY( player ), EntityZ( player )
. . .
Now that we have a real animated sprite called player, we can control it and position our sky sphere on it.
Since the object_key_control( ) function also controls the sound, we will use the sound volume to control
our NEW sprite animation function AnimSprite( player, anim_tex, 200, 4).
When we are running forward and the running sound is playing then the sprite animation will be active.
EXPERIMENT !
Try changing AnimSprite( player, anim_tex, 200, 4) to
AnimSprite( player, anim_tex, 50, 4)

What happened?
Undo the changes

function4.bb

. . .
; Load world randomly with "tree" sprites using
; our NEW reuseable LoadRandomSprites() function
; Just supply the sprite image, how many, and the X and Z range for random placement!

Function LoadRandomSprites( sprite$, total, x1, x2, z1, z2 )
    temp_sprite = LoadSprite( sprite$ , 7 )
    HandleSprite temp_sprite, 0, -1
    EntityAutoFade temp_sprite, 100, 120

    For s = 1 To total
        sx# = Rnd( x1 , x2 ) : sz# = Rnd( z1 , z2 )
        copied_sprite = CopyEntity( temp_sprite )
        PositionEntity copied_sprite, sx, 0, sz
        ScaleSprite copied_sprite, Rand( 2, 3 ), Rand( 4, 6 )
    Next
    FreeEntity temp_sprite
End Function


Function AnimSprite( obj, atex, d, f )
    frame = MilliSecs( ) / d Mod f
    EntityTexture obj, atex, frame
End Function

. . .

temp_sprite = LoadSprite( sprite$ , 7 ) creates a sprite and textures it with whatever image we pass through sprite$ . Since we will be using a copy command this sprite (called temp_sprite) will only be used temporarily. The 7 is a commonly used flag for multiple image options that need not be discussed at this time. Also, by default, black background in any image becomes transparent.

HandleSprite temp_sprite, 0, -1
moves the positioning "handle" from it's default 0, 0 location. Most sprites are positioned using the center of the sprite but we want to use the bottom-center 0, -1 so that when we place our characters they aren't buried halfway in the ground :)


EntityAutoFade temp_sprite, 100, 120
is a great command that will cause game object we select to fade off into the distance. With this setting it will start to fade at 100 units and be invisible by 120 units. This helps prevent large levels with lots of objects to run faster.

For s = 1 To total . . . Next is another type of LOOP similar to our While - Wend main game loop but this one will loop for a specific number of times ( total ) and then stop. When we called this function we set the total to 500 trees.

sx# = Rnd( x1 , x2 ) : sz# = Rnd( z1 , z2 )
are 2 commands on the same line (separated by a colon) that generate random numbers between 2 supplied values. When we called this function we set x1 , x2 and z1 , z2 to -250, 250, and -250, 250 which will scatter our 500 trees in a 500 x 500 unit square centered in our level.

copied_sprite = CopyEntity( temp_sprite )
is another powerful command that lets us quickly copy our previously loaded temp_sprite rather than waste time reloading the image 500 times off the hard drive!

PositionEntity copied_sprite, sx, 0, sz is an old command that now uses the randomly generated coordinates ( sx, sz ) to position things in our world.

ScaleSprite copied_sprite, Rand( 2, 3 ), Rand( 4, 6 )
is used to randomly scale our sprite 2-3 times as wide and 4-6 times as high. You might think using ScaleEntity from lesson 1 would work but that's only for 3D objects. Sprites are flat and require their own scale command.

FreeEntity temp_sprite

Since we don't need the temp_sprite any more we can save a little memory by freeing it up (deleting it). These lessons won't go into this kind of "garbage collection" any more but for large scale game projects with multiple levels it is crucial.
EXPERIMENT !
Change HandleSprite temp_sprite, 0, -1 to
HandleSprite temp_sprite, 0, 0

Change EntityAutoFade temp_sprite, 100, 120 to
EntityAutoFade temp_sprite, 50, 60

Change ScaleSprite copied_sprite, Rand( 2, 3 ), Rand( 4, 6 ) to
ScaleSprite copied_sprite, Rand( 2, 3 ), Rand( 10, 20 )
Undo the changes

Function AnimSprite( obj, atex, d, f )
frame = MilliSecs( ) / d Mod f
EntityTexture obj, atex, frame
is indeed a mathematically challenging, interesting and critical function for doing animated sprites that allows us to apply an animated strip texture ( atex ) to any sprite ( obj ) if we know how many frames the strip has and can specify an animation speed ( d ) in milliseconds. We previously chose a d of 200 and since 1 second is 1000 milliseconds then the sprite will animate at 5 frames per second. Luckily you don't need to understand the math to use it :)

loadworld4.bb

LoadRandomSprites( "tree.bmp", 500, -250, 250, -250, 250 )

player = CreateSprite()
anim_tex = LoadAnimTexture( "char_back.bmp", 7, 32, 48, 0, 4 )
EntityTexture player, anim_tex, 1
HandleSprite player, 0, -1
ScaleSprite player, 2, 2

PositionEntity player, 0, 0, 10
EntityParent cam1, player

; Make the ground! Create, texture and scale a plane
plane1=CreatePlane()
texture1=LoadTexture("terrain_tex.jpg")
ScaleTexture texture1, 10, 10
EntityTexture plane1,texture1

; Make a pyramid! Create, position, scale and texture a cone
cone1 = CreateCone()
PositionEntity cone1, 0, 0, 50
ScaleEntity cone1, 30, 30, 30
texture2 = LoadTexture("rough.jpg")
ScaleTexture texture2, .2, .2
EntityTexture cone1, texture2



LoadRandomSprites( "tree.bmp", 500, -250, 250, -250, 250 ) "calls" our function previously explained above and passes the name of the sprite "tree.bmp", tells the function we want 500 copies spread in the X plane from -250 to +250 and the Z plane from -250 to +250. At this time it doesn't check to see if sprites get planted in the same place as something else though.
player = CreateSprite() creates a flat square polygon waiting to have an image "painted" on it
anim_tex = LoadAnimTexture( "char_back.bmp",
7, 32, 48, 0, 4 ) is a powerful command that loads an animation strip "char_back.bmp" that consists of 4    32 x 48 pixel poses of our character. The 7 & 0 are standard default values we can ignore for now.

EntityTexture player, anim_tex, 1 textures our player character using the 1st of the 4 images

HandleSprite player, 0, -1
moves the positioning "handle" from it's default 0, 0 location to the bottom-center 0, -1.

ScaleSprite player, 2, 2
is used to make our sprite twice as big.
THE CHALLENGE!!!
Find a different sprite set or create your own original character sprite set and replace the tree sprite with something else

NEXT : Lesson 5 - TYPES