An OOP Spaceship Movement routine

BlitzMax Forums/BlitzMax Tutorials/An OOP Spaceship Movement routine

Spaceship with OOP

VERSION 1 of 2
Strict					' 'strict' = must declare variables
' VARIABLES
Global GW = 800 			' Graphics Height
Global GH = 600			' Graphics Width
Global GD = 0				' Graphics Depth "0" means Windowed Mode
Global MID_GH = GH/2		' Middle of the screen Height (Y)
Global MID_GW = GW/2		' Middle of the screen width  (X)
Global MX,MY				' Used for Mousex() and Mousey()


Graphics(GW,GH,GD)

Rem 
	create an actor type that possess attributes that can be used by may different
	game characters. Player, enemy, Wizard, etc... 

	The Type "ACTOR" is the beginning of our Object Oriented Programming (OOP)
	Actor is a Class or Type.  It simply is a definition of what an "actor" is.
	the Fields can also called Attributes.  for example, an actor has the x#,y# 
	Fields that will hold the screen x,y coordinates that tell BMAX where to draw
	it.  the actor class also has "Methods". Methods are functions that are unique
	to that class.  the "update" method, for instance, will only work for the actor
	class.
	
	  	
End Rem 



Type actor
					' 
	Field x# = GW/2			' SCREEN X and Y POSITION (initially the middle)
	Field y# = GH/2
	Field xint:Int			' x,y integers fot neater screen printing
	Field yint:Int
	Field color
	Field name:String			' string contains the text name of actor
	Field life = 100			' life force (defaults to 100)
	Field entity				' a placeholder for the image of the actor
	'
	Field angle#				' angle# = 0 to 360 degrees
	Field velocity#			' speed of the actor (forward momentum?) 		
	Field angular_velocity#	' rotational speed, i.e. how fast it spins
	Field friction# = 0.99		' Friction slows down the actor slightly each frame
	Field rotfriction# = 0.97	' slows down the rate of spin slightly each frame
	
	
	Method applyImpulse#(angular#,thrust#=0.0)
		' add the rotational and/or thrust to the velocities
		' these are taken from key inputs. see below
		
		angular_velocity# = angular_velocity# + angular#
		velocity# = velocity# + thrust#
	End Method
	
	Method update()
			rem
			 This method calculates the velocity of the ship as well as the 
			 angle. It uses the SINE and COSINE of the current ANGLE to 
			 determine the direction the ship should travel. The ANGLE is between
			 0 and 360. 
			 both velocities (rotation and forward) are multiplied by a friction
			amount that will slow the ship down each frame
						
			End Rem
			
			angular_velocity# = angular_velocity# * rotfriction#
			velocity# = velocity# * friction
			
			' move the ship forward
			x=x+(velocity*Cos(angle-90))
			y=y+(velocity*Sin(angle-90))
		
		   ' Turn the ship
		
			angle#:+angular_velocity
				If angle#<0.0 Then angle#=360.0
				If angle#>360.0 Then angle#= 0.00
						
			' If the actor goes off screen, WRAP to other side
			If x>GW Then x = 0 
			If x<0 Then x = GW
			If y<0 Then y = GH 
			If y>GH Then y=0
				
	End Method
	
	Method show()
		' This Method is used to draw the current frame of the ship
		' as well as the print the x,y and velocity values to the screen
		xint = x
		yint = y
		
		SetRotation(angle)			' turn spaceship to current angle
		SetColor 255,0,0
			DrawImage(entity,x,y)
		
		SetTransform(0,1.1,1.1) 
		SetColor 0,0,255
			DrawRect x-44,y+30,96,15

		SetScale 1,1
		SetColor 255,255,0
			DrawText "x="+xint+" y="+yint,x-40,y+32
			DrawText "Velocity = "+velocity,x-40,y+48
		
	End Method
	
	
	
End Type 

