OOP question

BlitzMax Forums/BlitzMax Beginners Area/OOP question

JoshK(Posted 2006) [#1]
One advantage I can see to OOP is you can make everything a derivitive of a base class, in this case "tEngineNode". If your player class then has a "target" value of another entity, you can supply it with any kind of type, since they are all derivitives of the tEngineNode. However, when I try this, I am unable to recover the "speed" value.

Type tEngineNode
EndType

Type tPlayer Extends tEngineNode
	Field target:tenginenode
EndType

Type tVehicle Extends tEngineNode
	Field speed#=100
	Field target:tenginenode
EndType

player:tplayer=New tplayer
vehicle:tvehicle=New tvehicle

player.target=vehicle
Notify player.target.speed



N(Posted 2006) [#2]
Target is marked as a tEngineNode, not a tVehicle, so when you pass vehicle to target, it gets downcasted to a tEngineNode. So, before you can get speed, you have to cast it as a tVehicle.

player:tplayer=New tplayer
vehicle:tvehicle=New tvehicle

player.target=vehicle
vehicle = Null ' Just to ingrain the fact that we are not using the old reference

vehicle = tVehicle( player.target )
If vehicle Then Notify vehicle.speed


However, this is a rather inconvenient way to get something. After all, you don't want to cast and check each time.

My personal preference is to use messaging.

E.g.,
Const MSG_SPEED% = $00000001

Type tEngineNode
    Method SendMessageF:Float( msg:Int )
        Return 0
    End Method
    
    Field target:tenginenode
EndType

Type tPlayer Extends tEngineNode
EndType

Type tVehicle Extends tEngineNode
    Field speed#=100
    
    Method SendMessageF:Float( msg:Int )
        Select msg
            Case MSG_SPEED
                Return speed
        End Select
        Return 0
    End Method
EndType

player:tplayer=New tplayer
vehicle:tvehicle=New tvehicle

player.target=vehicle
Notify player.target.SendMessageF( MSG_SPEED )


All Objects have a default SendMessage method that you can override (not overload), it's under Language Reference->Objects in the documentation. The above example uses a custom method in the base class to return a float instead of an object.


JoshK(Posted 2006) [#3]
Your first example is just as inconvenient as the old Blitz method:
Type tplayer
Field target
End Type

Type tvehicle
Field speed#
End Type

player.tplayer=New tplayer
vehicle.tvehicle=New tvehicle

player\target=Handle(vehicle)

vehicle.tvehicle=Object.tvehicle(vehicle)
If vehicle<>Null Notify vehicle\speed

Your second example looks like hell.

And finally, here it is with my old system I used to write 3D World Studio:
player=CreatePlayer()
vehicle=CreateVehicle()
SetPlayerTarget player,vehicle
target=PlayerTarget(player)
If ObjectClass(target)=CLASS_VEHICLE Notify VehicleSpeed(target)


The big advantage with mine is I can do things like this:
SelectObjectClass(PlayerTarget(player))
	Case CLASS_MONSTER

	Case CLASS_FLARE
		
	EndSelect


Although OOP could do this:
Type tEngineNode
Field target:tenginenode
	Method Update()
	EndMethod
EndType

Type tPlayer Extends tEngineNode
	Method Update()
	EndMethod
EndType

Type tVehicle Extends tEngineNode	
	Method Update()
		Notify 1
	EndMethod
EndType

player:tplayer=New tplayer
vehicle:tvehicle=New tvehicle
player.target=vehicle

player.target.update()


The fact that I can't call player.target.speed is really putting me off of types.


semar(Posted 2006) [#4]
Would this work ?
vehicle:tvehicle=New tvehicle
player.target=vehicle
.
.
en:tEngineNode = player.target
Notify en.speed

[edit: oop(s), that wouldn't work. see my post below about casting]
This works:
'casting
If tvehicle(player.target) Then
Notify tvehicle(player.target).speed#
EndIf


N(Posted 2006) [#5]
No.


JoshK(Posted 2006) [#6]
Do it like this:
Type tEngineNode
Field target:tEngineNode
Field vehicle:tVehicle
Field player:tPlayer
EndType

Type tPlayer Extends tEngineNode
	Method New()
		player=Self
	EndMethod
EndType

Type tVehicle Extends tEngineNode
	Field speed#=100
	Method New()
		vehicle=Self
	EndMethod
EndType

player:tplayer=New tplayer
vehicle:tvehicle=New tvehicle
player.target=vehicle
Notify player.target.vehicle.speed



JoshK(Posted 2006) [#7]
Is it possible to have a generic :object?

Type tEngineNode
Field target:tEngineNode
Field subobject:Object
EndType



FlameDuck(Posted 2006) [#8]
Is it possible to have a generic :object?
Yes. All objects implicitly extend the Object class.


semar(Posted 2006) [#9]
What you need is casting:
Type tEngineNode
EndType

Type tPlayer Extends tEngineNode
	Field target:tengineNode
EndType

Type tVehicle Extends tEngineNode
	Field speed#=100
	Field target:tenginenode
EndType

player:tplayer=New tplayer
vehicle:tvehicle=New tvehicle

player.target=vehicle

'here is casting.
If tvehicle(player.target) Then
	Notify tvehicle(player.target).speed#
EndIf



Bare in mind that casting is either powerful and dangerous. if you attempt to use it against a wrong class (that is, type), then you get an error. That's why you should check first if the player.target can be 'casted' to a tvehicle type:
If tvehicle(player.target) 


Another thing. Classes are powerful, but you should use it wisely. If you have classes that inherits (extends in BMax) from a main class (in your example, tEngineNode), and share the same variable (like the speed# above), then you may put that variable in the main class, instead of spreading it in other classes.

That way you don't even need any casting later.

Good planned classes may save lots of work and time in the middle-long run of code development.

Sergio.