Python: Top-Down Procedural Generation
- Max Clark
- Jan 8, 2017
- 3 min read

This post poses a new simple way to create procedural generation in python. This is an alternative to Perlin noise.
The concept involves casting dots onto a two dimensional plane; for every pixel in the plane, the number of dots within a certain radius is counted. The value for this count will be the altitude, giving a third dimension.
First, we need to import some libraries:
import pygame import math import random from random import randint
Here are the necessary variables:
#Settings size = 80 #count radius, controls biome size pixel = 2 #controls resolution k = 1.0 #relative altitude (0.6 < x < 1.5, for best results) grass = 30 #color level rock = 44 # '' '' snow = 48 # '' ''
dotsx = [] # contains all x-coordinates for the dots dotsy = [] # contains all y-coordinates as well as the weighting
The dots require a weighting so that the dot count (and therefore altitude) is not a whole number. Now lets create a function to count the number dots within a radius around a given coordinate.
def get_count(xc, yc): count = 0
This most efficient way to do this requires the horizontal and vertical distance between the coordinate and each pixel. for i in range(len(dotsx)): dx = abs(dotsx[i] - xc) dy = abs(dotsy[i][0] - yc)
Now we use these values to see if the current dot lies within the radius (with 'size' being the radius). Please see philcolbourn's answer here for the explanation.
if dx < size: if dy < size: if dx + dy <= size: count += dotsy[i][1] elif math.pow(dx,2) + math.pow(dy,2) <= math.pow(size,2): count += dotsy[i][1]
return count
This concludes the function. Now we initiate pygame and set the window size. This can be set to any value.
pygame.init()
xSize, ySize = 400, 400 screen = pygame.display.set_mode((xSize, ySize)) pygame.display.set_caption("Noise Creation")
Next, we create the coordinates and the weighting for each dot. I have developed the formula 'k*10*(xSize+2*size)*(ySize+2*size)/math.pow(size,2)' to decide the number of dots based on the settings above. This algorithm creates dots beyond the size of the window so pixels on the edge do not automatically count fewer dots.
for _ in range(int(k*10*(xSize+2*size)*(ySize+2*size)/math.pow(size,2))): dotsx.append(randint(0,xSize+2*size)-1*size) dotsy.append([randint(0,ySize+2*size)-1*size, randint(0,200)/100])
It is now time to draw the landscape we draw squares with side length 'pixel'. The colour of the pixel is determined by the count of dots.
#Create Landscape
screen.fill([29,109,210])
for x in range(int(xSize/pixel)): x *= pixel for y in range(int(ySize/pixel)): y *= pixel
count = get_count(x, y)
if count > grass: pygame.draw.rect(screen, [60,204,62], (x,y,pixel,pixel)) if count > rock: pygame.draw.rect(screen, [91,46,39], (x,y,pixel,pixel)) if count > snow: pygame.draw.rect(screen, [255,255,255], (x,y,pixel,pixel)) pygame.display.flip()

Here is the whole code:
import pygame import math import random from random import randint
#Settings size = 80 #count radius, controls biome size pixel = 2 #controls resolution k = 1.0 #relative altitude (0.6 < x < 1.5, for best results) grass = 30 #color level rock = 44 # '' '' snow = 48 # '' ''
dotsx = [] # contains all x-coordinates for the dots dotsy = [] # contains all y-coordinates as well as the weighting
def get_count(xc, yc): count = 0
for i in range(len(dotsx)): dx = abs(dotsx[i] - xc) dy = abs(dotsy[i][0] - yc)
if dx < size: if dy < size: if dx + dy <= size: count += dotsy[i][1] elif math.pow(dx,2) + math.pow(dy,2) <= math.pow(size,2): count += dotsy[i][1]
return count
pygame.init()
xSize, ySize = 400, 400 screen = pygame.display.set_mode((xSize, ySize)) pygame.display.set_caption("Noise Creation")
for x in range(int(k*10*(xSize+2*size)*(ySize+2*size)/math.pow(size,2))): dotsx.append(randint(0,xSize+2*size)-1*size) dotsy.append([randint(0,ySize+2*size)-1*size, randint(0,200)/100])
#Create Landscape
screen.fill([29,109,210])
for x in range(int(xSize/pixel)): x *= pixel for y in range(int(ySize/pixel)):
y *= pixel count = get_count(x, y)
if count > grass: pygame.draw.rect(screen, [60,204,62], (x,y,pixel,pixel)) if count > rock: pygame.draw.rect(screen, [91,46,39], (x,y,pixel,pixel)) if count > snow: pygame.draw.rect(screen, [255,255,255], (x,y,pixel,pixel)) pygame.display.flip()
コメント