Rem  

	Now we create an extended Type called Player.  Type Player will INHERIT all of the
	attributes and methods of actor, as well as add a new field of its own, inventory.
	the [] at the end indicate an array.  inventory is an array of strings.


End Rem


Type player Extends actor
	Field inventory:String[]
End Type

Type enemy Extends actor
	Field anger
	
End Type

rem
	now create an INSTANCE of player called spaceship.

End Rem

Global spaceship:player = New player
spaceship.name = "Bob"

spaceship.entity = CreateImage(64,64,DYNAMICIMAGE)
	
	' draw a spaceship
	'AutoMidHandle(1)
	SetColor 255,255,0
	SetLineWidth 3
	DrawLine 0,64,32,0
	DrawLine 32,0,64,64
	DrawOval 24,16,16,16
	GrabImage spaceship.entity,0,0
	SetImageHandle(spaceship.entity,32,48)
	'MidHandleImage(spaceship.entity)
	SetScale 1,1

' Main Loop

Repeat
	Cls				' Clear the screen
		
	' get key inputs to move ship
	If KeyDown(KEY_LEFT) Then spaceship.applyImpulse(-0.1)
	If KeyDown(KEY_RIGHT) Then spaceship.applyImpulse(0.1)
	If KeyDown(KEY_UP) Then spaceship.applyImpulse(0,0.04)
	
	' update and display the ship
	
	spaceship.update()
	spaceship.show()
	
	Flip				' display the backbuffer (flip to front buffer)
Until KeyHit(KEY_ESCAPE)



Spaceship OOP
VERSION 2 of 2

Rem
        1/3/2005 Bill Radford
	Spaceship Movement tutorial--  Version 2 "A Little More OOP"
	In this episode I am incorporating some ideas and insights
	from fellow BlitzMAXers to make the code a little more OOPy.
	
	Significantly, We added a function called create that will act
	as sort of a "Spaceship Object Factory".  We also are using a
	LINKED LIST to hold our objects.
	
	Thanks to PERTURBIO for the key-binding and check controls methods as
	well as other OOP enhancements

End Rem


Strict					' 'strict' = must declare variables
' VARIABLES
Global GW = 800 			' Graphics Height
Global GH = 600			' Graphics Width
Global GD = 0				' Graphics Depth "0" means Windowed Mode
Global MID_GH = GH/2		' Middle of the screen Height (Y)
Global MID_GW = GW/2		' Middle of the screen width  (X)
Global MX,MY				' Used for Mousex() and Mousey()

Global ship_list:Tlist = CreateList()		' create a list to hold our ship objects
Local ship:player							

Graphics(GW,GH,GD)		' set graphics mode

Rem 
	create an actor type that possess attributes that can be used by may different
	game characters. Player, enemy, Wizard, etc... 

	The Type "ACTOR" is the beginning of our Object Oriented Programming (OOP)
	Actor is a Class or Type.  It simply is a definition of what an "actor" is.
	the Fields can also called Attributes.  for example, an actor has the x#,y# 
	Fields that will hold the screen x,y coordinates that tell BMAX where to draw
	it.  the actor class also has "Methods". Methods are functions that are unique
	to that class.  the "update" method, for instance, will only work for the actor
	class.
	
	  	
End Rem 



