6. A. I. & Paths
Get ready to make our 3 previously animated sprite characters appear somewhat intelligent rather than just randomly wandering around the world. We will make 3 different A. I. (Artificial Intelligence) temperments , i.e. a static non-moving simpleton (Case 0), a path following patroller (Case 1), and a more intelligent patroller (Case 2) that will aggressively pursue you if you come too close! To do this we will only need to modify our MoveCharacters( target ) function and add a few extra features to our SPRITE TYPE in our loadworld6.bb.

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

and you should see this...

NEW CODE is BOLD.

Here's the CODE!
lesson6.bb . . . Nothing NEW here!

Include "functions6.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 "loadworld6.bb"

LoadMusic()
LoadSFX()

MoveMouse screen_width/2, screen_height/2

While Not KeyHit( escape_key )

    MoveCharacters( player )
    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


loadworld6.bb

Type Sprite
    Field ID, char_tex
    Field pt1, pt2 ; Path endpoints
    Field targetpt
    Field temper

End Type

For C=1 To 3
    Character.Sprite = New Sprite
    Character\ID = CreateSprite()
    ; add temper + destination variables
    Read temper, img$, X, Y, Z, X2, Y2, Z2
    Character\char_tex = LoadAnimTexture( img$, 7, 32, 48, 0, 4)
    EntityTexture Character\ID, Character\char_tex, 1
    HandleSprite Character\ID, 0, -1
    ScaleSprite Character\ID, 2, 2
    EntityAutoFade Character\ID, 100, 120
    PositionEntity Character\ID, X, Y, Z

    Character\temper = temper
    Character\pt1 = CreatePivot() : Character\pt2 = CreatePivot()
    PositionEntity Character\pt1, X, Y, Z
    PositionEntity Character\pt2, X2, Y2, Z2
    Character\targetpt = Character\pt2

Next

.Characters
; add temper 0=still, 1=path, 2=path + track AND destination X2, Y2, Z2
Data 0, "char2.bmp", 30, 0, 30, 30, 0, 0
Data 1, "char3.bmp",-30, 0, 30, 20, 0, 30
Data 2, "char4.bmp", 0, 0, 30, -30, 0, 0
. . .


functions6.bb

. . .
Function MoveCharacters( target )
    For Character.Sprite = Each Sprite
    Select Character\temper

        Case 0
; Static
            ; Do Nothing or maybe run idle animation?

        Case 1
; Path
            PointEntity Character\ID, Character\targetpt
            MoveEntity Character\ID, 0, 0, Rnd(.05,.1)
            AnimSprite( Character\ID, Character\char_tex, 200, 4 )
            If EntityDistance( Character\ID, Character\targetpt ) < 1
                If Character\targetpt = Character\pt1 Then
                    Character\targetpt = Character\pt2
                Else Character\targetpt = Character\pt1
                EndIf
            EndIf

        Case 2
; Path with Pursuit
            If EntityDistance( Character\ID, target )<20
                PointEntity Character\ID, target
                MoveEntity Character\ID, 0, 0, Rnd( .1, .19 )
                AnimSprite( Character\ID, Character\char_tex, 200, 4 )
            Else
                PointEntity Character\ID, Character\targetpt
                MoveEntity Character\ID, 0, 0, Rnd( .05, .1 )
                AnimSprite( Character\ID, Character\char_tex, 200, 4 )
                If EntityDistance( Character\ID, Character\targetpt ) < 1
                    If Character\targetpt = Character\pt1 Then
                        Character\targetpt = Character\pt2
                    Else Character\targetpt = Character\pt1
                    EndIf
                EndIf
            EndIf

        End Select
    Next
End Function
. . .


NEW COMMANDS
lesson6.bb
; Nothing New!

loadworld6.bb

Type Sprite
    Field ID, char_tex
    Field pt1, pt2 ; Path endpoints
    Field targetpt
    Field temper

End Type

For C=1 To 3
    Character.Sprite = New Sprite
    Character\ID = CreateSprite()
    ; add temper + destination variables
    Read temper, img$, X, Y, Z, X2, Y2, Z2
    Character\char_tex = LoadAnimTexture( img$, 7, 32, 48, 0, 4 )
    EntityTexture Character\ID, Character\char_tex, 1
    HandleSprite Character\ID, 0, -1
    ScaleSprite Character\ID, 2, 2
    EntityAutoFade Character\ID, 100, 120
    PositionEntity Character\ID, X, Y, Z

    Character\temper = temper
    Character\pt1 = CreatePivot() : Character\pt2 = CreatePivot()
    PositionEntity Character\pt1, X, Y, Z
    PositionEntity Character\pt2, X2, Y2, Z2
    Character\targetpt = Character\pt2

