home  forum  showcase  royalty-free  sign in  register   
 
   Forum forum >> BlitzCoder >> Faked Object-Oriented Programming

Recent discussions

Sound Effects Events by megaphilx on Thu 6 Jan 2011 at 21:49:11
Latest updates by Heirauqs on Wed 5 Jan 2011 at 07:29:10
Bugs list by megaphilx on Wed 5 Jan 2011 at 03:46:36
What does PHP stand for? by sgvisual on Tue 23 Nov 2010 at 23:37:58
MegaPhilX To-Do List by megaphilx on Sat 13 Nov 2010 at 00:19:46
On-Topic Break Time by gosse on Fri 12 Nov 2010 at 17:58:47
gab to do list by Heirauqs on Fri 10 Sep 2010 at 07:37:11
gosse to do list by gosse on Fri 10 Sep 2010 at 06:27:45
Master Task List and Status by Heirauqs on Thu 9 Sep 2010 at 17:38:19
Borderlands Tweaks by gosse on Wed 28 Oct 2009 at 02:24:25

Back to BlitzCoderPage 1

 Faked Object-Oriented Programming on Sun 10 Aug 2008 at 01:16:27
 

gosse
  Administrator
indieground.com
Posts: 89



Joined: Apr 2006
Location: Montreal, QC, CA

 

(reposted from Nuloen forum - Feb 13, 2006)
Faked Object-Oriented Programming
GoSsE's Advanced Blitz3D Tutorial - #001
Version 1.0.0 - January 15th 2006


Introduction
Blitz3D's structure doesn't natively include an object-oriented approach to programming, which is really helpful in order to create abstraction, simpler code, more readable code and more efficient programming, in terms of redundacy and modularity. It does however, induce function call overheads, which slightly reduce performance but, this trade off is really acceptable for all the advantages OOP (Object-Oriented Programming) brings.
This tutorial will cover the several aspects of how to achieve OOP in the Blitz3D development enviroment. It will cover how to handle the structure in Blitz3D, having basic properties, methods (including constructors and destructors), and applying inheritance and polymorphism.

Creating Objects
In order to create objects in Blitz3D, we rely on Type structures, which is the core part of the objects, containing properties (also called members) and informations for the system to know how to handle the object itself.

Example Step 1 - The Game Object
We will create a Game Object which will be the basis for a game element. Like Java, we will use a one-object-one-file convention, hence creating the file "GameObject.bb" for our source file. Also, we will use a variable convention for the members; a simple prefix letter for the type of the member (h for handle, b for boolean, i for integer, f for float, s for string, etc).
Code


; Basic game object
Type GameObject
Field hEntity%
Field bDisabled%
End Type


All game objects will contain with a 3D entity and information if its currently disabled or not. Those are the only members we will keep for now, but in a complex game engine, they could contain a lot more information such as a velocity vector, a weight, game-related data, etc.