Type actor
					' 
	Field x# = GW/2			' SCREEN X and Y POSITION (initially the middle)
	Field y# = GH/2
	Field xint:Int			' x,y integers fot neater screen printing
	Field yint:Int
	Field name:String			' string contains the text name of actor
	Field life = 100			' life force (defaults to 100)
	Field entity				' a placeholder for the image of the actor
	'
	Field angle#				' angle# = 0 to 360 degrees
	Field velocity#			' speed of the actor (forward momentum?) 		
	Field angular_velocity#	' rotational speed, i.e. how fast it spins
	Field friction# = 0.99		' Friction slows down the actor slightly each frame
	Field rotfriction# = 0.97	' slows down the rate of spin slightly each frame
		
	Method applyImpulse#(angular#,thrust#=0.0)
		' add the rotational and/or thrust to the velocities
		' these are taken from key inputs. see below
		
		angular_velocity# = angular_velocity# + angular#
		velocity# = velocity# + thrust#
	End Method
	
	Method update()
			Rem
			 This method calculates the velocity of the ship as well as the 
			 angle. It uses the SINE and COSINE of the current ANGLE to 
			 determine the direction the ship should travel. The ANGLE is between
			 0 and 360. 
			 both velocities (rotation and forward) are multiplied by a friction
			amount that will slow the ship down each frame
						
			End Rem
			
			angular_velocity# = angular_velocity# * rotfriction#
			velocity# = velocity# * friction
			
			' move the ship forward
			x=x+(velocity*Cos(angle-90))
			y=y+(velocity*Sin(angle-90))
		
		   ' Turn the ship
		
			angle#:+angular_velocity
				If angle#<0.0 Then angle#=360.0
				If angle#>360.0 Then angle#= 0.00
						
			' If the actor goes off screen, WRAP to other side
			If x>GW Then x = 0 
			If x<0 Then x = GW
			If y<0 Then y = GH 
			If y>GH Then y=0
				
	End Method
	
	Method show()
		' This Method is used to draw the current frame of the ship
		' as well as the print the x,y and velocity values to the screen
		xint = x
		yint = y
		
		SetRotation(angle)			' turn spaceship to current angle
		
			DrawImage(entity,x,y)
		
		SetTransform(0,1.1,1.1) 
		SetColor 0,0,255
			DrawRect x-44,y+30,96,15

		SetScale 1,1
		SetColor 255,255,0
			DrawText "x="+xint+" y="+yint,x-40,y+32
			DrawText "Velocity = "+velocity,x-40,y+48
		
	End Method
	
	
	
End Type 

Rem  

	Now we create an extended Type called Player.  Type Player will INHERIT all of the
	attributes and methods of actor, as well as add a new field of its own, inventory.
	the [] at the end indicate an array.  inventory is an array of strings.


End Rem


Type player Extends actor
	
	
	Field inventory:String[]
	Field _KEYLEFT = KEY_LEFT 
	Field _KEYRIGHT = KEY_RIGHT
	Field _KEYUP = KEY_UP
	Field _KEYDOWN = KEY_DOWN
	
	Method SetKey(KeyName:String, KeyVal:Byte)
		Select Lower(KeyName)
			Case "left"
				_KEYLEFT = KeyVal
			Case "right"
				_KEYRIGHT = KeyVal
			Case "up"
				_KEYUP = KeyVal
			Case "down"
				_KEYDOWN = KeyVal
		End Select
	End Method
	
	Method GetKey(KeyName:String)
		Select Lower(KeyName)
			Case "left"
				Return _KEYLEFT
			Case "right"
				Return _KEYRIGHT
			Case "up"
				Return _KEYUP
			Case "down"
				Return _KEYDOWN
		End Select
	End Method
	
	Method CheckControls()
		If KeyDown(_KEYLEFT) Then applyImpulse(-0.1)
		If KeyDown(_KEYRIGHT) Then applyImpulse(0.1)
		If KeyDown(_KEYUP) Then applyImpulse(0,0.04)
	End Method
	
	' This is a unique type of function.  when called, this function will create a new instance
	' of PLAYER. Since functions can be called when there are no existing instances, they can
	' be used for this.  This adds a nice, tidy way to add new instances of our types, along with
	' several attributes (in this case the x,y starting position, name and RGB color)
	
	Function create:player(pname$,pox,poy,red,green,blue)
		Local t:player = New player
		t.entity = CreateImage(64,64,DYNAMICIMAGE)
		t.name = pname
		t.x = pox
		t.y = poy
		SetColor red,green,blue
		SetLineWidth 3
		DrawLine 0,64,32,0
		DrawLine 32,0,64,64
		DrawOval 24,16,16,16
		SetColor 255,255,0
		DrawText pname,32-(Len(pname)*8)/2,20
		GrabImage t.entity,0,0
		Cls ; Flip
		SetImageHandle(t.entity,32,48)
		SetScale 1,1
		
		' NEW - we add this INSTANCE of player to the LINKED LIST called ship_list
		' so we can iterate through them easily later on
		ListAddLast ship_list,t
		
		Return t
	End Function
