• Godot Help2D
  • Custom drawn Polygon2D in hex shape changes weirdly

To be honest, no idea what I am doing wrong here: I am using below code to draw a hexagon shape with a Polygon2D and assign a texture to it:

extends Polygon2D
var edgeLength = 100

# Called when the node enters the scene tree for the first time.
func _ready():
	drawTile()
	pass


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass
	
	
func drawTile():
	var height = sqrt(3) * edgeLength
	print(height)
	var pt0 = Vector2(edgeLength,0)
	var pt1 = Vector2(pt0.x + edgeLength, 0)
	var pt2 = Vector2(pt1.x + 0.5*edgeLength, pt1.y + height*0.5)
	var pt3 = Vector2(pt1.x, pt1.y + height)
	var pt4 = Vector2(pt0.x, pt1.y + height)
	var pt5 = Vector2(pt0.x - 0.5*edgeLength, pt1.y + height*0.5)
	var texture = load("res://tex/hex_grasland_430_371.png")
	
	var array = PackedVector2Array([pt0, pt1, pt2, pt3, pt4, pt5, pt0])
	print(array)
	self.polygon = array
	self.texture = texture
	self.set_texture_scale(Vector2(1,1))
	
	
	add_child(self)

The texture looks like below:

When setting the edgeLength to 100 the output is

Setting it to 75 and it looks like

What do I need to change to make sure that the texture always properly fills the hexagon and that the hexagon itself is drawn properly?

I'm not certain what the issue is, but what is with this line? I'm suspicious of it.

add_child(self)

Why are you childing the node to itself?

    award Good point - I have commented it out but the result is identical.

    Very strange, your code works perfectly for me. At 100:

    At 75:

    What version of Godot are you using? Is your hexagon childed to anything that could be clipping its top-left?

      award No, not really anything fancy here.
      I am running Godot 4.1.1

      For reference I have attached the project here:

      https://www.dropbox.com/scl/fi/3yl5zufa8jyy9ixez3awc/TileTest.zip?rlkey=qmodhi1l55qypzhg2gc0wk1om&dl=0

      Just wondering: for visual reference in the editor, I have also drawn a polygon shape for that Scene but I guess that gets overwritten by

      var array = PackedVector2Array([pt0, pt1, pt2, pt3, pt4, pt5, pt0])
      self.polygon = array

      Below the "dummy" hexagon in the 2D View of my Tile scene:

      award and note that in your example the visible portion of the texture changes based on the edgeLength value. My goal is to have it look the same, just smaller.

      Ah I'm sorry that I misunderstood your issue! The hexagon isn't drawing properly because the texture is a hexagon with transparency, so part of it is just drawing transparency. Your only issue is not actually scaling the texture. You can do

      var referenceLength = 100.0
      var edgeLength = 75.0

      and then later

      var scale = referenceLength / edgeLength
      self.set_texture_scale(Vector2(scale, scale))

        award hm ... referenceLength = 100 did not help but setting it to 1/2 of the texture width actually made it look perfect the. the edgeLength is 150:

        but then again when I decrease the edgeLength to 50 or increase to 300, it is. breaking again

        • xyz replied to this.

          max_godot When working on this use a dummy texture without transparency with just a hex silhouette. That way you'll immediately see any mismatches.

          If I understand the problem correctly, texture scale factor should be (2.0 * edgeLength) / textureWidth, given that the hex in the texture occupies maximum space.

          Ah I misunderstood again! I am sorry 😅

          @xyz you have it inverted. texture_scale scales the UVs, not the texture itself. You're right about texture width though.

          @max_godot I thought your initial picture was drawing correctly. I realize now that one was also incorrectly scaling.

          There are a few lines you need to add / change.
          var pt0 = Vector2(edgeLength*0.5,0)
          When you construct your vertices, it's easiest if you have your leftmost point at x=0. Replacing pt0 with the above line gives that result.

          var scale_x = float(texture.get_width()) / (edgeLength * 2)
          var scale_y = float(texture.get_height()) / (height)
          self.set_texture_scale(Vector2(scale_x, scale_y))

          Here we are scaling the width and height of texture_scale using the texture width and height while keeping the aspect ratio of your hexagon polygon.

          Something else I learned: CanvasItem UVs scale per-pixel rather than 0 to 1. I tried at first just setting a UV array but it was completely off because of that.

            award I'll be damned ...

            Turns out the change to

            var pt0 = Vector2(edgeLength * 0.5,0)

            is actually a game changer and it does not work without 😃
            As always, thanks a lot for your help @award and @xyz

            Just checking: should the UPMOST point of the vertices also always be 0?

              max_godot

              Ideally. It's worth understanding the reason why. When you set the points in a Polygon2D, it (apparently) gives them equivalent UV coordinates, which are in relation to the position of the Polygon2D node. You can think of the node's position as the pivot for all the points. It is at (0,0) for them. So a point at (100,100) will have a UV of (100,100).

              The UVs of the points line up to pixels on the texture. So if your leftmost UV is at (100, 0), then it will only start after the 100th horizontal pixel of the image. Likewise a topmost UV of (0, 100) would only start after the 100th vertical pixel of the image, from top to bottom.

              You don't have to line up the top or the left. You could manually set the UVs per point via Polygon2D.uv, or you could adjust Polygon2D.texture_offset to compensate for your "top left". But the easiest way when constructing a new polygon is to simply make your points line up with the top and the left.