Handles
In order to deal with the objects, we will use Blitz3D's way of dealing with them, that is, by handles. A handle is an integer representing the address in memory of the object. This will give us the option of not having to declare the objects as the proper type and will allow us the usage of polymorphism, which will be discussed later on.
All methods (function calls) will be standard global functions, but using a prefix with the name of the object and a following underscore. The arguments (more widely known as parameters) will always include the handle of the object, except the constructor, which will return the handle for the newly created object.
In order to access and modify members of the object, we will use functions (normally get<member> and set<member> in order to prevent border effects, that is, changing data outside the object, without the object knowing, defeating the purpose of abstraction.
In order to use handles, we will be using Blitz3D's internal commands which are Handle(<instance>) and Object.<type>(<handle>). Essentially, the constructor will return the handle of the internally created instance of the type and all other methods will use the handle, and dereference it to the object. If you are not familiar with using those hidden commands, it is recommended to read up on them.

Adding Constructors and Destructors
In OOP, methods are simply functions which are encapsulated by the object itself and only apply to it. The basic methods which makes an object an object are the constructor and the destructor.
A constructor is basically a function which will put everything in place for the object to be complete as we want it when we create a new instance.
A destructor is basically a function which will clean everything used by the object in order to prevent introducing memory leaks.

Example Step 2 - Constructor and Destructor
We will add the two methods to be able to create and destroy game objects.
Code


; Create new game object instance
Function GameObject_Constructor%()
Local oNewGameObject.GameObject = New GameObject
oNewGameObject\bDisabled = True
Return Handle(oNewGameObject)
End Function

; Free entity, delete game object instance
Function GameObject_Destructor(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
; Verify and free entity
If Not(oGameObject\hEntity = 0) Then
FreeEntity oGameObject\hEntity
End If
Delete oGameObject
End Function


Basically, the constructor only creates the instance, sets the object disabled by default and returns the handle. The destructor dereferences the handle, verifies if there's an entity, if there is, it frees it and deletes the instance.

Adding Other Methods
Once we have our object defintion and standard methods, we can now add the object specifics methods.

Example Step 3 - Specific Methods
Code


; Get the entity/mesh handle
Function GameObject_GetEntity%(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
Return oGameObject\hEntity
End Function

; Set the entity/mesh
Function GameObject_SetEntity(hGameObject%, hEntity%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
oGameObject\hEntity = hEntity
End Function

; Is the object disabled?
Function GameObject_IsDisabled%(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
Return oGameObject\bDisabled
End Function

; Disable and enable the game object
Function GameObject_SetDisabled(hGameObject%, bDisable%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
oGameObject\bDisabled = bDisable
End Function

; Update (game loop)
Function GameObject_Update(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
End Function


What we added at this step is functions to access and modify the members of the game object, Get/Set functions for the entity (3D model, or else), Get/Set for the disabled state and also and update, which is empty for now, for the game loop. Those methods will give us complete control to the object properly.

Introducing Inheritance and Polymorphism
If you are not familiar with inheritance and polymorphism in OOP, it is recommended to read up on the subject to be familiar with it. Put simply, inheritance is that we create a child object derived from a parent object, and this child inherits members and methods from the parent. Polymorphism is the ability to use the same function on different types of objects, without knowing the type of object in advance. Technically, we wouldn't need polymorphism for what we want to do, because it is basically simple inheritance, but with our faked OOP in Blitz3D, we have to rely on polymorphism for parent object methods used by the child.

Example Step 4 - Creating Child Types
We will add 2 child types to the Game Object parent type: player and pick-up.
Code


; Player object
Type Player
Field oGameObject.GameObject
Field iHealth
Field hHead
End Type

; PickUp object
Type PickUp
Field oGameObject.GameObject
Field iHealthBonus
End Type


We have created the 2 child types, the player, which has a health value and a head entity, and the pick-up, which only includes a health bonus. Both objects contains a reference to the parent object that will contain the data for the generic game object (the entity and disabled boolean) for the specific instance.
Now, we now need to modify the parent type to be able to receive those children.

Example Step 5 - Modifying Game Object to Receive Children
Code


; Game object type
Const TYPE_PLAYER = 1
Const TYPE_PICKUP = 2

; Basic game object
Type GameObject
Field hEntity%
Field bDisabled%
Field iType%
Field hTypeData%
End Type


We have added the 2 constants that will help us define the types and to know which type the object is when we need to access it. We also added 2 members, one defining the type, and the other one containing the handle for the data depending from the type.
Now, we'll add the constructors for the children and modify the parent's destructor to react properly. In a real OOP language, we would only overload the parent's function, but in Blitz3D's case, we will have to rely on polymorphism, that is, having a single function for all types, which will act accordingly. The children types won't have a proper destructor, they will instead rely on the parent's destructor method.

Example Step 6 - Constructors and Destructor
Code


; Create a new player
Function Player_Constructor%(iHealth% = 5)
Local oNewPlayer.Player = New Player
Local hGameObject = GameObject_Constructor()
Local oGameObject.GameObject = Object.GameObject(hGameObject)
oNewPlayer\oGameObject = oGameObject
ONewPlayer\iHealth = iHealth
oNewPlayer\hHead = CreateCube()
ScaleEntity oNewPlayer\hHead, 0.2, 0.2, 0.2
MoveEntity oNewPlayer\hHead, 0, 1.1, 0
GameObject_SetEntity(hGameObject, CreateCube())
oGameObject\iType = TYPE_PLAYER
oGameObject\hTypeData = Handle(oNewPlayer)
GameObject_SetDisabled(hGameObject, False)
Return hGameObject
End Function

; Create a new pick-up
Function PickUp_Constructor%(iHealthBonus% = 1)
Local oNewPickUp.PickUp = New PickUp
Local hGameObject = GameObject_Constructor()
Local oGameObject.GameObject = Object.GameObject(hGameObject)
oNewPickUp\oGameObject = oGameObject
ONewPickUp\iHealthBonus = iHealthBonus
GameObject_SetEntity(hGameObject, CreateSphere())
ScaleEntity oGameObject\hEntity, 0.3, 0.3, 0.3
oGameObject\iType = TYPE_PICKUP
oGameObject\hTypeData = Handle(oNewPickUp)
GameObject_SetDisabled(hGameObject, False)
Return hGameObject
End Function

; Destroy a game object of any type
Function GameObject_Destructor(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
Local oPlayer.Player
Local oPickUp.PickUp
; Verify and free entity
If Not(oGameObject\hEntity = 0) Then
FreeEntity oGameObject\hEntity
End If
; Destroy according from type
Select oGameObject\iType
; Player destruction
Case TYPE_PLAYER
oPlayer = Object.Player(oGameObject\hTypeData)
FreeEntity oPlayer\hHead
Delete oPlayer
; Pick-up destruction
Case TYPE_PICKUP
oPickUp = Object.PickUp(oGameObject\hTypeData)
Delete oPickUp
; Other types?
Default
RuntimeError "Game Object Error: Type#" + oGameObject\iType + "doesn't exist or function not implemented."
End Select
Delete oGameObject
End Function


Both constructors only create their instance, assign properties and create the appropriate mesh. They both use their parent's constructor within them to initialize the parent type correctly. The destructor is the same for every type, but only each type has special instructions to do on destruction.
We will now modify the parent's update function to suit our needs.

Example Step 7 - Update Function
Code


; Update (game loop)
Function GameObject_Update(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
Local oPlayer.Player
Local oPickUp.PickUp
Local iTurn%, fMove#
If Not(oGameObject\bDisabled) Then
; Type update
Select oGameObject\iType
; Player update
Case TYPE_PLAYER
oPlayer = Object.Player(oGameObject\hTypeData)
iTurn = (Keydown(203) - Keydown(205))*3
fMOve = (Keydown(200) - Keydown(208))*0.25
TurnEntity oPlayer\hHead, 0, iTurn, 0
TurnEntity oGameObject\hEntity, 0, iTurn, 0
MoveEntity oPlayer\hHead, fMove, 0, 0
MoveEntity oGameObject\hEntity, fMove, 0, 0
; Pick-up update
Case TYPE_PICKUP
oPickUp = Object.PickUp(oGameObject\hTypeData)
TurnEntity oGameObject\hEntity, 0, 1, 0
MoveEntity oGameObject\hEntity, 0, Sin(Millisecs())*0.1, 0
; Other types?
Default
RuntimeError "Game Object Error: Type#" + oGameObject\iType + " doesn't exist or function not implemented."
End Select
End If
End Function


We now have a fully working set of objects with their proper members and methods, using a high level of abstraction, inheritance and polymorphism. It might still be missing a few methods, but they will be added in the source code in the Annex. All is left to do now is to operate the objects, and this is all included in the full source code included in the Annex.

Conclusion
It is entirely possible and feasible to fake OOP with Blitz3D, using strict standards and methodology. It takes a little longer to code our core objects, but once it is done, programming the game's logic flows a lot better. It is also a great asset for your users when you code and distribute a library.
The source code exhibited in the Annex shows a simple implementation and not everything is encapsulated within the objects. As an exercice, try to modify the source code so that the user (outside the internal type, inside the game's code) never accesses internal data to the objects directly. Hint: The user shouldn't use (Player_GetHealth(hPlayer) <= 0) but rather a Player_IsAlive(hPlayer) method, and he really shouldn't use the entities but rather have a method to do the checks for the user.
If you have anymore questions, comments, suggestions or tips, don't hesitate to send me a mail at gosse@nuloen.com.

Annex
This annex contains useful information regarding this tutorial, such as the complete source code to the example, links about programming, OOP and Blitz3D, copyright information and the modification history.

Final Example Code

Code


; Game object type
Const TYPE_PLAYER = 1
Const TYPE_PICKUP = 2

; Basic game object
Type GameObject
Field hEntity%
Field bDisabled%
Field iType%
Field hTypeData%
End Type

; Player object
Type Player
Field oGameObject.GameObject
Field iHealth
Field hHead
End Type

; PickUp object
Type PickUp
Field oGameObject.GameObject
Field iHealthBonus
End Type

; Create new game object instance
Function GameObject_Constructor%()
Local oNewGameObject.GameObject = New GameObject
oNewGameObject\bDisabled = True
Return Handle(oNewGameObject)
End Function

; Get the entity/mesh handle
Function GameObject_GetEntity%(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
Return oGameObject\hEntity
End Function

; Set the entity/mesh
Function GameObject_SetEntity(hGameObject%, hEntity%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
oGameObject\hEntity = hEntity
End Function

; Is the object disabled?
Function GameObject_IsDisabled%(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
Return oGameObject\bDisabled
End Function

; Disable and enable the game object
Function GameObject_SetDisabled(hGameObject%, bDisable%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
oGameObject\bDisabled = bDisable
End Function

; Create a new player
Function Player_Constructor%(iHealth% = 5)
Local oNewPlayer.Player = New Player
Local hGameObject = GameObject_Constructor()
Local oGameObject.GameObject = Object.GameObject(hGameObject)
oNewPlayer\oGameObject = oGameObject
ONewPlayer\iHealth = iHealth
oNewPlayer\hHead = CreateCube()
ScaleEntity oNewPlayer\hHead, 0.2, 0.2, 0.2
MoveEntity oNewPlayer\hHead, 0, 1.1, 0
GameObject_SetEntity(hGameObject, CreateCube())
oGameObject\iType = TYPE_PLAYER
oGameObject\hTypeData = Handle(oNewPlayer)
GameObject_SetDisabled(hGameObject, False)
Return hGameObject
End Function

; Create a new pick-up
Function PickUp_Constructor%(iHealthBonus% = 1)
Local oNewPickUp.PickUp = New PickUp
Local hGameObject = GameObject_Constructor()
Local oGameObject.GameObject = Object.GameObject(hGameObject)
oNewPickUp\oGameObject = oGameObject
ONewPickUp\iHealthBonus = iHealthBonus
GameObject_SetEntity(hGameObject, CreateSphere())
ScaleEntity oGameObject\hEntity, 0.3, 0.3, 0.3
oGameObject\iType = TYPE_PICKUP
oGameObject\hTypeData = Handle(oNewPickUp)
GameObject_SetDisabled(hGameObject, False)
Return hGameObject
End Function

; Destroy a game object of any type
Function GameObject_Destructor(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
Local oPlayer.Player
Local oPickUp.PickUp
; Verify and free entity
If Not(oGameObject\hEntity = 0) Then
FreeEntity oGameObject\hEntity
End If
; Destroy according from type
Select oGameObject\iType
; Player destruction
Case TYPE_PLAYER
oPlayer = Object.Player(oGameObject\hTypeData)
FreeEntity oPlayer\hHead
Delete oPlayer
; Pick-up destruction
Case TYPE_PICKUP
oPickUp = Object.PickUp(oGameObject\hTypeData)
Delete oPickUp
; Other types?
Default
RuntimeError "Game Object Error: Type#" + oGameObject\iType + "doesn't exist or function not implemented."
End Select
Delete oGameObject
End Function

; Update (game loop)
Function GameObject_Update(hGameObject%)
Local oGameObject.GameObject = Object.GameObject(hGameObject)
Local oPlayer.Player
Local oPickUp.PickUp
Local iTurn%, fMove#
If Not(oGameObject\bDisabled) Then
; Type update
Select oGameObject\iType
; Player update
Case TYPE_PLAYER
oPlayer = Object.Player(oGameObject\hTypeData)
iTurn = (Keydown(203) - Keydown(205))*3
fMOve = (Keydown(200) - Keydown(208))*0.25
TurnEntity oPlayer\hHead, 0, iTurn, 0
TurnEntity oGameObject\hEntity, 0, iTurn, 0
MoveEntity oPlayer\hHead, fMove, 0, 0
MoveEntity oGameObject\hEntity, fMove, 0, 0
; Pick-up update
Case TYPE_PICKUP
oPickUp = Object.PickUp(oGameObject\hTypeData)
TurnEntity oGameObject\hEntity, 0, 1, 0
MoveEntity oGameObject\hEntity, 0, Sin(Millisecs())*0.1, 0
; Other types?
Default
RuntimeError "Game Object Error: Type#" + oGameObject\iType + " doesn't exist or function not implemented."
End Select
End If
End Function

; Get health from player
Function Player_GetHealth%(hPlayer%)
Local oGameObject.GameObject = Object.GameObject(hPlayer)
Local oPlayer.Player = Object.Player(oGameObject\hTypeData)
Return oPlayer\iHealth
End Function

; Set health for player
Function Player_SetHealth%(hPlayer%, iHealth%)
Local oGameObject.GameObject = Object.GameObject(hPlayer)
Local oPlayer.Player = Object.Player(oGameObject\hTypeData)
oPlayer\iHealth = iHealth
End Function

; Get health bonus from pick-up
Function PickUp_GetHealthBonus%(hPickUp%)
Local oGameObject.GameObject = Object.GameObject(hPickUp)
Local oPickUp.PickUp = Object.PickUp(oGameObject\hTypeData)
Return oPickUp\iHealthBonus
End Function

Graphics3D 800, 600, 0, 2
SetBuffer BackBuffer()
AmbientLight 255, 255, 255
SeedRnd Millisecs()

hCam = CreateCamera()
MoveEntity hCam, 10, 10, 10

hPlayer = Player_Constructor()

While Not (KeyDown(1) Or (Player_GetHealth(hPlayer) <= 0))

GameObject_Update(hPlayer)
If Not(hPickUp = 0) Then
GameObject_Update(hPickUp)
If EntityDistance(GameObject_GetEntity(hPickUp), GameObject_GetEntity(hPlayer)) < 1 Then
Player_SetHealth(hPlayer, Player_GetHealth(hPlayer) + PickUp_GetHealthBonus(hPickUp))
GameObject_Destructor(hPickUp)
hPickUp = 0
End If
Else
hPickUp = PickUp_Constructor(Rand(0, 4) - 2)
PositionEntity GameObject_GetEntity(hPickUp), Rand(0, 20) - 10, 0, Rand(0, 20) - 10
End If
PointEntity hCam, GameObject_GetEntity(hPlayer)

Cls

RenderWorld

Text 0, 0, Player_GetHealth(hPlayer)

Flip

Wend

GameObject_Destructor(hPlayer)
FreeEntity hCam



Related Links
BlitzBasic's HomePage
Object Oriented Programming Wiki
Polymorphism Wiki
Inheritance Wiki

Copyright Information
This tutorial is copyrighted to Jocelyn "GoSsE" Perreault, 2006.
Redistribution of this tutorial is prohibited unless consent is given by the author.

Revision History
-1.0.0 (January 15th 2006): Initial release of the tutorial.


Last edited on Sun 10 Aug 2008 at 01:17:08 by gosse.

-gosse

Back to BlitzCoderPage 1

1 guest online. 26 visitors in the last 24 hours.