End Type

Type enemy Extends actor
	Field anger
	
End Type

Rem
	call the player TYPE's function "create" to create a few instances of type "player"
	the we will add them to a LIST

End Rem


' Create INSTANCES of the type player
Global spaceship:player = player.create("Bobby",GW-100,GH/2,0,200,0)
	
Global player2:player = player.create("Kate",100,GH/2,200,0,0)
	player2.SetKey("left", KEY_A)
	player2.SetKey("right", KEY_D)
	player2.SetKey("up", KEY_W)
	
' Main Loop

Repeat
	Cls				' Clear the screen
	
	' we now have the player types (the ships) in the Linked List (ship_list)
	' we can iterate through them and get control inputs, update their position and 
	' display them to the screen within a FOR EACHIN loop
		
	For ship:player = EachIn ship_list	' get key inputs to move ship
		ship.CheckControls()
		ship.update()
		ship.show()
	next
	
	Flip				' display the backbuffer (flip to front buffer)
Until KeyHit(KEY_ESCAPE) 


nice tutorial. excellent comments.

nice :)

but just to make it a little more OOP:
Strict					' 'strict' = must declare variables
' VARIABLES
Global GW = 800 			' Graphics Height
Global GH = 600			' Graphics Width
Global GD = 0				' Graphics Depth "0" means Windowed Mode
Global MID_GH = GH/2		' Middle of the screen Height (Y)
Global MID_GW = GW/2		' Middle of the screen width  (X)
Global MX,MY				' Used for Mousex() and Mousey()


Graphics(GW,GH,GD)

Rem 
	create an actor type that possess attributes that can be used by may different
	game characters. Player, enemy, Wizard, etc... 

	The Type "ACTOR" is the beginning of our Object Oriented Programming (OOP)
	Actor is a Class or Type.  It simply is a definition of what an "actor" is.
	the Fields can also called Attributes.  for example, an actor has the x#,y# 
	Fields that will hold the screen x,y coordinates that tell BMAX where to draw
	it.  the actor class also has "Methods". Methods are functions that are unique
	to that class.  the "update" method, for instance, will only work for the actor
	class.
	
	  	
End Rem 



Type actor
					' 
	Field x# = GW/2			' SCREEN X and Y POSITION (initially the middle)
	Field y# = GH/2
	Field xint:Int			' x,y integers fot neater screen printing
	Field yint:Int
	Field color
	Field name:String			' string contains the text name of actor
	Field life = 100			' life force (defaults to 100)
	Field entity				' a placeholder for the image of the actor
	'
	Field angle#				' angle# = 0 to 360 degrees
	Field velocity#			' speed of the actor (forward momentum?) 		
	Field angular_velocity#	' rotational speed, i.e. how fast it spins
	Field friction# = 0.99		' Friction slows down the actor slightly each frame
	Field rotfriction# = 0.97	' slows down the rate of spin slightly each frame
	
	
	Method applyImpulse#(angular#,thrust#=0.0)
		' add the rotational and/or thrust to the velocities
		' these are taken from key inputs. see below
		
		angular_velocity# = angular_velocity# + angular#
		velocity# = velocity# + thrust#
	End Method
	
	Method update()
			Rem
			 This method calculates the velocity of the ship as well as the 
			 angle. It uses the SINE and COSINE of the current ANGLE to 
			 determine the direction the ship should travel. The ANGLE is between
			 0 and 360. 
			 both velocities (rotation and forward) are multiplied by a friction
			amount that will slow the ship down each frame
						
			End Rem
			
			angular_velocity# = angular_velocity# * rotfriction#
			velocity# = velocity# * friction
			
			' move the ship forward
			x=x+(velocity*Cos(angle-90))
			y=y+(velocity*Sin(angle-90))
		
		   ' Turn the ship
		
			angle#:+angular_velocity
				If angle#<0.0 Then angle#=360.0
				If angle#>360.0 Then angle#= 0.00
						
			' If the actor goes off screen, WRAP to other side
			If x>GW Then x = 0 
			If x<0 Then x = GW
			If y<0 Then y = GH 
			If y>GH Then y=0
				
	End Method
	
	Method show()
		' This Method is used to draw the current frame of the ship
		' as well as the print the x,y and velocity values to the screen
		xint = x
		yint = y
		
		SetRotation(angle)			' turn spaceship to current angle
		SetColor 255,0,0
			DrawImage(entity,x,y)
		
		SetTransform(0,1.1,1.1) 
		SetColor 0,0,255
			DrawRect x-44,y+30,96,15

		SetScale 1,1
		SetColor 255,255,0
			DrawText "x="+xint+" y="+yint,x-40,y+32
			DrawText "Velocity = "+velocity,x-40,y+48
		
	End Method
	
	
	
