Code archives/3D Graphics - Misc/First person perspective movement & mouselooking code

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

Download source code

First person perspective movement & mouselooking code by Bill Stanbrook
(Posted 22 years ago)
First person perspective movement and mouselooking code.

The code is heavily commented, so just grab it and go for it :-)

I'll add some more features to this code when I get time.

Graphics3D 800, 600, 0, 2
SetBuffer BackBuffer ()
HidePointer


; == Declarations ==


; -- World.
;Global gravity# = 9.8
;^^^^^^

; -- Viewport.
Global viewport_center_x = GraphicsWidth () / 2
Global viewport_center_y = GraphicsHeight () / 2
;^^^^^^


; -- Mouselook.
Global mouselook_x_inc# = 0.4 ; This sets both the sensitivity and direction (+/-) of the mouse on the X axis.
Global mouselook_y_inc# = 0.4 ; This sets both the sensitivity and direction (+/-) of the mouse on the Y axis.
Global mouse_left_limit = 250 ; Used to limit the mouse movement to within a certain number of pixels (250 is used here) from the center of the screen. This produces smoother mouse movement than continuously moving the mouse back to the center each loop.
Global mouse_right_limit = GraphicsWidth () - 250 ; As above.
Global mouse_top_limit = 250 ; As above.
Global mouse_bottom_limit = GraphicsHeight () - 250 ; As above.
;^^^^^^

; -- Mouse smoothing que.
Global mouse_x_speed_1#
Global mouse_x_speed_2#
Global mouse_x_speed_3#
Global mouse_x_speed_4#
Global mouse_x_speed_5#
Global mouse_y_speed_1#
Global mouse_y_speed_2#
Global mouse_y_speed_3#
Global mouse_y_speed_4#
Global mouse_y_speed_5#
;^^^^^^

; -- User.
Global user_movement_speed# = 4.0 ; The user's movement speed. This produces a movement speed of 4 metres per second.
Global user_camera_pitch#
Const USER_CENTERPOINT_HEIGHT# = 0.94
Const USER_CAMERA_HEIGHT# = USER_CENTERPOINT_HEIGHT# * 2.0 - 0.1
;^^^^^^

; -- Timing.
Global milli_secs ; Holds the value of the Millisecs() timer. Set at the start of the main loop.
Global old_time ; This must be set to the current 'MilliSecs()' time at the start of a new game and when returning from a pause.
Global game_time ; This holds a relative game time value which is used with timeouts so that they can be paused.
Global Delta_Time# ; Use this as a multiplier for all continuous game world events, to regulate game speed.
;^^^^^^

; -- Collision.
Const COLLTYPE_geometry = 1
Const COLLTYPE_user = 2
;^^^^^^


; == Setup ==


; -- Create a light;
RotateEntity CreateLight (), 45.0, 45.0, 0.0, True
;^^^^^^

; -- Create user entity.
Global user = CreatePivot()
PositionEntity user, 0.0, USER_CENTERPOINT_HEIGHT#, 0.0, True
EntityRadius user, USER_CENTERPOINT_HEIGHT#
EntityType user, COLLTYPE_user
;^^^^^^

; -- Create user's camera.
Global user_camera = CreateCamera( user )
CameraRange user_camera, 0.5, 1000.0
CameraClsColor user_camera, 0.0, 106.0, 213.0
CameraZoom user_camera, 1.6 ; Set the camera focus to the correct value for the human eye.
PositionEntity user_camera, 0.0, USER_CAMERA_HEIGHT#, 0.0, True
;^^^^^^

; -- Create some meshes to use as a movement reference.
Global sphere = CreateSphere ()
UpdateNormals sphere
PositionEntity sphere, 15.0, 1.0, 0.0, True
EntityType sphere, COLLTYPE_geometry
Global cone = CreateCone ()
UpdateNormals cone
PositionEntity cone, -15.0, 1.0, 0.0, True
EntityType cone, COLLTYPE_geometry
Global cube = CreateCube ()
UpdateNormals cube
PositionEntity cube, 0.0, 1.0, 15.0, True
EntityType cube, COLLTYPE_geometry
Global cylinder = CreateCylinder ()
UpdateNormals cylinder
PositionEntity cylinder, 0.0, 1.0, -15.0, True
EntityType cylinder, COLLTYPE_geometry
	; -- Create grid texture.
	Global grid_2d_tex = CreateTexture ( 256, 256, 11 )
	SetBuffer TextureBuffer ( grid_2d_tex )
	For i = 0 To 4
		Rect i, i, 256 - i - i, 256 - i - i, False
	Next
	For y = 5 To 250
		For x = 5 To 250
			WritePixel x, y, 0
		Next
	Next
	SetBuffer BackBuffer ()
	;^^^^^^
	; -- Create 2D grid.
	Global grid_2D = CreatePlane ()
	EntityType grid_2D, COLLTYPE_geometry
	EntityTexture grid_2D, grid_2d_tex
	EntityFX grid_2D, 9
	;^^^^^^
;^^^^^^

InitializeNewMap()

; == Main Code ==


; -- Initialize the loop.
old_time = MilliSecs ()
Collisions COLLTYPE_user, COLLTYPE_geometry, 2, 2
;^^^^^^

