Dungeon generator


Working on a dungeon generator, so far I’ve managed to spawn corridors of different lengths and intersections that behave as expected.
Now I think I have to figure out a way to remove overlaps.
I know it’s not very impressive but I’m new to scripting and happy that I’ve gotten this far. :slight_smile:
This night was spent figuring out a way to generate only one or two new corridors based on what intersection was there.
Couldn´t get it to work by names because of instancing I think. So I solved it by looking at how many children the intersection has. t-sections have more than left or right turns.

12 Likes

Really neat! Is this for a rogue-like?

1 Like

Looks neat! Do the colors denote any meaning, or is that just a stylistic choice? Either way, it looks very cool. What are you going to do once you’ve managed to get the overlaps working the way you want?

I find programming procedural things are very satisfying because small changes can really change how things turn out.

1 Like

I’m still trying to solve this intersection problem.
I want to check if the tiles are intersecting after placing it, if it intersects it should be removed.
I’ve tried with Area3D and collision shape.
And also direct_space_state
But the issue seems to be that all the collision detection is made after instantiation, even with direct_space_state
So it places all the tiles and then I get signaled about intersections.
Any help would be appreciated

extends Node

@onready var tile5 = load("res://assets/3D/Floor05.tscn")
@onready var tile10 = load("res://assets/3D/Floor10.tscn")
@onready var tile15 = load("res://assets/3D/Floor15.tscn")

@onready var cross = load("res://assets/3D/FloorT.tscn")
@onready var lTurn = load("res://assets/3D/FloorL.tscn")
@onready var rTurn = load("res://assets/3D/FloorR.tscn")


var corridorList = [tile5, tile10, tile15] 
var intersectionList = [cross, lTurn, rTurn]
var corridorArray = []
var intersectionArray = []
var intersected = false

func _ready():
	
	corridorList = [tile5, tile10, tile15]
	intersectionList = [cross, lTurn, rTurn]

	# Place first corridor at base marker
	var baseMarker = get_node("Floor05/Marker3D")
	var tileInstance = corridorList.pick_random().instantiate()
	add_child(tileInstance)
	corridorArray.append(tileInstance)
	tileInstance.position = baseMarker.global_position

	# Place intersection at corridor marker
	placeIntersectionAtMarker(corridorArray[0].get_node("Marker3D"))
	corridorArray.clear()

	# Repeat the process
	while get_child_count() < 50:
		for i in intersectionArray:
			
			#T sections will have more children than R and L sections
			if i.get_child_count() == 3:
				placeCorridorAtMarker(i.get_node("MarkerA"))
				placeCorridorAtMarker(i.get_node("MarkerB"))
				
			elif i.get_child_count() == 2:
				placeCorridorAtMarker(i.get_node("MarkerA"))	

		intersectionArray.clear()

		for i in corridorArray:
			placeIntersectionAtMarker(i.get_node("Marker3D"))

		corridorArray.clear()

func placeIntersectionAtMarker(marker):
	
	intersectionList = [cross, lTurn, rTurn]
	
	var intersectionInstance = intersectionList.pick_random().instantiate()
	add_child(intersectionInstance)
	intersectionInstance.position = marker.global_position
	intersectionInstance.rotation = marker.global_rotation
	
	#Check for overlaps before finalizing
	if is_tile_overlapping(intersectionInstance):
		print("Overlap detected, removing the new tile.")
		intersectionInstance.queue_free()
	else:
		print("No overlap, tile added successfully.")
		intersectionArray.append(intersectionInstance)

func placeCorridorAtMarker(marker):
	
	corridorList = [tile5, tile10, tile15] 
	
	var tileInstance = corridorList.pick_random().instantiate()
	add_child(tileInstance)
	tileInstance.position = marker.global_position
	tileInstance.rotation = marker.global_rotation
	
	#Check for overlaps before finalizing
	if is_tile_overlapping(tileInstance):
		print("Overlap detected, removing the new tile.")
		tileInstance.queue_free()
	else:
		print("No overlap, tile added successfully.")
		corridorArray.append(tileInstance)

func is_tile_overlapping(tile_instance) -> bool:
	# Find the CollisionShape3D within the tile instance
	var collision_shape = tile_instance.get_node("MeshInstance3D/Area3D/CollisionShape3D")
	if not collision_shape:
		print("Error: No CollisionShape3D found in tile!")
		return true  # Assume overlap if there's no collision shape
	
	# Use the CollisionShape3D's shape and global transform for the test
	var shape = collision_shape.shape
	if not shape:
		print("Error: CollisionShape3D has no shape!")
		return true  # Treat as overlapping by default (or false if you'd prefer)
			
	var transform = collision_shape.global_transform
	# Debug: Print transform and shape info
	print("Tile transform: ", transform)
	print("Tile shape: ", shape)
	# Access the direct space state
	var space_state = get_viewport().get_world_3d().direct_space_state
		
	#prepare the physicsShapeQueryParameters3D
	var query = PhysicsShapeQueryParameters3D.new()
	query.shape = shape
	query.transform = transform
	query.margin = 0.1  # Small margin to handle precision issues
		
	var result = space_state.intersect_shape(query, 32)  # Max 32 results
	if result.size() > 0:
		print("Overlap detected! Result: ", result)
		return true  # Overlap detected
	else:
		print("No overlap, tile added successfully.")
		return false  # No overlap

https://youtu.be/Drqu_lYzzf0
youtube video


Wanted to give an update on my dungeon progress.

I’ve made a character and an enemy
They have animations and there is a selection system and system for dealing and recieving damage.
checking for tile intersections is now solved with just grid positions, before I used Area 3D.
Next step is to make the dungeon look a little more interesting. And introducing the concept of doors.

1 Like