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))
func _ready():
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))
func _ready():
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.rotated(randi_range(0, 5) * deg_to_rad(60)))
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 = pos.rotated(deg_to_rad(180))
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 = pos.rotated(deg_to_rad(180))
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)
func _ready():
var point = random_hexagon_point(a,b,c)
print(point)