Skip to content
This repository has been archived by the owner on Jun 23, 2021. It is now read-only.

Commit

Permalink
Sample terrain generation (just a heightmap): something silly with me…
Browse files Browse the repository at this point in the history
…teors
  • Loading branch information
Jormungandr committed May 1, 2010
1 parent 43401fc commit a9bf983
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 12 deletions.
32 changes: 25 additions & 7 deletions game.py
Expand Up @@ -3,6 +3,7 @@
import pygame

from grid import Grid
from terrain import TerrainData, MeteorTerrainGenerator

# intiialize and blank the screen
pygame.init()
Expand All @@ -13,21 +14,36 @@
viewportY = 0

# create the grid, fill it with nodes
# this is currently a bit slow...
gameGrid = Grid()
for x in range(-160,160):
for y in range(-120,120):
contents = random.choice(string.letters).upper()
gameGrid.add_node((x, y, 0), contents)
# uncomment these two if you want random letters back
#contents = random.choice(string.letters).upper()
#gameGrid.add_node((x, y, 0), contents)
terrain = TerrainData()
#terrain.randomize()
gameGrid.add_node((x, y, -1), terrain)

#gameGrid.connect_grid()

generator = MeteorTerrainGenerator()
generator.apply(gameGrid)

font_file = pygame.font.match_font('freemono')
font = pygame.font.Font(font_file, 10)
font.set_bold(True)

#for node in gameGrid.nodes():
# updates the screen to show the appropriate visible nodes
def updateDisplay():
screen.fill((0,0,0))
for x in range(viewportX, viewportX+80):
for y in range(viewportY, viewportY+60):
terrainNode = gameGrid.get_node_at((x, y, -1))
if terrainNode != None:
rect = pygame.Rect((terrainNode.location[0] - viewportX)*10, (terrainNode.location[1] - viewportY)*10, 10, 10)
terrainNode.contents.render(rect, screen)

node = gameGrid.get_node_at((x, y, 0))
if node != None:
text = font.render(node.contents, 1, (255, 255, 255))
Expand All @@ -38,6 +54,8 @@ def updateDisplay():
rect.x *= 10
rect.y *= 10
screen.blit(text, rect)


pygame.display.update()

updateDisplay()
Expand All @@ -48,12 +66,12 @@ def updateDisplay():
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
viewportY += 1
viewportY += 3
if event.key == pygame.K_UP:
viewportY -= 1
viewportY -= 3
if event.key == pygame.K_LEFT:
viewportX -= 1
viewportX -= 3
if event.key == pygame.K_RIGHT:
viewportX += 1
viewportX += 3
updateDisplay()

39 changes: 34 additions & 5 deletions grid.py
Expand Up @@ -3,21 +3,34 @@
from node import Node

class Grid(graph):

def __init__(self):
super(Grid, self).__init__()
self.locationNodes = {}
self.minX = 0
self.minY = 0
self.maxX = 0
self.maxY = 0

def add_node(self, location, contents=None):
new_node = Node(location, contents)
self.locationNodes[location] = new_node;

if location[0] < self.minX:
self.minX = location[0]
if location[1] < self.minY:
self.minY = location[1]
if location[0] > self.maxX:
self.maxX = location[0]
if location[1] > self.maxY:
self.maxY = location[1]

super(Grid, self).add_node(new_node)

def get_node_at(self, location):
if location in self.locationNodes:
return self.locationNodes[location];
else:
return None
if location in self.locationNodes:
return self.locationNodes[location];
else:
return None

def get_node(self, location, contents=None):
test_node = Node(location, contents)
Expand All @@ -27,3 +40,19 @@ def get_node(self, location, contents=None):
return node
else:
return None

# builds 4-connected edges for the whole graph
def connect_grid(self):
for node in self.nodes():
left = self.get_node_at((node.location[0]-1, node.location[1], node.location[2]))
right = self.get_node_at((node.location[0]+1, node.location[1], node.location[2]))
up = self.get_node_at((node.location[0], node.location[1]+1, node.location[2]))
down = self.get_node_at((node.location[0], node.location[1]-1, node.location[2]))
if left != None and not self.has_edge((node, left)):
self.add_edge((node, left))
if right != None and not self.has_edge((node, right)):
self.add_edge((node, right))
if up != None and not self.has_edge((node, up)):
self.add_edge((node, up))
if down != None and not self.has_edge((node, down)):
self.add_edge((node, down))
2 changes: 2 additions & 0 deletions node.py
Expand Up @@ -7,6 +7,8 @@ def __str__(self):
return "%s - %s" % (self.location, self.contents )

def __eq__(self, other):
if other == None:
return False
same_location = self.location == other.location
same_contents = self.contents == other.contents
return same_location and same_contents
Expand Down
64 changes: 64 additions & 0 deletions terrain.py
@@ -0,0 +1,64 @@
import random, pygame, math

class TerrainData():
def __init__(self):
self.height = 0 # in meters, arbitrarily
self.moisture = 0 # 0..1 implies humid land, 1+ equals meters of water coverage
self.temperature = 0 # 0..1

def randomize(self):
self.height = (random.random() - 0.5)*1000
self.moisture = random.random()
self.temperature = random.random()

def render(self, rect, surface):
heightValue = ((self.height / 1000.0)+0.5) * 255
if heightValue > 255:
heightValue = 255
if heightValue < 0:
heightValue = 0
color = (heightValue, heightValue, heightValue)
surface.fill(color, rect)


class TerrainGenerator():
def apply(self, grid):
pass

class MeteorTerrainGenerator(TerrainGenerator):
def __init__(self, strikes=25, strikeMinRadius=20, strikeMaxRadius=100):
self.strikes = strikes
self.strikeMinRadius = strikeMinRadius
self.strikeMaxRadius = strikeMaxRadius

def apply(self, grid):
for i in range(0, self.strikes):
strikeLocation = (random.randint(grid.minX, grid.maxX), random.randint(grid.minY, grid.maxY), -1)
strikeNode = grid.get_node_at(strikeLocation)

strikeRadius = random.randint(self.strikeMinRadius, self.strikeMaxRadius)
sqrStrikeRadius = strikeRadius * strikeRadius

if None == strikeNode:
print "Out-of-range meteor strike at ",
print strikeLocation


for node in grid.nodes():
if node.location[2] == -1:
terrainData = node.contents
sqrDistance = math.pow(node.location[0] - strikeLocation[0], 2) + math.pow(node.location[1] - strikeLocation[1], 2)

# meteor profile is essentially a cosine curve
# (high at the center, falls off, high again at
# the crater edge)
# this is not exactly right, but close enough for
# the moment
# cubing creates a bit more of a reasonable "crater"
if sqrDistance < sqrStrikeRadius:
change = math.pow(math.cos(sqrDistance / sqrStrikeRadius * math.pi * 3), 3)
change *= (1 - (sqrDistance / sqrStrikeRadius))
change *= strikeRadius * 3
terrainData.height += change


0 comments on commit a9bf983

Please sign in to comment.