End Type 

Rem  

	Now we create an extended Type called Player.  Type Player will INHERIT all of the
	attributes and methods of actor, as well as add a new field of its own, inventory.
	the [] at the end indicate an array.  inventory is an array of strings.


End Rem


Type player Extends actor
	Field inventory:String[]
	Field _KEYLEFT = KEY_LEFT 
	Field _KEYRIGHT = KEY_RIGHT
	Field _KEYUP = KEY_UP
	Field _KEYDOWN = KEY_DOWN
	
	Method SetKey(KeyName:String, KeyVal:Byte)
		Select Lower(KeyName)
			Case "left"
				_KEYLEFT = KeyVal
			Case "right"
				_KEYRIGHT = KeyVal
			Case "up"
				_KEYUP = KeyVal
			Case "down"
				_KEYDOWN = KeyVal
		End Select
	End Method
	
	Method GetKey(KeyName:String)
		Select Lower(KeyName)
			Case "left"
				Return _KEYLEFT
			Case "right"
				Return _KEYRIGHT
			Case "up"
				Return _KEYUP
			Case "down"
				Return _KEYDOWN
		End Select
	End Method
	
	Method CheckControls()
		If KeyDown(_KEYLEFT) Then applyImpulse(-0.1)
		If KeyDown(_KEYRIGHT) Then applyImpulse(0.1)
		If KeyDown(_KEYUP) Then applyImpulse(0,0.04)
	End Method
End Type

Type enemy Extends actor
	Field anger
	
End Type

Rem
	now create an INSTANCE of player called spaceship.

End Rem

Global spaceship:player = New player
spaceship.name = "Bob"

spaceship.entity = CreateImage(64,64,DYNAMICIMAGE)

	' draw a spaceship
	'AutoMidHandle(1)
	SetColor 255,255,0
	SetLineWidth 3
	DrawLine 0,64,32,0
	DrawLine 32,0,64,64
	DrawOval 24,16,16,16
	GrabImage spaceship.entity,0,0
	SetImageHandle(spaceship.entity,32,48)
	'MidHandleImage(spaceship.entity)
	SetScale 1,1
	
Global player2:player = New player
	Player2.Name = "Kate"
	Player2.entity = CreateImage(64,64,DYNAMICIMAGE)
	' draw a spaceship
	'AutoMidHandle(1)
	SetColor 255,255,255
	SetLineWidth 3
	DrawLine 0,64,32,0
	DrawLine 32,0,64,64
	DrawOval 24,16,16,16
	GrabImage player2.entity,0,0
	SetImageHandle(player2.entity,32,48)
	SetScale 1,1
	player2.SetKey("left", KEY_A)
	player2.SetKey("right", KEY_D)
	player2.SetKey("up", KEY_W)
	
