(Idk if this is actually procedual generation or not, or what its actually called. So im calling it this)
Godot Version
4.5
Question
What my code is essencially doing is placing puzzle pieces down with connections to the sides. Each side has one connection that can be connected onto, where not every side might have a connection. Each piece has a position and can be rotated by any form of 90 degrees. My code has allowed me to make a straight line one way on the z axis, but is not working for the x axis for whatever reason.
Code for main system:
Here is a few things before you try and debug this mess, and if you have any questions, let me know!
I do have some comments that explain what some things are doing
Anything that is commented out is either a print line I don’t want active right now, or something that I tried to impliment thats the next step of my project, before I realized this step does not even work yet.
Getting anything from the “bag” array (like in bag[n]) will be outputting 0.
Sorry my code is messy, im a novice and if you have any reccomendations to clean it up that wont make my brain explode from having to redo all of this would be appreciated!
const base_piece = preload("res://resources/scenes/piece.tscn")
var piece_number: int
var placed_tiles = []
var non_rotation_connection: bool
var placed_tile_rotation: int
const tile_index = {
-1: "spawn",
0: "straight",
1: "corner",
}
const tile_definitions = {
"spawn": {
"connections": ["north", "east"],
"mesh": preload("res://resources/scenes/pieces/spawnPiece.tscn"),
},
"straight": {
"connections": ["north", "south"],
"mesh": preload("res://resources/scenes/pieces/straightPiece.tscn"),
},
"corner": {
"connections": ["north", "east"],
"mesh": preload("res://resources/scenes/pieces/cornerPiece.tscn"),
},
}
const direction_offset = {
"north": Vector2i(-1,0),
"east": Vector2i(0, -1),
"south": Vector2i(1, 0),
"west": Vector2i(0, 1)
}
const offsets_list = ["north", "east", "south", "west"]
#This is simply to take note of and copy+paste into code below (not functions that are used)
#func world_to_grid(inputX, inputZ, _grid_pos):
#_grid_pos = Vector2i(roundi(inputX/4),roundi(inputZ/4))
#
#func grid_to_world(_output, grid_pos):
#_output = Vector3(grid_pos.x, 1, grid_pos.y)
func place_tiles():
#Make the original tile, the spawn tile
piece_number += 1
var spawn_tile = base_piece.instantiate() #Do not just move the placed_tile variable here, or it will try to reuse itself and just change each variable each passthrough, which won't work with the setup, as base_piece changes itself based off of func _ready
spawn_tile.piece_name = "spawn"
spawn_tile.connections = tile_definitions["spawn"]["connections"]
spawn_tile.piece_mesh = tile_definitions["spawn"]["mesh"]
spawn_tile.piece_number = piece_number
add_child(spawn_tile)
spawn_tile.piece_position = Vector2i(0,0)
spawn_tile.name = "Tile" + str(spawn_tile.piece_position)
spawn_tile.global_position = Vector3i(spawn_tile.piece_position.x*4, 1, spawn_tile.piece_position.y*4)
placed_tiles.append(spawn_tile)
#print(spawn_tile.name)
#This is where the actual placing of the tiles happens
for n in range(bag.size() - 1):
#Makes a tile on the lowest tile number
var branch_tile
if n < placed_tiles.size():
branch_tile = placed_tiles[n]
else:
push_error("Invalid access of index '"+str(n)+"' on a base object of type: 'Array' (Basically: n >= placed_tile array size)")
break
#If your trying to place a tile off of nothing (going out of bounds of the array), then return an error. If an error is return it breaks the for loop so it does not cause a crash further down the road
var actual_directions = []
#print(branch_tile.connections)
var open_connections = []
for connection in branch_tile.connections:
if !branch_tile.used_connections.has(connection):
open_connections.append(connection)
#Finds open connections
for offset in open_connections:
actual_directions.append(offsets_list[(offsets_list.find(offset)+branch_tile.piece_rotation) % 4])
#Basically what this does it picks a direction from their direction list (that are open) and turns it into 0-3 (0 is north, east is 1, ect...), then adds 0-3 for the rotation (0 is none, 1 is turn right once, ect...) adds it together, modulo by 4 (so if you do 0%4 then you get 0/north, or 3%4 is 3/west) and you get the "actual" directions of your connections (the pieces connections with rotation accounted for)
#Time to actually place this godamn tile
piece_number += 1
var placed_tile = base_piece.instantiate()
placed_tile.piece_name = tile_index[bag[n]]
placed_tile.connections = tile_definitions[tile_index[bag[n]]]["connections"]
placed_tile.piece_mesh = tile_definitions[tile_index[bag[n]]]["mesh"]
placed_tile.piece_number = piece_number
var i = -1 #What i does it counting how many times the for loop has gone through so it allows the correct connection to be applied to branch_tile.used_connections
#places a tile for each side of the branch tile
print(str(actual_directions))
for real_offset in actual_directions:
print("Placing off: "+str(branch_tile.name)+", Piece Type: "+str(branch_tile.piece_name))
i += 1
placed_tile_rotation = 0
var opposite_connection
non_rotation_connection = false
opposite_connection = find_opposite_connection(placed_tile, real_offset)
#If there is an opposite connection then place the tile if not then rotate the tile until it works
if non_rotation_connection == true:
placed_tile.piece_position = branch_tile.piece_position + direction_offset[opposite_connection]
print("Placed tile "+str(placed_tile)+" at position "+ str(placed_tile.piece_position))
placed_tile.used_connections.append(offsets_list[(offsets_list.find(opposite_connection)-placed_tile_rotation) % 4])
branch_tile.used_connections.append(open_connections[i])
#print(str(placed_tile)+" (placed tile) has used connection(s) "+str(placed_tile.used_connections))
#print(str(branch_tile)+" (branch tile) has used connection(s) "+str(branch_tile.used_connections))
#if the placed tile does not have an opposite connection, then rotate the tile until you get an opposite connection
else:
push_error("No opposite connection was found for direction '"+str(open_connections[i])+"' for tile "+str(branch_tile))
add_child(placed_tile)
placed_tile.name = "Tile" + str(placed_tile.piece_position)
placed_tiles.append(placed_tile)
#print("piece position is: "+str(placed_tile.piece_position))
func find_opposite_connection(placed_tile, branch_tile_connection):
for n in range(3):
for placing_connection in placed_tile.connections:
placing_connection = offsets_list[(offsets_list.find(placing_connection)+n) %4]
print("possible opposite: "+str(placing_connection))
if placing_connection == offsets_list[(offsets_list.find(branch_tile_connection)+2) % 4]:
non_rotation_connection = true
placed_tile_rotation = n
placed_tile.piece_rotation = n
print("Choosen Opposite: ", str(offsets_list[n]))
#print(str(placing_connection)+" is the opposite of "+str(branch_tile_connection))
#print("placing connection is: "+str(placing_connection))
return placing_connection
#Basically if the placed tile has a connection thats 2 rotations away (180°) then make the bool true, and it checks it for each connection of the branch tile. Once it finds one it ends the checking to ensure only one is found
Code for “Piece” (variable holder and debug printer):
extends Node3D
var piece_position: Vector2i
var piece_rotation: int
var piece_name: String
var connections: Array
var used_connections: Array
var piece_mesh: PackedScene
var piece_number: int
func _ready() -> void:
if piece_mesh != null:
add_child(piece_mesh.instantiate())
rotation_degrees = Vector3i(0, piece_rotation*90, 0)
else:
push_error("No piece mesh found")
global_position = Vector3i(piece_position.x*4, 1, piece_position.y*4)
print(piece_name, str(position.x))
