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 |