Skip to content

6. Generating Randomness

Colin Benaissa edited this page Nov 15, 2024 · 6 revisions

Randomness is critical in generating lifelike, organic behaviors.

One such algorithm that generates randomness is known as Perlin Noise. Perlin noise can generate various organic effects such as clouds, terrain, and patterned textures. Perlin noise has a more organic appearance (rather than just pure randomness) because it produces a smooth sequence of pseudo-random numbers. In Godot, to use Perlin Noise, you must create a variable of the OpenSimplexNoise class (and initialize the variable) done by:

var noise : OpenSimplexNoise # this declares the var noise as an object of the class OpenSimplexNoise but refrains from giving it a value 
noise.seed = randi() # Randomize seed
noise.octaves = 3 # Controls the level of detail (higher values = more detailed noise)
noise.period = 20 # Controls how fast or slow the pattern repeats (higher values = less repetition)
noise.persistence = 0.5 # Controls the intensity of higher frequency noise

Then, to use this, say we want to generate an organic image, with normal random bits this would generally produce harsh contrast, however with Perlin Noise we can create a nice subtle gradient (a slow increase/decrease of RGB values per pixel):

var img = Image.new()
for x in range(256): 
      for y in range(256):
          var value = noise.get_noise_2d(x, y) # Generate a Perlin Noise value at the coordinates (x,y) 
          var color = Color(value, value, value) # Map the noise to a grayscale value
          img.set_pixel(x, y, color)
texture = ImageTexture.new()
texture.create_from_image(img)

Complete Randomness can also be Organic!

In this case, we will talk about the Drunkard's Walk Algorithm where the entity is moving in randomly generated directions up, left, right, or down (because they are drunk). Here is the code to do so (this code is a lot more self explanatory):

var position : Vector2
var steps : int = 1000  # Total number of steps
var step_size : int = 10  # Size of each step
position = Vector2(400, 400)  # Starting in the middle of the screen

 func draw_walk():
     # Drawing the walk on the screen
     var random_direction : int
     var random_step : Vector2

     for i in range(steps):
         random_direction = randi_range(0, 3)  # Random number between 0 and 3
         match random_direction:
             0: random_step = Vector2(step_size, 0)  # Move right
             1: random_step = Vector2(-step_size, 0)  # Move left
             2: random_step = Vector2(0, step_size)  # Move down
             3: random_step = Vector2(0, -step_size)  # Move up

    position += random_step
    
 
    draw_circle(position, 2, Color(1, 0, 0))  # Red dot for each step

update()

A general method we can use to generate randomness:

We can use the randi_range(x,y) function where x is an exclusive lower bound and y is an exclusive higher bound. So this means that if x is passed as 1 and y is passed as 5, this function generates a random number between (not including) 1 and 5.