Code archives/3D Graphics - Misc/Finite State Machine (FSM)

This code has been declared by its author to be Public Domain code.

Download source code

Finite State Machine (FSM) by kevin80842006
This is just a simple example of a Finite State Machine. It is primitive but is meant to be easy to understand.
; **************************************************************************************************
; Finite State Machine tutorial by Kevin Lee Legge (kevin8084@gmail.com)
; **************************************************************************************************
;
; ************************************** DEFINITION ************************************************
;
; A Finite State Machine (FSM) is an artificial intelligence method where the AI is in a series
; of "states". For game playing some of the most common states are: IDLE, HUNGRY, SEARCHING
; ATTACKING, SUPPORTING, RUNNING, DYING.
; There are two stages in the FSM - the "transistion" stage and the "behavior" stage.
; The transistion stage is where the AI's state is checked to see if the AI is in transistion
; from one state to another. That is, if the AI is in the IDLE state and is being attacked, then
; its state will be changing to ATTACKING or RUNNING or, perhaps, DYING.
; The behavior stage is where the action is. This part of the code instructs the AI on
; what to do according to what state it's currently in. For example, if the AI is currently in the
; SEARCHING state, then the behavior stage calls whatever searching functions are set up in the
; code.
; There are currently two schools of thought regarding FSM. The first regards the Behavior stage as
; completely adequate by itself, without bothering with any transistions. The second regards
; the transistion AND behavior stages as part of a whole.
; I, personally, subscribe to the idea that the FSM must have both transistion and behavior stages.
;
; **************************************************************************************************
Graphics3D 800,600
SetBuffer BackBuffer()

SeedRnd MilliSecs()

; **************************************************************************************************
;                                          CONSTANTS
; **************************************************************************************************

Const IDLE      = 0
Const SEARCHING = 1
Const ATTACKING = 2
Const DYING     = 3

Const PLAYER_TYPE = 1
Const ENEMY_TYPE  = 2
Const CUBE_TYPE   = 3
Const GROUND_TYPE = 4


; **************************************************************************************************
;                                            TYPES
; **************************************************************************************************

Type enemy
	Field entity	  ; the AI's mesh
	Field state		  ; what state the AI is currently in
	Field life#		  ; how much life the AI has
	Field range#	  ; the AI's search range
	Field bad.player  ; use to store handle of AI's enemy (ie., the player)
	Field x#,y#,z#	  ; the AI's location
	Field vx#,vy#,vz# ; the AI's velocity
End Type

Type player
	Field entity
	Field x#,y#,z#
End Type

; **************************************************************************************************
;                                      GLOBAL VARIABLES
; **************************************************************************************************

Global age	; use to age the enemy so that it transistions into its DYING state
Global alpha# = 1
Global eState$ ; holds enemy's state for printing purposes

; Create the ground
Global plane = CreatePlane()
EntityType plane,GROUND_TYPE
EntityColor plane,85,85,85
PositionEntity plane,0,0,0

; Create the enemy
Global enemy.enemy = New enemy
enemy\entity = CreateSphere()
EntityRadius enemy\entity,1
EntityType enemy\entity,ENEMY_TYPE
EntityShininess enemy\entity,1
enemy\x# = Rnd(-200,200)
enemy\y# = 2
enemy\z# = Rnd(-200,200)
enemy\state = IDLE
enemy\life# = 100.0
enemy\range# = 50.0
age = MilliSecs()
PositionEntity enemy\entity,enemy\x#,enemy\y#,enemy\z#

; Create the player
Global player.player = New player
player\entity = CreateCamera()
EntityRadius player\entity,1
EntityType player\entity,PLAYER_TYPE
player\x#=0
player\y#=2
player\z#=0
PositionEntity player\entity,player\x#,player\y#,player\z#

; Create the light
Global light = CreateLight()
PositionEntity light,100,100,100
RotateEntity light,90,0,0

; Populate the world with some cubes
For c=1 To 25
	cube=CreateCube()
	EntityColor cube,0,87,0
	EntityType cube,CUBE_TYPE
	PositionEntity cube,Rand(-200,200),1,Rand(-200,200)
Next
HidePointer

Collisions ENEMY_TYPE,CUBE_TYPE,2,2
Collisions ENEMY_TYPE,GROUND_TYPE,2,2
Collisions PLAYER_TYPE,GROUND_TYPE,2,21
Collisions PLAYER_TYPE,CUBE_TYPE,2,2

MoveMouse GraphicsWidth()/2,GraphicsHeight()/2

While Not KeyHit(1)
If KeyDown(200) Then MoveEntity player\entity,0,0,.3
If KeyDown(208) Then MoveEntity player\entity,0,0,-.3
If KeyDown(203) Then TurnEntity player\entity,0,1,0
If KeyDown(205) Then TurnEntity player\entity,0,-1,0

