Code archives/3D Graphics - Misc/Blitz3D Game Framework

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

Download source code

Blitz3D Game Framework by Festay2009
Yet another game framework - This time for Blitz3D.

Similar to the framework I posted for the Blitz3DSDK + Blitzmax, this example provides a basic game object and functions that handle game initialisation and running of the main loop (including built in render tweening).
The slick thing about this example is the use of the FastPointer library to make the framework as modular as possible. The example includes a GameComponent type that simply holds a set of function pointers to be called in the main loop - Want something to happen? simply instantiate a new GameComponent object, provide the functions that do the actual work, provide the function pointers and the framework will do the rest.

The example does not contain any comments but should still be fairly easy to understand - The code is fairly straight forward and I have declared all variables using descriptive names.

Please post any constructive criticism - I would appreciate any help in making this better - In particular if anyone has any ideas how to implement a system for controlling GameComponent state e.g. The ability to make one component active/inactive from another. I'm not sure how to handle this at the moment due to the way Blitz3D types work. I'm probably overlooking something simple.

## Important
Requires the (free) FastPointer library to implement the function callback mechanism. the FastPointer library can be downloaded from http://fastlibs.com/
If you do not have this userlib installed you will get the famous "userlib not found" error message when attempting to run this example.
## Important
Type TGame

	Field Timer.TGameTime
	Field FramesPerSecond%
	Field AnimationSpeed#
	Field Components.TGameComponent
	Field IsRunning%
	Field GraphicsMode.TGraphicsMode 
	Field WindowMode%
	
End Type

	Const GFXMODE_FULLSCREEN = 1
	Const GFXMODE_WINDOWED = 2

	Function CreateGame.TGame(gfx.TGraphicsMode,title$,windowMode=GFXMODE_WINDOWED,FPS%=60)
	
		AppTitle title
		HidePointer()
		
		Graphics3D(gfx\Width,gfx\Height,gfx\Depth,windowMode)
		SetBuffer(BackBuffer())
		
		Local G.TGame = New TGame
			G\GraphicsMode = gfx
			G\WindowMode = windowMode
			G\FramesPerSecond = FPS
			G\AnimationSpeed = 1
			G\Timer = CreateGameTime(FPS)
			Return G
			
	End Function

	Function RunGame(game.TGame)
	
		Local i
		
		; Initialise game components --------------------------------------
		For game\Components = Each TGameComponent 
			CallFunction(game\Components\Initialise) 
		Next
		
		; Start the main game loop ----------------------------------------
		game\IsRunning = True
		
		While game\IsRunning = True
		
			UpdateGameTime(game\Timer)
			
			For i=1 To game\Timer\Ticks
				UpdateFrameTime(game\Timer)
				If i=game\Timer\Ticks Then
					CaptureWorld()
				End If
				For game\Components = Each TGameComponent 
					CallFunction(game\Components\Update)
				Next
				UpdateWorld(game\AnimationSpeed)
			Next
			RenderWorld(game\Timer\Tween)
			
			UpdateRemainingTime(game\Timer)
			If game\Timer\RemainingTime > 1 Then 
				Delay (game\Timer\RemainingTime-1)
			End If
			
			For game\Components = Each TGameComponent
				CallFunction(game\Components\Draw)
			Next
			
			VWait
			Flip(False)
			
		Wend
		
		; Dispose game components ----------------------------------------
		For game\Components = Each TGameComponent
			CallFunction(game\Components\Dispose)
		Next
		
	End Function

	Function DisposeGame(game.TGame)
		Delete(game\Timer)
		For game\Components = Each TGameComponent 
			Delete game\Components 
		Next	
		Delete(game)
		ClearWorld()
		End
	End Function

;-------------------------------------------------------------------------

Type TGameTime

	Field Period%
	Field FrameTime%
	Field StartTime%
	Field ElapsedTime%
	Field Ticks%
	Field Tween#
	Field RemainingTime%
	
End Type

	Function CreateGameTime.TGameTime(FPS%=60)
		Local t.TGameTime = New TGameTime 
			t\Period = 1000 / FPS 
			t\FrameTime = MilliSecs() - t\Period 
			Return t
	End Function

	Function UpdateGameTime(GT.TGameTime)
		GT\StartTime = MilliSecs()
		GT\ElapsedTime = MilliSecs() - GT\FrameTime
		GT\Ticks = (GT\ElapsedTime) / (GT\Period)
		GT\Tween = Float(GT\ElapsedTime Mod GT\Period) / Float(GT\Period)
	End Function

	Function UpdateFrameTime(GT.TGameTime)
		GT\FrameTime = GT\FrameTime + GT\Period
	End Function

	Function UpdateRemainingTime(GT.TGameTime)
		GT\RemainingTime = GT\Period - (MilliSecs() - GT\StartTime)
	End Function

;-------------------------------------------------------------------------
	
Type TGameComponent
	Field Initialise
	Field Update
	Field Draw
	Field Dispose
End Type

;-------------------------------------------------------------------------

Type TGraphicsMode
	Field Width
	Field Height
	Field Depth
End Type

;-------------------------------------------------------------------------
; Main program

Const KEY_ESCAPE = 1
Const KEY_ENTER = 28

Global gfxMode.TGraphicsMode = New TGraphicsMode 
	gfxMode\Width = 800
	gfxMode\Height = 600
	gfxMode\Depth = 32