' Main Loop

Repeat
	Cls				' Clear the screen
		
	' get key inputs to move ship
	spaceship.CheckControls()
	player2.CheckControls()
	
	' update and display the ship
	
	spaceship.update()
	player2.update()
	spaceship.show()
	player2.show()
	
	Flip				' display the backbuffer (flip to front buffer)
Until KeyHit(KEY_ESCAPE)


Pretty useful and easy to understand even for a newbie like me :) thanks.

Sound very good, but I suggest some changes in the structure. I learnt that it's better to derive everything from something like TGameObject:

Type TGameObject
  Field x:float, y:Float
  Field hImage:TImage
  '...
EndType

type TEnemy extends TGameObject
  '...
endtype

type TPlayer extends TGameObject
  '...
endtype

type TBullet extends TGameObject
  Field hPlayerWhoShot:TPlayer
endtype

type TGuardian extends TEnemy
  'blah blah
endtype


Thats useful because you can implement a method like this in TGameObject:

Method collision:Byte( hObj:TGameObject )
		
		If ( Self.x + Self.width ) > hObj.x And Self.x < ( hObj.x + hObj.width )
			
			If ( Self.y + Self.height ) > hObj.y And Self.y < ( hObj.y + hObj.height )
			
				Return True
				
			EndIf
			
		EndIf
		
		Return False
		
	EndMethod


You can check everything with everything, use things in a single list (not very clever but you can put all enemies together, for example) etc., I love to code things this way because it feels pretty cool. But that doesnt mean that your code isnt good at all, i certainly like it, its a good work :)

great comments everyone.
nice mod, perturbio
I'll post V2 with those enhancements.

Thanks for the tutorial Bill. It is also helpful and interesting to see the progression from the original version, so it might be a good idea to include the original with the V2. That way others might be able to see how they can make their own code 'a little more OOP'.

Cheers

OK. Just added Version 2 above. Just like many of you, I am learning this as I go along so if any Master OOPs-men want to critique, I encourage it.

in V3 we may see some enemies...

Before BMax i used hundreds of Arrays like
EntitysVeloc(entity*100+EntityID)=1 to get quick acces to a certain entitys data.

With BMax thats all so beautyfull.......sniff


Mh, if you "release Player2" an error occures (sure).
I got around with statements like:
if player2<>Null player2.CheckControls()

Is there a faster way to ignore a non valid method call?

In order to test out what you mean, I added this to the code.

If KeyHit(KEY_SPACE)
		Release player2
		player2.checkcontrols
	Endif


it generate this:
Unhandled Exception: Attempt to access field or method of Null object


the Variable 'Player2' is a pointer. in the player.create Function we add the player2 object to the Ship_list LIST
ListAddLast ship_list,t 


remember how we assigned Player2 the name "Kate"? We can use that as an Index instead. or you could assign an arbitrary Integer, I like using names though.

We can create a function that will remove our player.

Rem
        1/5/2005 Bill Radford
        *** ADDED player.destroy() function
	Spaceship Movement tutorial--  Version 2 "A Little More OOP"
	In this episode I am incorporating some ideas and insights
	from fellow BlitzMAXers to make the code a little more OOPy.
	
	Significantly, We added a function called create that will act
	as sort of a "Spaceship Object Factory".  We also are using a
	LINKED LIST to hold our objects.
	
	Thanks to PERTURBIO for the key-binding and check controls methods as
	well as other OOP enhancements

End Rem


Strict					' 'strict' = must declare variables
' VARIABLES
Global GW = 800 			' Graphics Height
Global GH = 600			' Graphics Width
Global GD = 0				' Graphics Depth "0" means Windowed Mode
Global MID_GH = GH/2		' Middle of the screen Height (Y)
Global MID_GW = GW/2		' Middle of the screen width  (X)
Global MX,MY				' Used for Mousex() and Mousey()