TranslateEntity player\entity,0,-1,0 ; give player some gravity
If enemy <> Null Then TranslateEntity enemy\entity,0,-1,0 ; same with the enemy

mxs#=-MouseXSpeed()*.25
mys#=MouseYSpeed()*.25
MoveMouse GraphicsWidth()/2,GraphicsHeight()/2
TurnEntity player\entity,mys#,mxs#,0
RotateEntity player\entity,EntityPitch#(player\entity),EntityYaw#(player\entity),0
If EntityPitch#(player\entity)>80 Then RotateEntity player\entity,80,0,0
If EntityPitch#(player\entity)<-80 Then RotateEntity player\entity,-80,0,0

FSM_Transistion() ; check to see if there are any state transistions
FSM_Behavior()    ; now do the code, depending on the state the AI is in

UpdateWorld
RenderWorld
Text 0,0,"I'm "+eState$
Flip
Delay 10
Wend
FreeEntity player\entity
Delete player
End


; **************************************************************************************************
;                                             FUNCTIONS
; **************************************************************************************************

Function FSM_Transistion()
; this function determines whether or not the AI is in transistion from one state to another
For this.enemy = Each enemy ; iterate through each enemy AI
	Select this\state
		Case IDLE           
			eState$="Idle"
			EntityColor this\entity,0,255,0
			p.player = getEnemyDistance(this) ; check to see if player is in sensory range
			If p <> Null  Then                ; we see the player!
				this\state = ATTACKING        ; we transistion to the attack state
				this\bad = p                  ; make sure that we store player internally
			ElseIf MilliSecs() > age+50000 Then ; are we about to die of old age (50 seconds)?
				this\state = DYING				; yes? Then transistion to dying state
				age = MilliSecs()               ; not needed, really, but what the heck :)
			End If
		Case SEARCHING
			EntityColor this\entity,2550,0,255
			p.player = getEnemyDistance(this) ; again, check to see if player is nearby
			If p <> Null Then                 ; yes? 
				this\state = ATTACKING        ; then transistion to attack state
				this\bad = p
			ElseIf MilliSecs() > age+50000 Then ; are we dying of old age?
				this\state = DYING              ; yep...then we transistion
				age = MilliSecs()
			End If
		Case ATTACKING
			EntityColor this\entity,255,0,0
			p.player = getEnemyDistance(this)   ; even though we are in attacking state, we need to
										        ; make sure that player is still in range
			If p <> Null Then                   ; yes?
				this\state = ATTACKING          ; Houston, we have liftoff...we attack!
				this\bad = p
			ElseIf MilliSecs() > age+50000 Then ; else, are we dying?
				this\state = DYING              ; if so, then die already!
				age = MilliSecs()
			Else
				this\state = SEARCHING          ; else let's go to the searching state
				this\bad = Null                 ; no bad player here, so make it null
			End If
		Case DYING                              ; we are dying
			EntityColor this\entity,45,45,45
	End Select
Next
End Function

Function FSM_Behavior()
; this function reroutes according to which state the AI is in
For this.enemy = Each enemy ; iterate through the enemy
	Select this\state
		Case IDLE
			this\state = SEARCHING ; idle is so boring...let's search, instead
		Case SEARCHING
			patrol(this) ; do our patrol
		Case ATTACKING
			attack(this) ; attack!
		Case DYING
			die(this)    ; well, it's been real
			alpha#=alpha# - .001 ; this is to fade out the enemy
	End Select
Next

End Function

Function patrol(e.enemy)
; put patrol code here
eState$ = "patrolling"
; let's have the enemy move around a little bit
MoveEntity e\entity,0,0,.2
If Rand(255)=17 Then RotateEntity e\entity,0,EntityYaw#(e\entity)+5,0
End Function

Function attack(e.enemy)
; put attack code here
eState$ = "attacking"
; just to show how to implement the attack state
; this is NOT my normal attack function! :)
PointEntity e\entity,player\entity
MoveEntity e\entity,0,0,.2
End Function

Function die(e.enemy)
; we are dying now
eState$ = "dying"
EntityAlpha e\entity,alpha#
If alpha# <= 0 Then
	FreeEntity e\entity
	Delete e
	eState$="Nothing...no more enemies left"
End If
End Function

Function getEnemyDistance.player(e.enemy)
; function to determine if player is in AI's sensory range
For p.player = Each player ; iterate through all the players
	If p <> Null Then      ; hey, do we have a player?
		If EntityDistance#(e\entity,p\entity)<=e\range# Then ; yep...is he/she in range?
			Return p   ; yes? Then return the player
		End If
	End If
Next
End Function

Comments

markcw2006
another cracking code entry! thanks, very useful.


kevin80842006
You're very welcome


Boiled Sweets2006
Awesome


Code Archives Forum