Next

.Characters
; add temper 0=still, 1=path, 2=path + track AND destination X2, Y2, Z2
Data 0, "char2.bmp", 30, 0, 30, 30, 0, 0
Data 1, "char3.bmp",-30, 0, 30, 20, 0, 30
Data 2, "char4.bmp", 0, 0, 30, -30, 0, 0
. . .

To make our character sprites appear "smart" we need to add some more features to our SPRITE TYPE. Field pt1, pt2 will hold both endpoint markers (called pivots). The first marker will be the regular starting point where our character is normally positioned when loaded. Field targetpt will be a temporary placeholder for whichever 1 of the 2 pivots is the next target. Field temper will hold the characters A.I. "temper" value of 0,1,or 2. We could also rename this variable as "aggressiveness" or "mood" or "alliance" or "intelligence".

Read temper, img$, X, Y, Z, X2, Y2, Z2 . . .
loads these new values from our expanded Data 0, "char2.bmp", 30, 0, 30, 30, 0, 0
Character\temper = temper
loads the temper data value into our character type
Character\pt1 = CreatePivot() : Character\pt2 = CreatePivot()
PositionEntity Character\pt1, X, Y, Z
PositionEntity Character\pt2, X2, Y2, Z2
creates the 2 path endpoint pivots for our character and positions them
Character\targetpt = Character\pt2

sets the first path target as the 2nd pivot
EXPERIMENT !
In the Data try changing all 3 characters to the same "temper"

function6.bb

. . .
Function MoveCharacters(target)
    For Character.Sprite = Each Sprite
    Select Character\temper

        Case 0
; Static
            ; Do Nothing or maybe run idle animation?

        Case 1
; Path
            PointEntity Character\ID, Character\targetpt
            MoveEntity Character\ID, 0, 0, Rnd( .05, .1 )
            AnimSprite( Character\ID, Character\char_tex, 200, 4 )
            If EntityDistance( Character\ID, Character\targetpt ) < 1
                If Character\targetpt = Character\pt1 Then
                    Character\targetpt = Character\pt2
                Else Character\targetpt = Character\pt1
                EndIf
            EndIf

        Case 2
; Path with Pursuit
            If EntityDistance( Character\ID, target ) < 20
                PointEntity Character\ID, target
                MoveEntity Character\ID, 0, 0, Rnd( .1, .19 )
                AnimSprite( Character\ID, Character\char_tex, 200, 4 )
            Else
                PointEntity Character\ID, Character\targetpt
                MoveEntity Character\ID, 0, 0, Rnd( .05, .1 )
                AnimSprite( Character\ID, Character\char_tex, 200, 4 )
                If EntityDistance( Character\ID, Character\targetpt ) < 1
                    If Character\targetpt = Character\pt1 Then
                        Character\targetpt = Character\pt2
                    Else Character\targetpt = Character\pt1
                    EndIf
                EndIf
            EndIf

        End Select
    Next
End Function
. . .

Our newly modified MoveCharacters( target ) function will LOOP through each of our sprite types using For Character.Sprite = Each Sprite . . . Next and then we will use the Character\temper variable to Select the Case for our available tempers 0,1, or 2.

Case 0 does nothing because it is our quiet wallflower temperment.

Case 1 will point our character toward the current target pivot using PointEntity Character\ID, Character\targetpt . It is then moved forward with a slightly randomized distance and the sprite animation strip is updated. If EntityDistance( Character\ID, Character\targetpt ) < 1 checks if the Character is close to the path target pivot and if so, the remaining code switches the target pivot to the opposite one.

Case 2 is similar to the previous but now it checks if it is within 20 units from its target (you the player!) using If EntityDistance( Character\ID, target ) < 20 . If so it points and moves the character towards the player and updates its animation. If not ( Else ) it does just what the previous case did, i.e. moving it toward the target pivot and switching target pivots if necessary.
EXPERIMENT !
In Case 2 change the pursuit distance and observe the effect


THE CHALLENGE!!!
Add up to 3 more of your own characters to loadworld6.bb

NEXT : Lesson 7 - Collisions