How To Pick A Random Point From Triangles, Hexagons and Squares

This tutorial will cover multiple shapes, each comprising of two parts: an explanation of how the triangle formula is used then an usable example script to place on a Node2D object.

The recommended reading order for learners is [Triangles] → [SHAPE_OF_CHOICE] → [Making Code Cleaner]

Triangles

The triangle formula simply requires the positions of the three corners.
Here, the points form an equilateral triangle with a side length of one

``````extends Node2D

func random_triangle_point(a, b, c):
return a + sqrt(randf()) * (-a + b + randf() * (c - b))

var a = Vector2(0,0)
var b = Vector2(1,0)
var c = Vector2(0.5,sqrt(3.0)/2.0)

var point = random_triangle_point(a,b,c)
print(point)
``````

In game

``````extends Node2D

func random_triangle_point(a, b, c):
return a + sqrt(randf()) * (-a + b + randf() * (c - b))

func _draw():
var a = Vector2(0,0)
var b = Vector2(1,0)
var c = Vector2(0.5,sqrt(3.0)/2.0)
var triangleSize = 100

for i in range(10000):
var pos = random_triangle_point(a,b,c) * triangleSize
draw_circle(pos, 1, Color.AQUAMARINE)

``````

Hexagons

Hexagons are just 6 equilateral triangles in disguise, each rotated 60 degrees
All we need to do is pick a random equilateral triangle point, and rotate it

``````extends Node2D

func random_triangle_point(a, b, c):
return a + sqrt(randf()) * (-a + b + randf() * (c - b))

var a = Vector2(0,0)
var b = Vector2(1,0)
var c = Vector2(0.5,sqrt(3.0)/2.0)

var point = random_triangle_point(a,b,c)
``````

In game

Notice that this code is the same as the triangle’s code with the exception of an extra line that simply rotates the triangle 60 degrees a random amount of times.

``````extends Node2D

func random_triangle_point(a, b, c):
return a + sqrt(randf()) * (-a + b + randf() * (c - b))

func _draw():
var a = Vector2(0,0)
var b = Vector2(1,0)
var c = Vector2(0.5,sqrt(3.0)/2.0)
var triangleSize = 100

for i in range(10000):
var pos = random_triangle_point(a,b,c) * triangleSize
pos = pos.rotated(randi_range(0, 5) * deg_to_rad(60))
draw_circle(pos, 1, Color.AQUAMARINE)
``````

Square

Squares are a little more difficult because they are made of two right isosceles triangles that are rotated 180 degrees from each other (aka mirror opposites) but they don’t rotate in place so it ends up looking like this:

These two triangles do make up a square, but one of the triangles is in the wrong spot. If we’re on the rotated triangle, we should also move it by a set amount to the correct location. One side length to the right, One side length down.

``````extends Node2D

func random_triangle_point(a, b, c):
return a + sqrt(randf()) * (-a + b + randf() * (c - b))

func _draw():
var a = Vector2(0,0)
var b = Vector2(1,0)
var c = Vector2(1,1)
var triangleSize = 100

var pos = random_triangle_point(a,b,c) * triangleSize
var shouldRotate = randi_range(0, 1) == 0
if shouldRotate:
pos += Vector2(1, 1) * triangleSize
``````

In game

``````extends Node2D

func random_triangle_point(a, b, c):
return a + sqrt(randf()) * (-a + b + randf() * (c - b))

func _draw():
var a = Vector2(0,0)
var b = Vector2(1,0)
var c = Vector2(1,1)
var triangleSize = 100

for i in range(10000):
var pos = random_triangle_point(a,b,c) * triangleSize

var shouldRotate = randi_range(0, 1) == 0
if shouldRotate:
pos += Vector2(1, 1) * triangleSize
draw_circle(pos, 1, Color.AQUAMARINE)
``````

Making Code Cleaner

While I actively avoided making named functions so the logic can be followed sequentially, it is probably not a good idea to have unexplained a,b,c points as well as a seemingly random rotation.

For this reason, it is better to make a function that generates a random hexagon instead.

``````extends Node2D

func random_triangle_point(a, b, c):
return a + sqrt(randf()) * (-a + b + randf() * (c - b))

func random_hexagon_point():
var a = Vector2(0,0)
var b = Vector2(1,0)
var c = Vector2(0.5,sqrt(3.0)/2.0)
var randomRotation = randi_range(0,5) * deg_to_rad(60)
return random_triangle_point(a,b,c).rotated(randomRotation)