Line of site beamcastin

BlitzMax Forums/BlitzMax Programming/Line of site beamcastin

Hi Guys
I've been working a Rougelike game, and have recently completed some workable Line of Sight (LOS) code (see code below).

What it does it to draw a semicircle in the direction the player is facing, and then uses Brasenham's Algorhithm (function Line) to draw a line between the centre point (cx,cy in function DrawMap() ) and every point that comprises the semi circle.

The easiest way to get what I'm talking about it to copy and paste the code, and then get it running. In terms of the code, the important bit is the function Line(). I'm sure you guys can work out how the whole thing works :)

Left + Right = adjust direction
Up + Down = adjust vision distance
Mouse left = create cell
Mouse right = null cell

Pretty straightforwards, huh?

Well, if you start to adjust the vision distance you will start seeing black squares - i.e. places the line drawing function never goes through when drawing between the start point and semi-circle point. These are dead spaces that the player can't see.

My question is this: can the line drawing function be tweaked to ensure that all cells are "seen" or am I barking up the wrong tree?

If you can shed any light on this, I'd be extremely grateful.

	
	SuperStrict
	
	Graphics 640,480,0,32
	
	Local x:Int
	Local y:Int
	Global r:Int = 8	'default site distance
	Global w:Int = 8	'height and width of each cell
	Global s:Int = 1	'draw solid shape

	Global dir:Int		'direction faced
	
	Type cell
		Field x:Int
		Field y:Int
	End Type
	
	Const dungeon_x:Int = 50
	Const dungeon_y:Int = 50
	
	'Initialise the array
	Global dungeon:cell [dungeon_x,dungeon_y]
	For x = 0 To dungeon_x -1
	
		For y = 0 To dungeon_y -1
		
			dungeon [x,y] = New cell
		
		Next
		
	Next
	
	
	Const dir_north:Int = 0
	Const dir_south:Int = 4
	Const dir_east:Int = 2
	Const dir_west:Int = 6
	Const dir_northeast:Int = 1
	Const dir_northwest:Int = 7
	Const dir_southeast:Int = 3		
	Const dir_southwest:Int = 5		
	
	Global direction:String [8]
	
	direction[0] = "north"
	direction[1]="north east"
	direction[2]="east"	
	direction[3]="south east"
	direction[4]="south"	
	direction[5]="south west"	
	direction[6]="west"	
	direction[7]="north west"
						

	'Main loop							
																		
	While Not KeyHit (key_escape)
	
		Cls
		
		SetColor 255,255,255
		DrawText "Radius: " + r , 400,10
		DrawText "Direction: " + direction[dir] , 400,40
		
		drawmap		
		keypress
		mouse
		
		Flip
		
	Wend

Function checkCell:Int(x:Int, y:Int)

	If x < dungeon_x - 1 And y < dungeon_y -1 Then

		If dungeon [x,y] <> Null Then Return True
		
	End If

End Function

'
'	Draw the map and stuff
'
'	Calculates each point of the semicircle, and then tries to draw a line
'	between each point and the start point before moving onto the next point
'

Function drawMap()

	Local x:Int
	Local y:Int
	Local xDiff:Int
	Local yDiff:Int
	Local cX:Int = 20
	Local cY:Int = 20
	
		For x = 0 To dungeon_x - 1
		
			'draw grid
			SetColor 128,128,128
			DrawLine x * w, 0, x * w, dungeon_y * w
			DrawLine 0,x * w, dungeon_x * w, x * w	
			
			For y = 0 To dungeon_y - 1
				
				'check the cell isn't null
				If dungeon [x,y] <> Null Then
					
					'work out the difference between the start point
					'and the semicircle point
					xDiff = x - Cx
					yDiff = y - Cx
				
					'draw hollow circle
					If 	((xDiff * xDiff) + (yDiff * yDiff)) < (r * r) And ..
						((xDiff * xDiff) + (yDiff * yDiff)) > ((r*r) - (2*r)) Then
						
						SetColor 255,255,255
						
						Select dir
						
							Case dir_north
							
								If (y <= cy)
								
									draw (cx,cy,x,y)
								
								End If
							
							Case dir_south
							
								If (y >= cy)
								
									draw (cx,cy,x,y)						
								
								End If						
							
							Case dir_east
							
								If (x >= cX)
								
									draw (cx,cy,x,y)						
								
								End If								
							
							Case dir_west
	
								If x <= cy	
								
									draw (cx,cy,x,y)																	
								
								End If								
							
							Case dir_northWest
							
								If x <=(cX + (r / 2) + 1) And y <=(cy + (r/2) + 1)
								
									draw (cx,cy,x,y)
								
								End If								
							
							Case dir_northEast
							
								If x >=(cX - (r / 2) - 1) And y <=(cy + (r/2) + 1)
								
									draw (cx,cy,x,y)						
								
								End If								
							
							Case dir_southEast
							
								If x >= (cX - (r / 2) - 1) And y >= (cy - (r/2) - 1)
								
									draw (cx,cy,x,y)					
								
								End If								
							
							Case dir_southWest
							
								If x <= (cX + (r / 2) + 1) And y >= (cy - (r/2) - 1)	
								
									draw (cx,cy,x,y)					
								
								End If																																								
						
						End Select
											
					End If
				
				End If
			
			Next
			
		Next