Global ExampleGame.TGame = CreateGame(gfxMode,"Game Framework Demo")

Global cam = CreateCamera()
Global Cube = CreateCube()
Global Light = CreateLight()

CreateSpinningCubeComponent(ExampleGame)
CreatecolorChangerComponent(ExampleGame)
RunGame(ExampleGame)
DisposeGame(ExampleGame)

;-------------------------------------------------------------------------
; Spinning cube component
Function CreateSpinningCubeComponent(g.TGame)
	
	g\Components = New TGameComponent
	
	g\Components\Initialise = FunctionPointer()
		Goto skipInitialise
		initialiseSpinningCube()
		.skipInitialise
	
	g\Components\Update = FunctionPointer()
		Goto skipUpdate
		UpdateSpinningCube()
		.skipUpdate
	
	g\Components\Draw = FunctionPointer()
		Goto skipDraw
		DrawSpinningCube()
		.skipDraw
		
	g\Components\Dispose = FunctionPointer()
		Goto SkipDispose
		DisposeSpinningCube()
		.SkipDispose
	
End Function

Function initialiseSpinningCube()
	CameraClsColor(cam,100,149,237)
	PositionEntity(Cube,0,0,5)
End Function

Function UpdateSpinningCube()
	If KeyHit(KEY_ESCAPE) Then ExampleGame\IsRunning = False
	TurnEntity(Cube,0.1,0.2,0.3)
End Function

Function DrawSpinningCube()
	Text(10,10,"Cube Spinner Component Loaded.")
End Function

Function DisposeSpinningCube()
	FreeEntity(Cube)
	FreeEntity(Light)
End Function

;-------------------------------------------------------------------------
; Color changer component.
Function CreatecolorChangerComponent(g.TGame)
	
	Local C.TGameComponent = New TGameComponent 
	
	C\Initialise = FunctionPointer()
		Goto skipInitialise 
		initialiseColorChanger()
		.skipInitialise
	
	C\Update = FunctionPointer()
		Goto skipUpdate
		UpdateColorChanger()
		.skipUpdate
	
	C\Draw = FunctionPointer()
		Goto skipDraw
		DrawColorChanger()
		.skipDraw
		
	g\Components = C	
	
End Function

Function initialiseColorChanger()
	SeedRnd(MilliSecs())
	EntityColor(Cube,Rand(0,255),Rand(0,255),Rand(0,255))
End Function

Function UpdateColorChanger()
	If KeyHit(KEY_ENTER) EntityColor(Cube,Rand(0,255),Rand(0,255),Rand(0,255))
End Function

Function DrawColorChanger()
	Text(10,20,"Cube color changer component loaded")
End Function

Comments

Charrua2009
interesting!
i've thinking a way to improve the clasic Case struct we tend to use to implement a state machine for the "Components" behavior, but till FuntionPoninter apear to life i didn't found a good way to implement it. The idea is at first the same your are using. A simple call to a function via pointer no mather wich funtion is in fact called. So as the call uses a variable, the same call can call diferents functions, in my planning : a case with out cases: when a component's state is to be moved to another state it simply change the pointer to a function that defines the new state, in place to change the variable that controls the case struct.

i know that my post dont answer your request but seems to me that a framework to implement a case using poninter should complement your work.

to control a Component from another one in a general way there should be a general function to search for a particular kind of Component and for that component a do a call to a Particular function Behavior or StateChange or Control (the name is not relvant). So only certain Components (ID, Group or all of them) receive the command.


I should add a field ID and a field Group

when created, the ID field should be initialized:
g\ID = Handle(g) ;and returned by the function
g\Group = group (should be a parameter pased to the fun that creates the component)

function CreateComponentBla(g.TGame, Group=0)
 g.TGameComponent = new TGameComponent
 g\ID = Handle(g)
 g\Group = Group

 ;rest of creation code

 return g\ID
end function

global C1 = CreateComponentBla(ExampleGame,1) ; Group=1

having C1 equals a handle, then you could:

c.TGameComponent = Object.TGameComponent(C1)

(Both Handle and Object are vastly used but Undocumented!)

in this way you get the instance of the object with out to do a search for each object in the list!

I use a general Control(Type, ID, Action, Value) function
Type says the kind of object to act on (in this case always TGameComponent), ID should be 0 for all, a negative number (the minus Group) to act on a group and a positive one to go stright to the object. When I reach the one or the ones i do a call to a function based on Action and pas Value to that function.
some thing like:

function Control(ID, Action, Value)
  if sgn(ID) then
     c.TGameComponent = Object.TGameComponent(ID)
     Select Action
       Case 1
          call c\Wathever(Value)
       Case 2
       ....
     End Select
  else
    for c.TGameComponent = each TGameComponent
     If ID=0 Or ((Sgn(ID)=-1) And (c\Group = Abs(ID)) )Then
       ;call all if ID=0 or only who's group is indicated
       ;you have to pass a -Group to act on a group!
       Select Action
         Case 1
            call c\Wathever(Value)
         Case 2
         ....
       End Select
     end if
    next
  end if

end function


so if you do
Control(0,1,5) ;you call Function Case 1 with value 5 to all the components.
Control(-2,3,True) ; you call to all the components of the group 2, with the function case 3 with value True
but if you call:
Control(C1,2,-3) : you only control one component: the component ho's ID you have stored in C1

hope i expresed my self, my english is not so good! sorry

Juan


Code Archives Forum