Godot 4.3
Hello all. I am trying to rotate this simple map that is composed of two tilemaplayers in a way that preserves their relationship to each other. I looked at a recent topic that was similar to this, but it only rotated a single tilemaplayer. I have a global script that has all the rotation logic and another script that is linked to the Node2D node that is housing the tilemaplayers. However, when I rotate it, the relationship is broken and the top layer rotates in a weird way with respect to the bottom layer. After four rotations, the map returns to its original position. I have attached pictures of the rotations and the scripts.
Global Code:
extends Node
Function to recursively search for a node by name
func find_node_recursive(base_node: Node, name: String) → Node:
for child in base_node.get_children():
if child.name == name:
return child
var result = find_node_recursive(child, name)
if result:
return result
return null
Function to get a specific Map node by its unique name
func get_region_map(region_name: String) → Node2D:
var node = find_node_recursive(get_tree().root, region_name)
if node and node is Node2D:
return node as Node2D
else:
print("Map node not found or invalid node type: ", region_name)
return null
Calculate the global map boundaries for all TileMapLayers
func get_map_boundaries(map: Node2D) → Rect2:
var min_pos = Vector2(INF, INF)
var max_pos = Vector2(-INF, -INF)
for layer in map.get_children():
if layer is TileMapLayer:
var used_cells = layer.get_used_cells()
for cell in used_cells:
# Adjust for layer's global position
var cell_global_pos = cell + Vector2i(layer.position)
min_pos.x = min(min_pos.x, cell_global_pos.x)
min_pos.y = min(min_pos.y, cell_global_pos.y)
max_pos.x = max(max_pos.x, cell_global_pos.x)
max_pos.y = max(max_pos.y, cell_global_pos.y)
var size = max_pos - min_pos
return Rect2(min_pos, size)
Rotate all layers in the Map around a global center
func rotate_map(map: Node2D) → void:
# Get the global boundaries and calculate the global center of all layers
var boundaries = get_map_boundaries(map)
var global_center = boundaries.position + boundaries.size / 2.0 # Global center for all layers
# Prepare an array to hold new layers
var new_layers: Array = []
# Rotate each layer around the global center
for layer in map.get_children():
if layer is TileMapLayer:
# Duplicate the current layer for rotation
var current_layer_copy = layer.duplicate()
rotate_map_layer(current_layer_copy, global_center) # Rotate it
new_layers.append(current_layer_copy)
# Remove old layers and free them
for layer in map.get_children():
if layer is TileMapLayer:
map.remove_child(layer)
layer.queue_free()
# Add rotated layers
for layer in new_layers:
map.add_child(layer)
Rotate the cells in a TileMapLayer around the global center
func rotate_map_layer(tilemap_layer: TileMapLayer, global_center: Vector2) → void:
var new_cells = rotate_cell_data(tilemap_layer, global_center)
tilemap_layer.clear() # Clear the layer before setting new cells
for cell_data in new_cells:
# Ensure new positions are set accurately
tilemap_layer.set_cell(
cell_data.coords,
cell_data.source_id,
cell_data.atlas_coords,
cell_data.alternative_tile
)
# Recalculate global_center based on new boundaries (if needed)
var new_boundaries = get_map_boundaries(tilemap_layer)
global_center = new_boundaries.position + new_boundaries.size / 2.0
Rotate and collect all cell data for a TileMapLayer
func rotate_cell_data(tilemap_layer: TileMapLayer, global_center: Vector2) → Array:
var return_array: Array =
var used_cells = tilemap_layer.get_used_cells()
for cell in used_cells:
var cell_global_pos = cell + Vector2i(tilemap_layer.position) # Adjust for layer's global position
var rotated_coords = rotate_vector_90_clockwise_around_center(cell_global_pos, global_center)
var local_rotated_coords = (rotated_coords - tilemap_layer.position).round() # Convert to local and round
# Store the new cell data with recalculated positions
var cell_data = CellData.new()
cell_data.coords = local_rotated_coords
cell_data.source_id = tilemap_layer.get_cell_source_id(cell)
cell_data.atlas_coords = tilemap_layer.get_cell_atlas_coords(cell)
cell_data.alternative_tile = tilemap_layer.get_cell_alternative_tile(cell)
return_array.append(cell_data)
return return_array
Function to rotate a vector 90 degrees clockwise around the global center
func rotate_vector_90_clockwise_around_center(given_vector: Vector2, center: Vector2) → Vector2:
print(“Global Center:”, center) # Print the global center for debugging
# Calculate the vector relative to the center
var relative_vector = given_vector - center
# Rotate 90 degrees clockwise
var rotated_vector = Vector2(relative_vector.y, -relative_vector.x)
# Adjust back to global space
return rotated_vector + center
CellData class to hold cell information during rotation
class CellData:
var coords: Vector2i
var source_id: int
var atlas_coords: Vector2i
var alternative_tile: int
Node2D Code:
extends Node2D
@onready var PacificaCoast_Overworld = $“…”
Called when the node enters the scene tree for the first time.
func _ready() → void:
pass
Detect input to rotate the map
func _input(event: InputEvent) → void:
if event.is_action_pressed(“rotate_map”):
# Get the map node by name using the global script
var map_node = GlobalRotatemap.get_region_map(“PacificaCoast_Overworld_Map”)
# Check if the map node exists
if map_node:
# Rotate the map using the global function
GlobalRotatemap.rotate_map(map_node)
else:
print("Map node not found.")