Code archives/3D Graphics - Misc/miniB3D Simple Dungeon
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
An example of an old school randomly generated Wolfenstein-3D type dungeon. | |||||
Import sidesign.minib3d Strict SeedRnd MilliSecs() '////////////////////////////////////////////////////////////////////// '// Simple 3d Dungeon 1.1 by Jeff Hanson // '// ------------------------------------ // '// This uses Ryan Burnside's Simple Dungeon Engine Release 1.0 from // '// the code archives to generate a simple 3d dungeon that you can // '// walk around in using the arrow keys (and Escape to quit). It // '// requires MiniB3D version 0.51. // '// // '// You can play with the dungeon generation variables to change the // '// characteristics of the dungeon. // '// // '// And sorry for the sloppy code. It's just meant to be a quick // '// and dirty example, not a masterpiece. // '////////////////////////////////////////////////////////////////////// '////////////////////////////////////////////////////////////////////// '// Version History // '// --------------- // '// 1.0 - Initial Release // '// 1.1 - Fixed ground problem by dividing ground into several // '// chunks when the map gets big. // '// - Added texture ability (shut off by default) // '// // '////////////////////////////////////////////////////////////////////// Graphics3d 800, 600, 32, 2 Const USETEXTURES:Int = 0 '0=don't use textures, 1=use textures Const GRAVITY:Float = -0.3 'apply gravity 'start dungeon generation variables Global dungeonWidth:Int = 30 Global dungeonHeight:Int = 30 Global roomSizeMin:Int = 3 Global roomSizeMax:Int = 10 Global numberRooms:Int = Rand(5,12) 'end dungeon generation variables Global ground:TMesh[Ceil((dungeonWidth+dungeonHeight)/20)+1] Global ceiling:TMesh[Ceil((dungeonWidth+dungeonHeight)/20)+1] Global wall:TMesh = CreateMesh() Global camera:TCamera = CreateCamera() Global light:TLight = CreateLight(2, camera) For Local i:Int = 0 To Ceil((dungeonWidth+dungeonHeight)/20) ground[i] = CreateMesh() ceiling[i] = CreateMesh() Next 'dungeon generation code Local d:dungeon = create_dungeon(dungeonWidth, dungeonHeight, numberRooms, roomSizeMin, roomSizeMax, roomSizeMin, roomSizeMax) ' seed the rooms with 1 player starting point, we will call this value "2" add_value(d, 2, 1) 'remove all of the walls that will never be seen by the player remove_blocked(d, dungeonWidth, dungeonHeight) 'Here's where you load your textures 'Make sure to put them where the program can find them, or it'll fail Global texture:TTexture[3] If USETEXTURES Then texture[0]=LoadTexture("ground.jpg") texture[1]=LoadTexture("wall.jpg") texture[2]=LoadTexture("ceiling.jpg") EndIf 'create a 3d version of the dungeon map makeDungeon(d) EntityType camera, 1 EntityRadius camera, 0.9 CameraRange camera, 0.1, 51.0 CameraFogMode camera, 1 CameraFogRange camera, 0.1, 50 CameraFogColor camera, 0,0,0 'turning on the light range seems to cause some artifacts on the screen 'not sure why. you can uncomment it and try it though. 'LightRange light, 10 Collisions 1, 2, 2, 2 While Not KeyDown (KEY_ESCAPE) If KeyDown (KEY_LEFT) Then TurnEntity camera, 0, 2.0, 0 ElseIf KeyDown (KEY_RIGHT) Then TurnEntity camera, 0, -2.0, 0 EndIf If KeyDown (KEY_UP) Then MoveEntity camera, 0, 0, 0.3 ElseIf KeyDown (KEY_DOWN) Then MoveEntity camera, 0, 0, -0.3 EndIf MoveEntity camera, 0, GRAVITY, 0 UpdateWorld() RenderWorld() Flip 1 Wend 'FUNCTIONS Function makeDungeon (d:dungeon) Local master:TMesh = CreateCube() For Local i = 0 To d.array.dimensions()[0] - 1 For Local j = 0 To d.array.dimensions()[1] - 1 If d.array[i, j] Select d.array[i, j] Case 1 'wall Local tempblock:TMesh = CopyMesh(master) PositionMesh tempblock, i*2, 2, j*2 AddMesh tempblock, wall FreeEntity tempblock Case 2 'player starting point PositionEntity camera, i*2, 2, j*2 EndSelect End If 'ground Local temp_obj:TMesh = CopyMesh(master) PositionMesh temp_obj, i*2, 0, j*2 AddMesh temp_obj, ground[Floor((j+i)/20)] FreeEntity temp_obj 'ceiling Local temp_obj2:TMesh = CopyMesh(master) PositionMesh temp_obj2, i*2, 4, j*2 AddMesh temp_obj2, ceiling[Floor((j+i)/20)] FreeEntity temp_obj2 Next Next 'block the outside edges of the map, since the dugeon generator 'will sometimes put rooms against the edge, and you don't want 'anyone falling off the map For Local xa:Int = -1 To dungeonWidth Local temp_obj3:TMesh = CopyMesh(master) PositionMesh temp_obj3, xa*2, 2, -2 Local temp_obj4:TMesh = CopyMesh(master) PositionMesh temp_obj4, xa*2, 2, (dungeonHeight)*2 AddMesh temp_obj3, wall AddMesh temp_obj4, wall FreeEntity temp_obj3 FreeEntity temp_obj4 Next For Local ya:Int = -1 To dungeonHeight Local temp_obj3:TMesh = CopyMesh(master) PositionMesh temp_obj3, -2, 2, ya*2 Local temp_obj4:TMesh = CopyMesh(master) PositionMesh temp_obj4, dungeonWidth*2, 2, ya*2 AddMesh temp_obj3, wall AddMesh temp_obj4, wall FreeEntity temp_obj3 FreeEntity temp_obj4 Next For Local i:Int = 0 To Ceil((dungeonWidth+dungeonHeight)/20) EntityType ground[i], 2 EntityType ceiling[i], 2 If USETEXTURES Then EntityTexture ground[i], texture[0] EntityTexture ceiling[i], texture[2] Else EntityColor ground[i], 0,150,0 EntityColor ceiling[i], 0, 0, 150 EndIf Next EntityType wall, 2 If USETEXTURES Then EntityTexture wall, texture[1] Else EntityColor wall, 150, 0, 0 EndIf EndFunction Function remove_blocked(d:dungeon, width:Int, height:Int) Local tempArray:Int[width, height] For Local x:Int = 0 To width-1 For Local y:Int = 0 To height-1 If x = 0 Or x = width-1 Or y=0 Or y=height-1 Then 'at edge, do nothing Else If d.array[x,y] = 1 Then If d.array[x-1,y] = 1 And d.array[x+1,y] = 1 And d.array[x,y-1] = 1 And d.array[x,y+1] = 1 Then temparray[x,y]=1 EndIf EndIf EndIf Next Next For Local x2:Int = 0 To width-1 For Local y2:Int = 0 To height-1 If temparray[x2,y2]=1 Then d.array[x2,y2] = 0 EndIf Next Next EndFunction '......................................................................................................... ' Ryan Burnside Dungeon Engine Release 1.0 ' ***Please credit "Ryan Burnside" if possible.*** ' This dungeon generator is meant to do just that, nothing more and nothing less ' a dungeon holds an array and rooms that overlay spaces in the array ' the dungeon array is simply called "array" ' this array holds values for walls floor and will eventually hold special data for tiles ' it is up to the programmer to defing meaningful conventions for special values ' rooms can be seeded with special values using the "add_value" command ' it is important to note that the add_value command will overwrite values if the random room spot is taken ' because of this you will want to make your stairs last and items and monster values first ' once an item is obtained you will want to set the square back to 0 in the dungeon array '......................................................................................................... ' A wrapper object for an array and holder object for room instances Type dungeon Field array:Byte[,] , rooms:TList = New TList, rooms_maxed:Byte = False, room_count:Int = 0 Method add_room(min_width:Int, min_height:Int, max_width:Int, max_height:Int) Local array_width:Int = array.dimensions()[0] Local array_height:Int = array.dimensions()[1] ' first ensure that the room is not larger than the array If max_width > array_width max_width = array_width End If If max_height > array_height max_height = array_height End If ' ensure that the min values are larger than 0 If min_width < 2 min_width = 2 If min_height < 2 min_height = 2 ' now set the size of this room Local width:Int = Rand(min_width, max_width) Local height:Int = Rand(min_height, max_height) Local search_width:Int = array_width - width Local search_height:Int = array_height - height Local search_x:Int = Rand(0, search_width) Local search_y:Int = Rand(0, search_height) Local max_checks:Int = search_width * search_height Local checked:Int = 0 Local finished:Int = 0 While Not finished If room_count > 0 Local r:room = New room r.x = search_x r.y = search_y r.x2 = search_x + width - 1 r.y2 = search_y + height - 1 Local collisions:Int = 0 For Local b:room = EachIn(rooms) If rooms_collide(b, r) = True collisions:+1 End If Next If Not collisions carve_room(r) ListAddFirst(rooms, r) room_count:+1 finished = True Else End If EndIf If room_count = 0 Local r:room = New room r.x = search_x r.y = search_y r.x2 = search_x + width - 1 r.y2 = search_y + height - 1 carve_room(r) ListAddFirst(rooms, r) room_count:+1 finished = True Exit EndIf ' add to the index search_x:+1 If search_x > search_width search_x = 0 search_y:+1 End If If search_y > search_height search_y = 0 End If checked:+1 If checked = max_checks finished = True End If Wend End Method Method carve_room(r:room) ' take a room and carve out the space needed set squares to 0's For Local x = r.x To r.x2 For Local y = r.y To r.y2 array[x, y] = 0 Next Next End Method Method ready_array(length:Int, height:Int) ' sets all indexes to 1 and readys the array for writing Local a:Byte[length, height] array = a For Local x:Int = 0 To length - 1 For Local y:Int = 0 To height - 1 array[x, y] = 1 Next Next End Method End Type ' A rectangular field that serves as a storage house for seeded values Type room Field x:Int, y:Int, x2:Int, y2:Int End Type ' return if rooms collide (used in create_dungeon) Function rooms_collide:Int(room1:room, room2:room) If room1.y2 + 3 < room2.y Return 0 If room1.y - 3 > room2.y2 Return 0 If room1.x2 + 3 < room2.x Return 0 If room1.x - 3 > room2.x2 Return 0 Return 1 End Function ' connect 2 rooms (used in create_dungeon) Function connect_rooms(room1:room, room2:room, d:dungeon) ' first pick between 2 connection styles ' we always draw from left to right so we must choose what order to process the rooms Local x1:Int = (room1.x + room1.x2) / 2.0 Local y1:Int = (room1.y + room1.y2) / 2.0 Local x2:Int = (room2.x + room2.x2) / 2.0 Local y2:Int = (room2.y + room2.y2) / 2.0 ' make sure the values for each of the x's and y's are EVEN so no corridors touch If Float(x1 Mod 2.0) x1:+1 End If If Float(x2 Mod 2) x2:+1 End If If Float(y1 Mod 2) y1:+1 End If If Float(y2 Mod 2) y2:+1 End If draw_hori(y1, x1, x2, d) draw_vert(x2, y1, y2, d) End Function ' draw a verticle line on the array (used in create_dungeon) Function draw_vert(x1:Int, y1:Int, y2:Int, d:dungeon) ' see if step multiplier is negative Local dist:Int = Abs(y1 - y2) Local mult:Int = 1 If y1 > y2 mult = -1 End If ' draw For Local i = 0 To dist d.array[x1, y1 + (i * mult)] = 0 Next EndFunction ' draw a horizontal line on the array (used in create_dungeon) Function draw_hori(y1:Int, x1:Int, x2:Int, d:dungeon) ' see if step multiplier is negative Local dist:Int = Abs(x1 - x2) Local mult:Int = 1 If x1 > x2 mult = -1 End If ' draw For Local i = 0 To dist d.array[x1 + (i * mult), y1] = 0 Next EndFunction ' *IMPORTANT* lets a programmer seed the dungeon with item, monster and exit values as needed Function add_value(d:dungeon, value:Int, attempts:Int) For Local i:Int = 0 To attempts - 1 Local r:room = room(d.rooms.ValueAtIndex(Rand(0, CountList(d.rooms) - 1))) d.array[Rand(r.x, r.x2), Rand(r.y, r.y2)] = value Next End Function ' *IMPORTANT* returns a freshly made dungeon, the workhorse of the program! Function create_dungeon:dungeon(width:Int, height:Int, room_count:Int, room_min_height:Int, room_max_height:Int, room_min_width:Int, room_max_width:Int) Local d:dungeon = New dungeon 'ready the array d.ready_array(width, height) 'add rooms For Local i = 0 To room_count - 1 d.add_room(room_min_width, room_min_height, room_max_width, room_max_height) Next 'connect rooms in a loop Local length:Int = CountList(d.rooms) For Local j:Int = 0 To length If j + 1 < length connect_rooms(room(d.rooms.ValueAtIndex(j)) , room(d.rooms.ValueAtIndex(j + 1)) , d) EndIf Next connect_rooms(room(d.rooms.ValueAtIndex(0)) , room(d.rooms.ValueAtIndex(length - 1)) , d) Return d End Function |
Comments
None.
Code Archives Forum