Python: Top-Down Procedural Generation

January 8, 2017

 

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()

 

 

Please reload

Read more:

iOSDC Japan 2018

September 7, 2018

Making an iOS Newsletter App with Updating Content using JSON and Swift 4

July 25, 2018

1/14
Please reload

    © 2019 Tech Kingdom

    Contact us : official.techkingdom@gmail.com