; -- Main loop.
While Not KeyHit ( 1 ) ; Exit the main loop if the escape key is pressed.
	CalculateDeltatimeAndGametime()
	MouseLook()
	MoveUser()
	UpdateWorld
	RenderWorld

	; -- Frames Per Second counter.
	; Place into the main program loop where it will be executed each frame. The variables used here do not need to be declared globlly as this will occur automatically if the routine is placed in the main program. The value of 'milli_secs' should be set from the 'MilliSecs()' Blitz function at the start of the main program loop.
	FPS_framecounter = FPS_framecounter + 1
	If milli_secs >= FPS_timeout
		FPS_timeout = milli_secs + 1000
		FPS_framecount = FPS_framecounter
		FPS_framecounter = 0
	EndIf
	Text 10, 10, "FPS"
	Text 40, 10, FPS_framecount
	;^^^^^^

	Text 10, 30, "Use W, A, S, D, F, C to move and ESCAPE to exit."

	VWait ; Use this along with 'Flip False' so that the user can't disable the wait for vertical blank.
	Flip False

	Delay ( 1 ) ; Lets Windows do whatever housekeeping it needs to. Useful if the user is downloading something at the same time as playing a game, etc. Not sure if it's required for a windowed game.

Wend
;^^^^^^

End ; End the program.


; == Functions ==


Function MouseLook()

	; -- Update the smoothing que to smooth the movement of the mouse.
	mouse_x_speed_5# = mouse_x_speed_4#
	mouse_x_speed_4# = mouse_x_speed_3#
	mouse_x_speed_3# = mouse_x_speed_2#
	mouse_x_speed_2# = mouse_x_speed_1#
	mouse_x_speed_1# = MouseXSpeed ( )
	mouse_y_speed_5# = mouse_y_speed_4#
	mouse_y_speed_4# = mouse_y_speed_3#
	mouse_y_speed_3# = mouse_y_speed_2#
	mouse_y_speed_2# = mouse_y_speed_1#
	mouse_y_speed_1# = MouseYSpeed ( )
	the_yaw# = ( ( mouse_x_speed_1# + mouse_x_speed_2# + mouse_x_speed_3# + mouse_x_speed_4# + mouse_x_speed_5# ) / 5.0 ) * mouselook_x_inc#
	the_pitch# = ( ( mouse_y_speed_1# + mouse_y_speed_2# + mouse_y_speed_3# + mouse_y_speed_4# + mouse_y_speed_5# ) / 5.0 ) * mouselook_y_inc#
	;^^^^^^

	TurnEntity user, 0.0, -the_yaw#, 0.0 ; Turn the user on the Y (yaw) axis.
	user_camera_pitch# = user_camera_pitch# + the_pitch#
	; -- Limit the user's camera to within 180 degrees of pitch rotation. 'EntityPitch()' returns useless values so we need to use a variable to keep track of the camera pitch.
	If user_camera_pitch# > 90.0 Then user_camera_pitch# = 90.0
	If user_camera_pitch# < -90.0 Then user_camera_pitch# = -90.0
	;^^^^^^
	RotateEntity user_camera, user_camera_pitch#, 0.0, 0.0 ; Pitch the user's camera up and down.
	
	; -- Limit the mouse's movement. Using this method produces smoother mouselook movement than centering the mouse each loop.
	If ( MouseX() > mouse_right_limit ) Or ( MouseX() < mouse_left_limit ) Or ( MouseY() > mouse_bottom_limit ) Or ( MouseY() < mouse_top_limit )
		MoveMouse viewport_center_x, viewport_center_y
	EndIf
	;^^^^^^

End Function


Function MoveUser()

	If KeyDown( 32 ) Or KeyDown( 205 ) ; Right. The 'D' and 'CURSOR RIGHT' keys.
		x#=1.0
	Else If KeyDown( 30 ) Or KeyDown( 203 ) ; Left. The 'A' and 'CURSOR LEFT' keys.
		x#=-1.0
	EndIf

	If KeyDown( 33 ) Or KeyDown( 54 ) ; Up. The 'F' and 'RIGHT SHIFT' keys.
		y#=1.0
	Else If KeyDown( 46 ) Or KeyDown( 157 ) ; Down. The 'C' and 'RIGHT CONTROL' keys.
		y#=-1.0
	EndIf

	If KeyDown( 17 ) Or KeyDown( 200 ) ; Forward. The 'W' and 'CURSOR UP' keys.
		z#=1.0
	Else If KeyDown( 31 ) Or KeyDown( 208 ) ; Backward. The 'S' and 'CURSOR DOWN' keys.
		z#=-1.0
	EndIf

	MoveEntity user, x# * user_movement_speed# * Delta_Time#, y# * user_movement_speed# * Delta_Time#, z# * user_movement_speed# * Delta_Time# ; Move the user.

End Function


Function CalculateDeltatimeAndGametime()
; NOTES:
; The variable 'old_time' must be set to the current millisecs time at the start of a new game and when returning from a pause.

	milli_secs = MilliSecs () ; Store the 'MilliSecs ()' time in the 'milli_secs' variable so that you can use the stored time value without calling 'MilliSecs ()' again in this loop.

	the_time_taken = milli_secs - old_time ; Calculate the time the last loop took to execute.
	If the_time_taken > 100 Then the_time_taken = 100 ; This stops disk accesses and the like from causing jumps in position.
	game_time = game_time + the_time_taken ; Calculate the value for the 'game_time' variable used with timeouts, etc.
	Delta_Time# = the_time_taken / 1000.0 ; Calculate the value for the 'Delta_Time#' variable used to regulate game speed.
	old_time = milli_secs ; Update the 'old_time' variable with the current time.

End Function


Function InitializeNewMap()
; NOTES:
 ; This function should only need to be run when a new map is first loaded. It should normally be executed by the map file loader routine.

	; -- Precache the game's 3D graphics media. This forces the media to be uploaded to the video card. This should be done before any of the media is hidden or has its alpha set to zero to turn off rendering. Note that the camera will end up pointing back to where it started, so there is no need to reset its orientation.
	For i = 1 to 4
		TurnEntity user_camera, 0.0, 90.0, 0.0, True
		RenderWorld
	Next
	;^^^^^^
End Function
; The code is at: http://www.blitzbasic.com/codearcs/codearcs.php?code=1137