Global ship_list:Tlist = CreateList()		' create a list to hold our ship objects
Local ship:player							

Graphics(GW,GH,GD)		' set graphics mode

Rem 
	create an actor type that possess attributes that can be used by may different
	game characters. Player, enemy, Wizard, etc... 

	The Type "ACTOR" is the beginning of our Object Oriented Programming (OOP)
	Actor is a Class or Type.  It simply is a definition of what an "actor" is.
	the Fields can also called Attributes.  for example, an actor has the x#,y# 
	Fields that will hold the screen x,y coordinates that tell BMAX where to draw
	it.  the actor class also has "Methods". Methods are functions that are unique
	to that class.  the "update" method, for instance, will only work for the actor
	class.
	
	  	
End Rem 



Type actor
					' 
	Field x# = GW/2			' SCREEN X and Y POSITION (initially the middle)
	Field y# = GH/2
	Field xint:Int			' x,y integers fot neater screen printing
	Field yint:Int
	Field name:String			' string contains the text name of actor
	Field life = 100			' life force (defaults to 100)
	Field entity				' a placeholder for the image of the actor
	'
	Field angle#				' angle# = 0 to 360 degrees
	Field velocity#			' speed of the actor (forward momentum?) 		
	Field angular_velocity#	' rotational speed, i.e. how fast it spins
	Field friction# = 0.99		' Friction slows down the actor slightly each frame
	Field rotfriction# = 0.97	' slows down the rate of spin slightly each frame
		
	Method applyImpulse#(angular#,thrust#=0.0)
		' add the rotational and/or thrust to the velocities
		' these are taken from key inputs. see below
		
		angular_velocity# = angular_velocity# + angular#
		velocity# = velocity# + thrust#
	End Method
	
	Method update()
			Rem
			 This method calculates the velocity of the ship as well as the 
			 angle. It uses the SINE and COSINE of the current ANGLE to 
			 determine the direction the ship should travel. The ANGLE is between
			 0 and 360. 
			 both velocities (rotation and forward) are multiplied by a friction
			amount that will slow the ship down each frame
						
			End Rem
			
			angular_velocity# = angular_velocity# * rotfriction#
			velocity# = velocity# * friction
			
			' move the ship forward
			x=x+(velocity*Cos(angle-90))
			y=y+(velocity*Sin(angle-90))
		
		   ' Turn the ship
		
			angle#:+angular_velocity
				If angle#<0.0 Then angle#=360.0
				If angle#>360.0 Then angle#= 0.00
						
			' If the actor goes off screen, WRAP to other side
			If x>GW Then x = 0 
			If x<0 Then x = GW
			If y<0 Then y = GH 
			If y>GH Then y=0
				
	End Method
	
	Method show()
		' This Method is used to draw the current frame of the ship
		' as well as the print the x,y and velocity values to the screen
		xint = x
		yint = y
		
		SetRotation(angle)			' turn spaceship to current angle
		
			DrawImage(entity,x,y)
		
		SetTransform(0,1.1,1.1) 
		SetColor 0,0,255
			DrawRect x-44,y+30,96,15

		SetScale 1,1
		SetColor 255,255,0
			DrawText "x="+xint+" y="+yint,x-40,y+32
			DrawText "Velocity = "+velocity,x-40,y+48
		
	End Method
	
	
	
End Type 

Rem  

	Now we create an extended Type called Player.  Type Player will INHERIT all of the
	attributes and methods of actor, as well as add a new field of its own, inventory.
	the [] at the end indicate an array.  inventory is an array of strings.


End Rem


