• 2D
  • Generating circual tilemap?

Hi people! i need some assistance, how would i go about auto generating a round / circular tilemap ? i already have 2 for loops that make a square grid and change the color depending on that noise value it has, but what i want is this tilemap to be a perfect circle. Thanks!

You'll probably need to use a normal tile, but place tiles in a circular pattern instead of a square. That way the map appears to be circular, while in reality it is still a square Tilemap.

There are several different ways you could generate a circular shape within a Tilemap, but the easiest is probably to check if the tile position is within the radius of the circle for every tile in the loops.

Something like this, assuming the center of the circle is the origin of the Tilemap (untested):

# This defines how many tiles are roughly within the radius of the circle
const CIRCLE_RADIUS = 10
for x in range(0, tilemap_width):
	for y in range(0, tilemap_height):
		var tile_coords = Vector2(x, y)
		if tile_coords.length() <= CIRCLE_RADIUS:
			# The tile is within the radius of the circle, so add a tile here
			# (or whatever you need to do for tiles within the circle's radius)
			place_tile(x, y)
		else:
			# The tile is not within the radius of the circle, so ignore the tile
			pass

That said, it is not going to be a perfect circle, as the edges will still be made up of many square tiles. If you want something closer to a perfect circle, you'll need to have the edges of the circle use differently shaped tiles so it appears smoother. I would suggest looking at something like Marching Squares (Wikipedia) and/or Godot's auto tile functionality (KidsCanCode).

That is exactly what i need! i will experiment with this, thank you!

This example only makes the squares look like a pizza slice :P i will look at this tomorrow and see if i can figure out what to do next :)

@Christoffer said: This example only makes the squares look like a pizza slice :P i will look at this tomorrow and see if i can figure out what to do next :)

After looking at the code and running it in a test project, I realized the code only calculates in the bottom right quadrant of the circle, which is why it produces a pizza slice. :sweat_smile: Adding a few more loops fixes it. I also moved the process into a function and wrote the code so the starting position, radius, and tile ID are easily changeable.

extends TileMap

func _ready():
	make_tilemap_circle(Vector2(0, 0), 10, 0);

func make_tilemap_circle(center_position, radius, tile_id=0):
	# The right half of the circle:
	for x in range(center_position.x, center_position.x + radius):
		# The bottom right half of the circle:
		for y in range(center_position.y, center_position.y + radius):
			var relative_vector = Vector2(x, y) - center_position;
			if (relative_vector.length() < radius):
				self.set_cell(x, y, tile_id);
		# The top right half of the circle
		for y in range(center_position.y - radius, center_position.y):
			var relative_vector = center_position - Vector2(x, y);
			if (relative_vector.length() < radius):
				self.set_cell(x, y, tile_id);
	
	# The left half of the circle
	for x in range(center_position.x - radius, center_position.x):
		# The bottom left half of the circle:
		for y in range(center_position.y, center_position.y + radius):
			var relative_vector = Vector2(x, y) - center_position;
			if (relative_vector.length() < radius):
				self.set_cell(x, y, tile_id);
		# The top left half of the circle
		for y in range(center_position.y - radius, center_position.y):
			var relative_vector = center_position - Vector2(x, y);
			if (relative_vector.length() < radius):
				self.set_cell(x, y, tile_id);
3 months later

Just a small optimization suggestion: as long as you work with circles (or ovals that are not on a weirdly rotated angle), and approach the problem from the circle's center you could calculate based on length/radius once and mirror the x,y coord relative to the circle's center. Just one x-loop, one y-loop and a whole bunch less length calculations :D

4 months later

@MarbleTemple said: Just a small optimization suggestion: as long as you work with circles (or ovals that are not on a weirdly rotated angle), and approach the problem from the circle's center you could calculate based on length/radius once and mirror the x,y coord relative to the circle's center. Just one x-loop, one y-loop and a whole bunch less length calculations :D

Any way you could give an explanation as to how? or @TwistedTwigleg

I only skimmed over what I wrote previously, but basically it works around the issue and creates a circular tilemap by creating a circle of individual, square tiles, much like how you can make a circle out of pixels.

As for the optimization, I think you can use code like this:

func make_tilemap_circle(center_position, radius, tile_id=0):

	var relative_vector = Vector2.ZERO
	for x in range(center_position.x, center_position.x + radius):
		for y in range(center_position.y, center_position.y + radius):
			relative_vector.x = x
			relative_vector.y = y
			# you might be able to skip this check and using relative_vector
			# entirely, depending on the radius. Hm...
			if (relative_vector.length() < radius):
				self.set_cell(x, y, tile_id)
				self.set_cell(-x, y, tile_id)
				self.set_cell(-x, -y, tile_id)
				self.set_cell(x, -y, tile_id)

Well, in pseudo code:

    circle_radius = radius
    center_x = center of your circle, x coord
    center_y = center of your circle, y coord
    
    for x = 0 to circle_radius
    	for y = 0 to circle_radius
    		length = sqrt( x^2 + y^2)
    		if length <= circle_radius
    			set_cell(center_x + x, center_y + y)
    			set_cell(center_x - x, center_y + y)
    			set_cell(center_x + x, center_y - y)
    			set_cell(center_x - x, center_y - y)
    		endif
    	next
   next

Wow, that's amazing thank you so much! I'll make sure to pass along and post my code when I'm done