Grid based lightning
BlitzMax Forums/BlitzMax Programming/Grid based lightning
| ||
| Anyone has any example of a grid based lightning system? Basically what I have atm. is a world built out of 1's and 0's in an Array, 1 is a wall while 0 is nothing. And I need to figure out what the player can see, like in older games such as NetHack you can't see behind walls, and the area that you can see lights up. Ontop of that, I also need some sort of lightning system that's grid based. Kinda like what Terraria has, if anyone's seen that. ![]() But yeah, you guys think that would be hard to do? I'm horrible at math, so any help or tips would be much appreciated. Last edited 2011 |
| ||
| Rogue Basin has some articles on this. http://roguebasin.roguelikedevelopment.org/index.php/FOV And you could also try the LibTCod module which has a function for field of view if you don't want to roll your own. Brucey has a wrapper for it somewhere. |
| ||
| Yeah, I did have a look at LibTCod, but it feels very dirty having that entire thing running in the background. I prefer having everything made myself specifically for my needs. Thanks for the link, I'll have a look at it. :) |
| ||
| A simple hack, that a surprisingly large number of games use is just to have a visibility radius. Don't bother calculating if you can see through a wall or not. Saves a ton of time and the end result is not much different unless you're planning very tight maps with hidden things physically close to expected paths... not exactly what you're looking for but it's easy with less overhead so worth a mention. |
| ||
| How about. if your game is made up of blocks, say 16*16, they could be any size the block size does not matter, conver your world/map into a 1pixel by 1pixel image, where walls are represented by a red pixel, the player by a blue pixel and empty space by black pixels. kinda like a mini map. Now draw a line from the blue pixel to the edge of the map, or until it hits a red pixel, do this for the 360 degree's around the player, once your have done this light pass, read the pixel's any you read as white, are places your player can see, get the appropriate block from your world that corresponds to that white pixel and make it visible. It's the first thing I would try. |
| ||
| OH, I would also set the light value of a pixel, to be half of the light value of all 8 neighbouring pixels, that should glow your light a bit and let it bleed out to darkness. hope this helps. |
| ||
| Hello. I've got something for you, let me dig it out... Goodbye. |
| ||
| Hello. Here you go, something I knocked up a while ago inspired by my love of Larn - another Meh it went nowhere project :) It uses someone else's maze code (taken from the forums) and apologies for not crediting, but I can't remember whose. I was only going to use it for testing. As you'll appreciate, the code is pretty much brute force, not overly efficient and can be made so much better. Commenting would help, but again, Meh, never intended for public release. Be nice. As with anything I post here, it's free to use BUT I'd like crediting (oh, the irony) and would also appreciate any improvements be posted back onto the site. Be nice. Everything is self contained, there is extraneous code and well, enjoy.
Strict
Graphics 1024,768
Local x:Float
Local y:Float
Local mx,my:Float
Global cx,cy:Float
' Map Size Must Not be Odd
Const MaxX = 1024
Const MaxY = 1024
Global lines:Int = 4
Global lampsize:Int = 8
Local size:Int = 16 ' Image Size
Global Beast_Im:TImage = CreateImage(size,size)
Cls
SetColor 0,255,0
DrawOval 0,0,size,size
SetColor 0,192,0
DrawOval 0,0,size,size
GrabImage Beast_Im,0,0
Global Player_Im:TImage= CreateImage(size,size)
Cls
SetColor 255,0,0
DrawOval 0,0,size,size
SetColor 64,0,0
DrawOval 1,1,size-2,size-2
GrabImage(Player_Im,0,0)
Global wall_im:TImage = CreateImage(size,size)
Cls
SetColor 128,64,0
DrawRect 0,0,size,size
For Local i:Int = 0 To 100
Local x:Int = Rand(size)
Local y:Int = Rand(size)
SetColor 240+Rand(32)-16,192+Rand(128)-64,0
Plot x,y
Next
GrabImage(wall_im, 0,0)
Global floor_im:TImage = CreateImage(size,size)
Cls
SetColor 192,255,192
DrawRect 0,0,size,size
For Local i:Int = 0 To 100
Local x:Int = Rand(size)
Local y:Int = Rand(size)
SetColor 240+Rand(32)-16,192+Rand(128)-64,0
Plot x,y
Next
GrabImage floor_im,0,0
Global Object_Im:TImage = CreateImage(size,size)
Cls
SetColor 255,255,255
DrawOval 0,0,size,size
SetColor 255,255,0
DrawOval 1,1,size-2,size-2
GrabImage(Object_im,0,0)
Local ccx,ccy:Float
Local mmx,mmy:Float
Local icx,icy,imx,imy:Int
Type PlayerType
Field x:Int
Field y:Int
Field freq:Int
Field Current:Int
End Type
Global p:playertype = New PlayerType
p.freq = 2
p.Current = p.freq
Type MapType
Field x:Int
Field y:Int
Field Blocked:Int
Field visible:Int
Field visited:Int
Field Done:Int
Field Dist:Float
Field Shadow:Float
End Type
Type ObjectType
Field x:Int
Field y:Int
Field Id:Int
End Type
Global ObjectList:TList = New TList
Type BeastType
Field x:Int
Field y:Int
Field xd:Int
Field yd:Int
Field freq:Int
Field Current:Int
End Type
Global BeastList:TList = New TList
Local Maze[,] = MakeMaze(MaxX-1,MaxY-1,0,Rand(10))
Global Map:Maptype[MaxX,MaxY]
Local n:Int
For Local y:Int = 0 To Maxy-1
For Local x:Int = 0 To MaxX-1
Map[x,y] = New MapType
Map[x,y].Visible = False
Next
Next
For Local y:Int = 0 To MaxY-1
For Local x:Int = 0 To MaxX-1
Map[x,y].Blocked = Maze[x,y]
Next
Next
MakeMap lines
Repeat
cx = Rand(Maxx-1)
cy = Rand(MaxY-1)
Until Map[cx,cy].Blocked = False
Local ms1,ms2:Int
Local ems1,ems2:Int
Global los_list:TList = New TList
Global vis_list:TList = New TList
Local ocx,ocy:Int
MakeObjects
MakeBeasts
While Not KeyDown(Key_Escape)
Cls
Local MainTimer:Int = MilliSecs()
p.Current:-1
If p.Current = 0 Then
p.Current = p.freq
If KeyDown(key_left) Then cx=cx-1
If KeyDown(key_right) Then cx=cx+1
If KeyDown(key_up) Then cy=cy-1
If KeyDown(key_down) Then cy=cy+1
If cx<0 Or cx>MaxX-1 Or cy<0 Or cy>MaxY-1 Or Map[cx,cy].Blocked = True Then
cx = ocx
cy = ocy
End If
icx = Int(cx)
icy = Int(cy)
End If
ClearShadows
If icx<>ocx Or icy<>ocy Then
ms1 = MilliSecs()
For Local y:Int = icy-(lampsize+1) To icy+(lampsize+1)
For Local x:Int = icx-(lampsize+1) To icx+(lampsize+1)
If x>=0 And x<=MaxX-1 And y>=0 And y<=MaxY-1 Then
Map[x,y].Visited = 0
Local dist:Float = Sqr( ((cx-x)*(cx-x)) + ((cy-y)*(cy-y)))
If dist<lampsize Then
Map[x,y].Visible = False
Map[x,y].Dist = dist
Map[x,y].Shadow = dist
Else
Map[x,y].Visited = 1
Map[x,y].visible = False
Map[x,y].Dist = lampsize + 1
End If
End If
Next
Next
vis_list.clear
Local map_item:MapType = New MapType
map_item.x = icx
map_item.y = icy
map_item.visible = True
map_item.visited = 1
map_item.done = 0
los_list.clear()
los_list.addfirst map_item
Local counter = 1
While counter<>0 And Not(KeyDown(key_escape))
counter = 0
For Local li:MapType = EachIn los_list
Local lx,ly:Int
lx = li.x
ly = li.y
map[lx,ly].visible = True
If li.done = 0 Then
li.done = 1
If lx>0 Then
If Map[lx-1,ly].visited = 0 Then
map[lx-1,ly].visited = 1
map[lx-1,ly].visible = True
If map[lx-1,ly].blocked = False Then
map_item = New maptype
map_item.x = lx-1
map_item.y = ly
los_list.addlast map_item
Else
DrawBlockLineHeader icx,icy,lx-1,ly,1
End If
End If
End If
If lx<MaxX-1 Then
If Map[lx+1,ly].visited = 0 Then
map[lx+1,ly].visited = 1
map[lx+1,ly].visible = True
If map[lx+1,ly].blocked = False Then
map_item = New maptype
map_item.x = lx+1
map_item.y = ly
los_list.addlast map_item
Else
DrawBlockLineHeader icx,icy,lx+1,ly,1
End If
End If
End If
If ly>0 Then
If Map[lx,ly-1].visited = 0 Then
map[lx,ly-1].visited = 1
map[lx,ly-1].visible = True
If map[lx,ly-1].blocked = False Then
map_item = New maptype
map_item.x = lx
map_item.y = ly-1
los_list.addlast map_item
Else
DrawBlocklineHeader icx,icy,lx,ly-1,1
End If
End If
End If
If ly<MaxY-1 Then
If Map[lx,ly+1].visited = 0 Then
map[lx,ly+1].visited = 1
map[lx,ly+1].visible = True
If map[lx,ly+1].blocked = False Then
map_item = New maptype
map_item.x = lx
map_item.y = ly+1
los_list.addlast map_item
Else
DrawBlocklineHeader icx,icy,lx,ly+1,1
End If
End If
End If
counter:+1
End If
Next
Wend
For Local vi:MapType = EachIn Vis_List
Local ix:Int = vi.x
Local iy:Int = vi.y
If Map[ix,iy].Blocked = True Then
If ix>0 Then
If map[ix-1,iy].Visible = True And Map[ix-1,iy].Blocked = False Then Map[ix,iy].Visible = True
End If
If ix<MaxX- 1 Then
If map[ix+1,iy].Visible = True And Map[ix+1,iy].Blocked = False Then Map[ix,iy].Visible = True
End If
If iy>0 Then
If map[ix,iy-1].Visible = True And Map[ix,iy-1].Blocked = False Then Map[ix,iy].Visible = True
End If
If iy<MaxY-1 Then
If map[ix,iy+1].Visible = True And Map[ix,iy+1].Blocked = False Then Map[ix,iy].Visible = True
End If
End If
Next
For Local iy:Int = cy-1 To cy+1
For Local ix:Int = cx-1 To cx+1
If ix>=0 And ix<MaxX And iy>=0 And iy<MaxY Then
Map[ix,iy].Visible = True
End If
Next
Next
ems1 = MilliSecs()
End If
For Local obj:ObjectType = EachIn ObjectList
If map[obj.x,obj.y].visible = True And map[obj.x,obj.y].dist<5 Then
If cx<>obj.x Or cy<>obj.y Then
DrawBlockLineHeader Int(cx),Int(cy),Int(obj.x),Int(obj.y),2
End If
End If
Next
For Local b:BeastType = EachIn BeastList
b.Current:-1
If b.Current<=0 Then
b.Current = b.freq
Local obx:Int = b.x
Local oby:Int = b.y
b.x:+b.xd
b.y:+b.yd
If map[b.x,b.y].blocked = True Then
b.x = obx
b.y = oby
b.xd = Rand(3)-2
b.yd = Rand(3)-2
If b.xd = 0 And b.yd = 0 Then b.yd = 1
End If
End If
If map[b.x,b.y].visible = True Then 'And map[b.x,b.y].dist<5 Then
If cx<>b.x Or cy<>b.y Then
DrawBlockLineHeader Int(cx),Int(cy),Int(b.x),Int(b.y),2
End If
End If
Next
Local EndMainTimer:Int = MilliSecs()
If endMainTimer-MainTimer<50 Then
While EndMainTimer-MainTimer<50
EndMainTimer = MilliSecs()
Wend
Else
DebugLog "Took longer than 200: " + (EndMainTimer-MainTimer)
End If
Cls
SetOrigin 512-icx*size, 384-icy*size
SetBlend lightblend
ms2 = MilliSecs()
DrawMapArray size
ems2 = MilliSecs()
SetBlend alphablend
For Local obj:ObjectType = EachIn ObjectList
If map[obj.x,obj.y].visible = True Then
Local col:Float = 255-(map[obj.x,obj.y].dist * (256/lampsize))
SetColor col,col,col
DrawImage Object_im,obj.x * size, obj.y * size
End If
Next
For Local b:BeastType = EachIn BeastList
If map[b.x,b.y].visible = True Then
Local col:Float = 255-(map[b.x,b.y].dist * (256/lampsize))
SetColor col,col,col
DrawImage Beast_im,b.x * size, b.y * size
End If
Next
SetColor 255,255,255
SetBlend alphablend
DrawImage Player_Im, icx*size, icy*size
Flip
ocx = cx
ocy = cy
SetOrigin 0,0
Wend
End
Function ClearShadows()
For Local y:Int = cy-(lampsize+1) To cy+(lampsize+1)
For Local x:Int = cx-(lampsize+1) To cx+(lampsize+1)
If x>=0 And x<=MaxX-1 And y>=0 And y<=MaxY-1 Then
If Map[x,y].visible = True Then
Map[x,y].Shadow = map[x,y].dist
End If
End If
Next
Next
SetColor 255,255,255
End Function
Function DrawMapArray(size:Int=16)
For Local y:Int = cy-(lampsize+1) To cy+(lampsize+1)
For Local x:Int = cx-(lampsize+1) To cx+(lampsize+1)
If x>=0 And x<=MaxX-1 And y>=0 And y<=MaxY-1 Then
Local col:Float = 255-(map[x,y].Shadow * (256/lampsize))
SetColor col,col,col
If Map[x,y].visible = True Then
If Map[x,y].Blocked = True Then
DrawImage wall_im,x * size, y * size
Else
DrawImage Floor_im, x * size, y*size
EndIf
End If
End If
Next
Next
SetColor 255,255,255
End Function
Function DrawBlockLineHeader(x#,y#,mx#,my#,blocktype:Int)
' blocktype
' 1 = Wall
' 2 = Object
DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 8,my * 16 + 8,blocktype ' Centre
DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 4,my * 16 + 4,blocktype ' Top Left
DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 12, my * 16 + 4,blocktype 'Top right
DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 4,my * 16 + 12,blocktype ' Bottom Left
DrawBlockLine2 x * 16 + 8, y * 16 + 8, mx * 16 + 12, my * 16 + 12,blocktype 'Bottom right
End Function
Function DrawBlockLine2(x#,y#,mx#,my#, blocktype:Int)
'If blocktype = 2 Then DebugLog "Entering Blocktype = " + blocktype
Local vecx:Float = mx-x
Local vecy:Float = my-y
Local xs:Float
Local ys:Float
Local draw_it:Int = 0
Local drawn:Int = 0
Local current_x:Float
Local current_y:Float
'If blocktype = 2 Then DebugLog " Vec X: " + vecx + " Vec Y: " + vecy
If vecx<>0 And vecy<>0 Then
'It's not an even boundary/straight line
If Abs(vecx)=Abs(vecy) Then
' It's an even diagonal line
xs = vecx/Abs(vecx)
ys = vecy/Abs(vecy)
draw_it = 1
Else
If Abs(vecx)>Abs(vecy) Then
' XVector is greater
xs = (vecx/Abs(vecx))/2
ys = (vecy/Abs(vecx))/2
draw_it = 1
Else
' YVector is greater
ys = (vecy/Abs(vecy))/2
xs = (vecx/Abs(vecy))/2
draw_it = 1
End If
End If
Else
'It's an even boundary
If vecx = 0 And vecy = 0 Then
xs = 0
ys = 0
' If blocktype = 2 Then DebugLog "Both vecx and vecy = 0" ; DebugStop
ElseIf vecx = 0 Then
ys = vecy/Abs(vecy)
xs = 0
draw_it = 1
' If blocktype = 2 Then DebugLog "vecx = 0"; DebugStop
ElseIf vecy = 0 Then
xs = vecx/Abs(vecx)
ys = 0
draw_it = 1
' If blocktype = 2 Then DebugLog "vecy = 0" ; DebugStop
End If
End If
Local icx,icy:Int
Local oicx,oicy:Int
Local px,py:Int
px = Int(x/16)
py = Int(y/16)
Local count:Int = 0
Local shadaddValue:Int = 5
'If blocktype=2 Then DebugLog blocktype
If draw_it = 1 Then
current_x = mx
current_y = my
While Not drawn
current_x:+xs
current_y:+ys
icx = Int(current_x/16)
icy = Int(current_y/16)
If icx<0 Or icx>MaxX-1 Or icy<0 Or icy>MaxY-1 Then
drawn = True
' DebugLog "here"
Else
If map[icx,icy].dist>lampsize Then
drawn = True
' DebugLog "here2"
Else
Select blocktype
Case 1
If Map[icx,icy].Visited = False Then
Map[icx,icy].Visible = False
Map[icx,icy].Visited = True
If map[icx,icy].Blocked = True Then
Local mi:MapType = New MapType
mi.x = icx
mi.y = icy
vis_List.addlast mi
End If
End If
Case 2
' DebugLog "here3"
If oicx<>icx Or oicy<>icy Then
If map[icx,icy].Blocked = False Then
Map[icx,icy].Shadow = Map[icx,icy].Dist + shadaddvalue
shadaddvalue:-1
If shadaddvalue = 0 Then drawn = True
' DebugLog "ICX: " + icx + " ICY: " + icy + " Shadow: " + Map[icx,icy].Shadow
oicx = icx
oicy = icy
Else
drawn = True
End If
End If
End Select
End If
End If
Wend
End If
'DebugLog "-------"
End Function
Function MakeBeasts()
For Local i:Int = 0 To MaxY
Local b:BeastType = New BeastType
Local x:Int
Local y:Int
Local xd:Int
Local yd:Int
Local done:Int = 0
While done = 0
x = Rand(MaxX-1)
y = Rand(MaxY-1)
If map[x,y].blocked=False Then done = 1
Wend
b.x = x
b.y = y
done = 0
While done = 0
xd = Rand(3)-2
yd = Rand(3)-2
If xd<>0 Or yd<>0 Then done = 1
Wend
b.xd = xd
b.yd = yd
b.freq = Rand(10)
b.Current = b.freq
' DebugLog "XD: " + xd + " YD: " + yd
BeastList.AddLast b
Next
End Function
Function MakeObjects()
For Local i:Int = 0 To MaxY '* 10
Local obj:ObjectType = New ObjectType
Repeat
obj.x = Rand(MaxX-1)
obj.y = Rand(MaxY-1)
Until Map[obj.x,obj.y].Blocked = False
obj.Id = 1
ObjectList.AddLast obj
Next
End Function
Function MakeMap(lines)
For Local i:Int = 1 To (MaxX)
Local xs1 = Rand(MaxX-1)
Local ys1 = Rand(MaxY-1)
Map[xs1,ys1].Blocked = True
Next
End Function
#MapData
DefData 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,1,0,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1
DefData 1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,0,0,0,0,0,0,1
DefData 1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1
DefData 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1
DefData 1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1
DefData 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1
DefData 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
'MAKEMAZE()
'Returns a 2D array of integers. 1=wall. 0=open space.
'OPEN tells how wide the space between walls are
'If OPEN is large the mazes tend to look the same
'when EXACT is 1 a maze wall can only make a turn on an odd number cell
'EXACT=1
'#
'###
' #
' #
'EXACT=0
'#
'##
' ##
' #
'ex. when EXACT is 5, a wall can only be put on every fifth cell
'#####
' #
' #
' #
' ######
Function MakeMaze:Int[,](width,height,exact=0,open=1)
Local fx,fy,allx,ally
Local go,farx,fary,alldir
Local dir,ok,f,f2
Local x,y,x2,y2
Local did,didmax
Local mx2,my2
Local mx=width,my=height
Global movex[4],movey[4]
Local cango[4]
Local candir,candir2
If exact = 1 Then
If (my & 1)=0 Or (mx & 1)=0 Then RuntimeError "maze size must be odd for exact mazes"
EndIf
If open<1 Then RuntimeError "open must be greater than 0"
movex[0]=+1
movey[1]=+1
movex[2]=-1
movey[3]=-1
Local maze[,]
maze=New Int[mx+1,my+1]
For fx=1 To mx
maze[fx,1]=1
maze[fx,my]=1
Next
For fy=1 To my
maze[1,fy]=1
maze[mx,fy]=1
Next
farx=mx
fary=my
ally=1
allx=1
exact:+1
didmax=mx*my
mx2=mx+exact+1
my2=my+exact+1
Repeat
Repeat
allx=Rand(1,mx2)
ally=Rand(1,my2)
allx=allx/exact*exact-1
ally=ally/exact*exact-1
If allx<1 Then allx=1
If ally<1 Then ally=1
If allx>mx Then allx=mx
If ally>my Then ally=my
If maze[allx,ally]=1 Then Exit
Forever
did:+1
If did>didmax Then Exit
x=allx
y=ally
dir=Rand(0,3)
Repeat
If Rand(didmax)=1 Then Exit
ok=0
For candir=0 To 3
For f=1 To exact+open
x2=x+movex[candir]*f
y2=y+movey[candir]*f
If x2<=0 Or y2<=0 Or x2>mx Or y2>my Then f=9999;Exit
If maze[x2,y2]=1 Then f=9999;Exit
For f2=1 To open
candir2=(candir+1) & 3
If maze[x2+movex[candir2]*f2,y2+movey[candir2]*f2]=1 Then f=9999;Exit
candir2=(candir-1) & 3
If maze[x2+movex[candir2]*f2,y2+movey[candir2]*f2]=1 Then f=9999;Exit
Next
Next
cango[candir]=(f<9999)
If cango[candir] Then ok=1
Next
If ok=0 Then Exit
Repeat
dir=(dir+Rand(-1,+1)) & 3
If cango[dir]=1 Then Exit
Forever
For f=1 To exact
x:+movex[dir]
y:+movey[dir]
maze[x,y]=1
Next
Forever
Forever
Return maze
EndFunction
|
| ||
| Whoa, many thanks SoggyP!! If I do end up using your code you'll surely get all the credits, even if it's just a small fraction of it I'll actually use. :) Last edited 2011 |
| ||
| Hello. No worries, it's a good feeling being able to help after such a long time. Goodbye. |
| ||
| Right so... Even after all this help, example code and advice, I STILL can't get it to work correctly! I have something that "almost" works but is far from perfect. The problem are all the artifacts you get when viewing stuff from the side. I've whipped together this example code to illustrate the problem. Now when you try this code... Remember to firstly draw some floor (left click) and then draw some walls (left click on floor). You can remove floor and walls with the right mouse buttons. Move the player around with arrow keys. Const Grid:Int = 32 'You may change this for bigger levels Global Map:Int[25, 19, 2] 'X,Y,Layer (Layer 0 is floor while 1 is wall) Global MouseAction:Int 'Mouse action for removing/adding walls/floors Global PlayerX:Int = 12 Global PlayerY:Int = 9 Graphics(800, 600, 0, 0, 2) SetBlend(ALPHABLEND) 'Alpha blend is needed to cover stuff with "shadows" 'Fill the map with floor tiles For Local Y:Int = 0 To 19 - 1 For Local X:Int = 0 To 25 - 1 Map[X, Y, 0] = 1 Next Next While Not KeyDown(KEY_ESCAPE) DoMouseAction() 'Check what the mouse does (debug) RenderMap() 'Render the entire map RenderFov() 'Throw black boxes over it all for "shadows" 'Player stuff DrawRect(PlayerX * Grid, PlayerY * Grid, Grid, Grid) If KeyHit(KEY_UP) And Map[PlayerX, PlayerY - 1, 0] And Not Map[PlayerX, PlayerY - 1, 1] Then PlayerY:-1 If KeyHit(KEY_DOWN) And Map[PlayerX, PlayerY + 1, 0] And Not Map[PlayerX, PlayerY + 1, 1] Then PlayerY:+1 If KeyHit(KEY_LEFT) And Map[PlayerX - 1, PlayerY, 0] And Not Map[PlayerX - 1, PlayerY, 1] Then PlayerX:-1 If KeyHit(KEY_RIGHT) And Map[PlayerX + 1, PlayerY, 0] And Not Map[PlayerX + 1, PlayerY, 1] Then PlayerX:+1 'Normal render stuff... :3 Flip() Cls() Wend Function RenderMap() 'Run through the entire array For Local Y:Int = 0 To 19 - 1 For Local X:Int = 0 To 25 - 1 'Floor SetColor(50, 10, 50) If Map[X, Y, 0] DrawRect(X * Grid, Y * Grid, Grid, Grid) EndIf 'Wall SetColor(100, 255, 255) If Map[X, Y, 1] DrawRect(X * Grid, Y * Grid, Grid, Grid) EndIf Next Next SetColor(255, 255, 255) 'RESET! \o/ End Function Function RenderFov() 'Set the color and alpha for the shadows SetColor(0, 0, 0) SetAlpha(0.5) 'Loop throught the ENTIRE map array! :o '(This should of course be optimized to work with a range instead of the entire array) For Local Y:Int = 0 To 19 - 1 For Local X:Int = 0 To 25 - 1 If Map[X, Y, 1] Or Map[X, Y, 0] Then 'If there's floor or a wall... If Not CheckFov(X, Y) Then DrawRect(X * Grid, Y * Grid, Grid, Grid) 'Check if it's visible, and if it's not... draw a black box! EndIf Next Next 'Reset drawing stuffzorz SetAlpha(1) SetColor(255, 255, 255) End Function Function CheckFov(X:Int, Y:Int) 'This is stolen from http://roguebasin.roguelikedevelopment.org/index.php/Eligloscode 'Basically what we do is a "linepick" between the player and the tile 'and if there's a block in the way we return False, otherwise we return True Local vx:Float Local vy:Float Local ox:Float Local oy:Float Local l:Float vx = PlayerX - x vy = PlayerY - y ox = X + 0.5 oy = Y + 0.5 l = Sqr((PlayerX - X) ^ 2 + (PlayerY - Y) ^ 2) vx:/l vy:/l For Local i:Int = 0 To l If Map(Floor(ox), Floor(oy), 1) Return(False) EndIf ox:+vx oy:+vy Next Return(True) End Function Function DoMouseAction() 'This function is unimportant as it's only for debugging... If MouseHit(1) Then MouseAction = 0 If Not Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 1;Print "Making floor" If Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 2;Print "Making wall" EndIf If MouseHit(2) Then MouseAction = 0 If Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 3;Print "Removing floor" If Map[MouseX() / Grid, MouseY() / Grid, 1] > 0 Then MouseAction = 4;Print "Removing wall" EndIf If MouseAction < 3 And Not MouseDown(1) Then MouseAction = 0 If MouseAction >= 3 And Not MouseDown(2) Then MouseAction = 0 Select MouseAction Case 1 Map[MouseX() / Grid, MouseY() / Grid, 0] = 1 Case 2 Map[MouseX() / Grid, MouseY() / Grid, 1] = 1 Case 3 Map[MouseX() / Grid, MouseY() / Grid, 0] = Null Case 4 Map[MouseX() / Grid, MouseY() / Grid, 1] = Null End Select End Function Last edited 2011 |
| ||
| Here's a picture of the problem for those of you who can't understand my ramblings. The green areas has some nasty spread shadows... ![]() Last edited 2011 Last edited 2011 |
| ||
| .. Last edited 2011 |
| ||
| Your code works perfectly on my machine, it may be a bug or driver issue, not sure actually its prety straight forward stuff. But yeah its working fine here. |
| ||
| Give the code a go again Taiphoz, I solved one issue and updated it... Another issue now. x) |
| ||
|
| ||
| Nope. it's all looking good m8. nothing wrong with the code I am running. and tested after your last update so.. what's the issue ? |
| ||
| If you take one step to the left it should show some nasty jittery shadows. It's standing near edges that displays it. Have a look at the picture I posted above (Updated that too heh...) It's also very visible if you place a wall directly AT the player! :o Last edited 2011 |
| ||
| Take each Pixel that's light, and look around it, if its next to a shadow pixel, then change the light value of that shadow pixel to be HALF of the light pixel its touching. So, it will render out like this. [Light][half light][quarter light][black] Make the values additive, so 1 black pixel between 2 light pixels will become a light pixel, and you will no longer get that jaggy shadow. plus you will get nice blending of your shadows. |
| ||
| I don't mind the shadows being 100% "black" and not fading out. Adding shading to it wouldn't really "fix" it, it'd just cover it up a bit and I believe the issue would still exist if I did faded shadows, we'd just end up with scattered faded shadows. x) The issue has to do with the "line picking" not being very precise and my lack of math skills. heh Last edited 2011 |
| ||
| Well... All problems have been solved and I've fully integrated this into my game, much more optimized though of course. If anyone's interested, here's the updated example with working line-of-sight. Thanks everyone for the help, and feel free to improve on this if you want. Post result here though! :) Const Grid:Int = 32 'You may change this for bigger levels Global Map:Int[25, 19, 2] 'X,Y,Layer (Layer 0 is floor while 1 is wall) Global MouseAction:Int 'Mouse action for removing/adding walls/floors Global PlayerX:Int = 12 Global PlayerY:Int = 9 Graphics(800, 600, 0, 0, 2) SetBlend(ALPHABLEND) 'Alpha blend is needed to cover stuff with "shadows" 'Fill the map with floor tiles For Local Y:Int = 0 To 19 - 1 For Local X:Int = 0 To 25 - 1 Map[X, Y, 0] = 1 Next Next While Not KeyDown(KEY_ESCAPE) DoMouseAction() 'Check what the mouse does (debug) RenderMap() 'Render the entire map RenderFov() 'Throw black boxes over it all for "shadows" 'Player stuff DrawRect(PlayerX * Grid, PlayerY * Grid, Grid, Grid) If KeyHit(KEY_UP) And Map[PlayerX, PlayerY - 1, 0] And Not Map[PlayerX, PlayerY - 1, 1] Then PlayerY:-1 If KeyHit(KEY_DOWN) And Map[PlayerX, PlayerY + 1, 0] And Not Map[PlayerX, PlayerY + 1, 1] Then PlayerY:+1 If KeyHit(KEY_LEFT) And Map[PlayerX - 1, PlayerY, 0] And Not Map[PlayerX - 1, PlayerY, 1] Then PlayerX:-1 If KeyHit(KEY_RIGHT) And Map[PlayerX + 1, PlayerY, 0] And Not Map[PlayerX + 1, PlayerY, 1] Then PlayerX:+1 'Normal render stuff... :3 Flip() Cls() Wend Function RenderMap() 'Run through the entire array For Local Y:Int = 0 To 19 - 1 For Local X:Int = 0 To 25 - 1 'Floor SetColor(50, 10, 50) If Map[X, Y, 0] DrawRect(X * Grid, Y * Grid, Grid, Grid) EndIf 'Wall SetColor(100, 255, 255) If Map[X, Y, 1] DrawRect(X * Grid, Y * Grid, Grid, Grid) EndIf Next Next SetColor(255, 255, 255) 'RESET! \o/ End Function Function RenderFov() 'Set the color and alpha for the shadows SetColor(0, 0, 0) SetAlpha(0.5) Local X:Int Local Y:Int 'Loop throught the ENTIRE map array! :o '(This should of course be optimized to work with a range instead of the entire array) For Y= 0 To 19 - 1 For X= 0 To 25 - 1 If Map[X, Y, 1] Or Map[X, Y, 0] Then 'If there's floor or a wall... 'Check if it's visible, and if it's not... draw a black box! If Not CheckFov(X, Y) Then DrawRect(X * Grid, Y * Grid, Grid, Grid) EndIf Next Next rem Smooth attempt SetAlpha(0.5) For Y = 0 To 19 - 1 For X= 0 To 25 - 1 If Map[X, Y, 1] Or Map[X, Y, 0] Then 'If there's floor or a wall... If Not CheckFov(X+1, Y+1) Then DrawRect(X * Grid, Y * Grid, Grid, Grid) If Not CheckFov(X-1, Y+1) Then DrawRect(X * Grid, Y * Grid, Grid, Grid) If Not CheckFov(X-1, Y-1) Then DrawRect(X * Grid, Y * Grid, Grid, Grid) If Not CheckFov(X + 1, Y - 1) Then DrawRect(X * Grid, Y * Grid, Grid, Grid) EndIf Next Next endrem 'Reset drawing stuffzorz SetAlpha(1) SetColor(255, 255, 255) End Function Function CheckFov(X:Int, Y:Int) Local vx:Float Local vy:Float Local ox:Float Local oy:Float Local l:Float vx = PlayerX - x vy = PlayerY - y ox = X + 0.5 oy = Y + 0.5 l = Max(Abs(PlayerX - X),Abs(PlayerY - Y)) For Local i:Int = 0 To l - 1 If .. Map(Floor(ox+vx*(i/l)-0.0001), Floor(oy+vy*(i/l)-0.0001), 1) Or .. Map(Floor(ox+vx*(i/l)+0.0001), Floor(oy+vy*(i/l)-0.0001), 1) Or .. Map(Floor(ox+vx*(i/l)+0.0001), Floor(oy+vy*(i/l)+0.0001), 1) Or .. Map(Floor(ox+vx*(i/l)-0.0001), Floor(oy+vy*(i/l)+0.0001), 1) .. Then Return(False) Next Return(True) End Function Function DoMouseAction() 'This function is unimportant as it's only for debugging... If MouseHit(1) Then MouseAction = 0 If Not Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 1;Print "Making floor" If Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 2;Print "Making wall" EndIf If MouseHit(2) Then MouseAction = 0 If Map[MouseX() / Grid, MouseY() / Grid, 0] > 0 Then MouseAction = 3;Print "Removing floor" If Map[MouseX() / Grid, MouseY() / Grid, 1] > 0 Then MouseAction = 4;Print "Removing wall" EndIf If MouseAction < 3 And Not MouseDown(1) Then MouseAction = 0 If MouseAction >= 3 And Not MouseDown(2) Then MouseAction = 0 Select MouseAction Case 1 Map[MouseX() / Grid, MouseY() / Grid, 0] = 1 Case 2 Map[MouseX() / Grid, MouseY() / Grid, 1] = 1 Case 3 Map[MouseX() / Grid, MouseY() / Grid, 0] = Null Case 4 Map[MouseX() / Grid, MouseY() / Grid, 1] = Null End Select End Function Last edited 2011 |