Type player Extends actor
	
	
	Field inventory:String[]
	Field _KEYLEFT = KEY_LEFT 
	Field _KEYRIGHT = KEY_RIGHT
	Field _KEYUP = KEY_UP
	Field _KEYDOWN = KEY_DOWN
	
	Method SetKey(KeyName:String, KeyVal:Byte)
		Select Lower(KeyName)
			Case "left"
				_KEYLEFT = KeyVal
			Case "right"
				_KEYRIGHT = KeyVal
			Case "up"
				_KEYUP = KeyVal
			Case "down"
				_KEYDOWN = KeyVal
		End Select
	End Method
	
	Method GetKey(KeyName:String)
		Select Lower(KeyName)
			Case "left"
				Return _KEYLEFT
			Case "right"
				Return _KEYRIGHT
			Case "up"
				Return _KEYUP
			Case "down"
				Return _KEYDOWN
		End Select
	End Method
	
	Method CheckControls()
		If KeyDown(_KEYLEFT) Then applyImpulse(-0.1)
		If KeyDown(_KEYRIGHT) Then applyImpulse(0.1)
		If KeyDown(_KEYUP) Then applyImpulse(0,0.04)
	End Method
	
	Function Destroy(pname$)
		Local temp:Player
		For temp:player = EachIn ship_list
			If temp.name = pname$
				Release temp
				FlushMem
				Return True
			endif
		next
		Return False
	
	
	End Function
	
	' This is a unique type of function.  when called, this function will create a new instance
	' of PLAYER. Since functions can be called when there are no existing instances, they can
	' be used for this.  This adds a nice, tidy way to add new instances of our types, along with
	' several attributes (in this case the x,y starting position, name and RGB color)
	
	Function create:player(pname$,pox,poy,red,green,blue)
		Local t:player = New player
		t.entity = CreateImage(64,64,DYNAMICIMAGE)
		t.name = pname
		t.x = pox
		t.y = poy
		SetColor red,green,blue
		SetLineWidth 3
		DrawLine 0,64,32,0
		DrawLine 32,0,64,64
		DrawOval 24,16,16,16
		SetColor 255,255,0
		DrawText pname,32-(Len(pname)*8)/2,20
		GrabImage t.entity,0,0
		Cls ; Flip
		SetImageHandle(t.entity,32,48)
		SetScale 1,1
		
		' NEW - we add this INSTANCE of player to the LINKED LIST called ship_list
		' so we can iterate through them easily later on
		ListAddLast ship_list,t
		
		Return t
	End Function
End Type

Type enemy Extends actor
	Field anger
	
End Type

Rem
	call the player TYPE's function "create" to create a few instances of type "player"
	the we will add them to a LIST

End Rem


' Create INSTANCES of the type player

' This creates the default player
player.create("Bobby",GW-100,GH/2,0,200,0)
' This creates a 2nd player.  Since it will have unique keycodes, we will use a temp variable to reference
' it during the KEY binding setup
Global temp:player = player.create("Kate",100,GH/2,200,0,0)
	temp.SetKey("left", KEY_A)
	temp.SetKey("right", KEY_D)
	temp.SetKey("up", KEY_W)
' now we can release the temp variable
Release temp

FlushMem			' Momma says "Always Flush your Memory" 

#Main_Loop		' This is an identifier

Repeat
	Cls				' Clear the screen
	
	' we now have the player types (the ships) in the Linked List (ship_list)
	' we can iterate through them and get control inputs, update their position and 
	' display them to the screen within a FOR EACHIN loop
		
	For ship:player = EachIn ship_list	' get key inputs to move ship
		ship.CheckControls()
		ship.update()
		ship.show()
	Next
	
	If KeyHit(KEY_SPACE)
		player.destroy("Kate")
	Endif
	
	Flip				' display the backbuffer (flip to front buffer)
Until KeyHit(KEY_ESCAPE) 


Good tutorial.
This inspire me for the OOP version of Nehe tutorial 32. :)

I added some code in the method update() :
angular_velocity# = angular_velocity# * rotfriction#
velocity# = velocity# * friction
If velocity#<0.0001 Then velocity#=0.0

And change this to the methode show() :
DrawText "Velocity = "+ "".FromFloat(velocity)[..6],x-40,y+48

I hate this long long long float. :)
It's just a preference.