End Function
	
'
'	if flag s = 1 then draw a line between x1,y1 and x2,y2
'	else just draw x2,y2 - the circle's outline
'	
	
Function draw(x1:Int, y1:Int, x2:Int, y2:Int)

	If checkCell (x2, y2) = True Then 							
	
		If s = 1 Then
		
			line (x1,y1,x2,y2)
			
		Else
			
			DrawRect x1 * w, y1 * w, w -1 ,w -1	'start point
			DrawRect x2 * w, y2 * w, w -1 ,w -1	'semi-circle point
		
		End If
		
	End If	

End Function	
	
Function keypress()
	
	'change direction
	If KeyHit (key_right) Then
	
		dir:+ 1 
		
		If dir > 7 Then dir = 0
	
	End If

	'change direction		
	If KeyHit (key_left) Then
	
		dir:- 1 
		
		If dir < 0 Then dir = 7
	
	End If	
	
	'increment vision distance
	If KeyHit (key_up) Then
	
		r:+1
	
	End If
	
	'decrement vision distance
	If KeyHit (key_down) Then
	
		r:-1
		If r<0 Then r = 0
	
	End If

	'toggle line drawing
	If KeyHit (key_s) Then
	
		If s = True Then
		
			s = False
			
		Else
		
			s = True
		
		End If
	
	End If

End Function


'
'	Brasenham's alg: draw a line between x1, y1 and x2,y2
'	If a null cell is hit along the way bail out.
'
'
		
Function Line(X1:Int,Y1:Int,X2:Int,Y2:Int)

	If s = True Then

		'Draws a line of individual pixels from X1,Y1 to X2,Y2 at any angle
	 	Local Steep:Int=Abs(Y2-Y1) > Abs(X2-X1)			'Boolean
	
		If Steep
		
			Local Temp:Int=X1; X1=Y1; Y1=Temp		'Swap X1,Y1
			Temp=X2; X2=Y2; Y2=Temp		'Swap X2,Y2
			
		EndIf
		
		Local DeltaX:Int=Abs(X2-X1)		'X Difference
		Local DeltaY:Int=Abs(Y2-Y1)		'Y Difference
		Local Error:Int=0		'Overflow counter
		Local DeltaError:Int=DeltaY		'Counter adder
		Local X:Int=X1		'Start at X1,Y1
		Local Y:Int=Y1		
		Local XStep:Int
		Local YStep:Int
		
		If X1<X2 Then XStep=1 Else XStep=-1	'Direction
		
		If Y1<Y2 Then YStep=1 Else YStep=-1	'Direction
		
		If Steep Then 
		
			DrawRect y * w, x * w, w -1 ,w -1
			
		Else 
		
			DrawRect x * w, y * w, w -1 ,w -1
			
		End If
		
		While X<>X2
		
			X:+XStep		'Move in X
			
			Error:+DeltaError		'Add to counter
			
			If (Error Shl 1)> DeltaX		'Would it overflow?
			
				Y:+YStep		'Move in Y
				
				Error=Error-DeltaX		'Overflow/wrap the counter
				
			EndIf
			
			If Steep Then 
			
				If checkcell (y,x) = True Then
				
					DrawRect y * w, x * w, w -1 ,w -1
					
				Else
				
					Exit
				
				End If
				
			Else 
			
				If checkcell (x,y) = True Then 
				
					DrawRect x * w, y * w, w -1 ,w -1
					
				Else
				
					Exit
				
				End If
				
			End If
			
		Wend
	
	End If
	
End Function

  
Function mouse()

	Local x:Int
	Local y:Int

	If 	MouseX() > 0 And ..
		MouseX() < (dungeon_x * w)  And ..
		MouseY() > 0 And ..
		MouseY() < (dungeon_y * w) Then
		
		x = MouseX() / w 
		y = MouseY() / w
		
		If MouseHit (1) Then
		
			If dungeon [x,y] = Null Then dungeon[x,y] = New cell
			
		End If
		
		If MouseHit (2) Then
		
			If dungeon [x,y] <> Null Then dungeon[x,y] = Null
			
		End If		
		
	End If

End Function	



The full topic title should be "Line of sight beam casting problem", btw.

Try www.roguelikedevelopment.org
Look for recursive line of sight algorithm