Friday, June 14, 2013

Map Generation - Finish Him!

I've got some working code for the Map Gen class I've been working on. There are some issues with placing rooms on top of existing stuff, and I'd like to code something to place walls on the outside of the rectangles that make up my rooms and hallways. But at least it's working now, and it's been pretty fun to work on up to this point.

Loaded the mapgen class into a pygame testbed

I know this is a crappy screenshot, but you can see that there are several rooms (the black rectangles) and hall-objects that connect them. Ignore the crappy images I used for the rooms and player objects. I know they suck, but I've been using them as my standard test images for a while now.

Currently the room generator chooses from a number of pre-loaded images and scales the image to fit the random size. I'm thinking I may change it to tile a floor pattern on the room after a size is chosen. This way I don't end up with floors that are clearly the same image, just stretched to fit. Either that, or just use the same floors and have the room generators just draw random carpets or mats or something to draw attention from the stretched image underneath...

I also want to place some generic walls on each room/hall then add random pictures, posters and windows.

For now though, you just import the class, and run genMaps() and it returns a list (or array if you prefer) of these room/hall objects. You can then use the pygame rectangle objects for collisions  and the pygame image (surface) objects to draw them to another surface.

Here's the source for this class file:
Click here to expand Source
import random, pygame

class RoomObj:
  def update(self,anchorpoint):
    self.rect.left = self.xoffset + anchorpoint[0]
    self.rect.top = self.yoffset + anchorpoint[1]
  
class Room(RoomObj):
  def __init__(self, imageset, direction=0, room_rect=None):
    #RoomObj.__init__(self)
    self.direction = random.randint(1,4) #up, down, left, right
    self.size = (random.randint(400,600),random.randint(500,700))
    self.image = pygame.Surface(self.size)
    pygame.transform.scale(random.choice(imageset), self.size, self.image)
    self.rect = self.image.get_rect()
    if direction==0:
      self.rect.top = 0
      self.rect.left = 0
    if direction==1:
      if self.direction == 2:
        self.direction = 1
      self.rect.bottom = room_rect.top
      self.rect.left = room_rect.left-(self.size[0]/2)
    if direction==2:
      if self.direction == 1:
        self.direction = 2
      self.rect.top = room_rect.bottom
      self.rect.left = room_rect.left-(self.size[0]/2)
    if direction==3:
      if self.direction == 4:
        self.direction = 3
      self.rect.top = room_rect.top-(self.size[1]/2)
      self.rect.right = room_rect.left
    if direction==4:
      if self.direction == 3:
        self.direction = 4
      self.rect.top = room_rect.top-(self.size[1]/2)
      self.rect.left = room_rect.right
    self.xoffset = self.rect.left
    self.yoffset = self.rect.top
  
  
class Hall(RoomObj):
  def __init__(self, direction, room_rect, imageset):
    #RoomObj.__init__(self)
    self.size = (80,random.randint(100,300))
    self.image = pygame.Surface(self.size)
    tmpimage = random.choice(imageset)
    if direction==1:
      self.image = pygame.transform.scale(tmpimage, self.size, self.image)
      self.rect = self.image.get_rect()
      self.rect.bottom = room_rect.top
      self.rect.left = random.randint(room_rect.left, room_rect.right-80)
      self.direction=1
    
    if direction==2:
      self.image = pygame.transform.scale(tmpimage, self.size, self.image)
      self.rect = self.image.get_rect()
      self.rect.top = room_rect.bottom
      self.rect.left = random.randint(room_rect.left, room_rect.right-80)
      self.direction=2
    
    if direction==3:
      self.size = (self.size[1],self.size[0])
      self.image = pygame.Surface(self.size)
      self.image = pygame.transform.rotate(tmpimage, 90)
      #print self.size
      #print self.image.get_size()
      self.image = pygame.transform.scale(tmpimage, self.image.get_size(), self.image)
      self.rect = self.image.get_rect()
      self.rect.top = random.randint(room_rect.top, room_rect.bottom-80)
      self.rect.right = room_rect.left
      self.direction=3
    
    if direction==4:
      self.size = (self.size[1],self.size[0])
      self.image = pygame.Surface(self.size)
      self.image = pygame.transform.rotate(tmpimage, 90)
      self.image = pygame.transform.scale(tmpimage, self.image.get_size(), self.image)
      self.rect = self.image.get_rect()
      self.rect.top = random.randint(room_rect.top, room_rect.bottom-80)
      self.rect.left = room_rect.right
      self.direction=4
    
    self.xoffset = self.rect.left
    self.yoffset = self.rect.top
  
def genMap(size,roomimages,hallimages):
  """
  takes 3 perameters: size (string), roomimages (array of pygame images/surfaces),
  and hallimages (also an array of pygame images)
 
  size should be small, medium or large
  the two arrays should use pygame.image.load() objects or pygame.Surface objects
  e.g. [pygame.image.load("file1.png").convert_alpha(), pygame.image.load("file2.jpg)]
 
  returns an array containing room and hall objects.
 
  each room or hall can be moved through the use of an anchor point variable.
  e.g. mapobjects[0].update(anchorpoint) where anchorpoint is the (x, y) offset
  """
  numofrooms=0
  if size=='small':
    numofrooms = random.randint(3,5)
  if size=='medium':
    numofrooms = random.randint(5,8)
  rooms = []
  map = []
  for i in range(numofrooms):
    if i == 0:
      tmproom = Room(roomimages)
      map.append(tmproom)
      tmphall = Hall(tmproom.direction, tmproom.rect, hallimages)
      map.append(tmphall)
    else:
      if i < len(range(numofrooms))-1:
        tmproom = Room(roomimages, tmphall.direction, tmphall.rect)
        map.append(tmproom)
        tmphall=Hall(tmproom.direction, tmproom.rect, hallimages)
        map.append(tmphall)
      else:
        map.append(Room(roomimages, tmphall.direction, tmphall.rect))
  return map

*Edited - 6-21-2013* I decided to go through the class file and make a few minor updates. I added some checking to see if there was an existing object where the new room was going to be placed. I also changed the logic a bit in the loop that created the Rooms/Halls. I also went through and commented it all. (Except the Room subclass; I figured it was close enough to the Hall, I didn't need to comment both)
In any case, here's the cleaned up class file:

Click here to expand the cleaned up Source
import random, pygame

class RoomObj:
  def __init__(self, wallimgset):
    if self.type == 'room':
      #create walls for a room
      pass
    if self.type == 'hall':
      #create walls for a room
      pass
  def update(self,anchorpoint):
    self.rect.left = self.xoffset + anchorpoint[0]
    self.rect.top = self.yoffset + anchorpoint[1]
  
class Room(RoomObj):
  def __init__(self, imageset, direction=0, room_rect=None, wallimgset=[]):
    #main room rectangle
    self.type = 'room'
    self.direction = random.randint(1,4) #up, down, left, right
    self.size = (random.randint(400,600),random.randint(500,700))
    self.image = pygame.Surface(self.size)
    pygame.transform.scale(random.choice(imageset), self.size, self.image)
    self.rect = self.image.get_rect()
    if direction==0:
      self.rect.top = 0
      self.rect.left = 0
    if direction==1:
      if self.direction == 2:
        self.direction = 1
      self.rect.bottom = room_rect.top
      self.rect.left = room_rect.left-(self.size[0]/2)
    if direction==2:
      if self.direction == 1:
        self.direction = 2
      self.rect.top = room_rect.bottom
      self.rect.left = room_rect.left-(self.size[0]/2)
    if direction==3:
      if self.direction == 4:
        self.direction = 3
      self.rect.top = room_rect.top-(self.size[1]/2)
      self.rect.right = room_rect.left
    if direction==4:
      if self.direction == 3:
        self.direction = 4
      self.rect.top = room_rect.top-(self.size[1]/2)
      self.rect.left = room_rect.right
    self.xoffset = self.rect.left
    self.yoffset = self.rect.top
    RoomObj.__init__(self,wallimgset)
   
   
class Hall(RoomObj):
  #initialize our hall object
  def __init__(self, direction, room_rect, imageset, wallimgset=[]):
    self.type = 'hall'
    #size may want to be changed to be relative to screen size
    self.size = (80,random.randint(100,300))
    #will be surface that is drawn to screen
    self.image = pygame.Surface(self.size)
    #will be drawn to self.image surface
    tmpimage = random.choice(imageset)
    #hall will be facing up and down
    if direction==1:
      self.image = pygame.transform.scale(tmpimage, self.size, self.image)
      self.rect = self.image.get_rect()
      self.rect.bottom = room_rect.top
      self.rect.left = random.randint(room_rect.left, room_rect.right-80)
      self.direction=1
    #hall will be up and down 
    if direction==2:
      self.image = pygame.transform.scale(tmpimage, self.size, self.image)
      self.rect = self.image.get_rect()
      self.rect.top = room_rect.bottom
      self.rect.left = random.randint(room_rect.left, room_rect.right-80)
      self.direction=2
    #hall will be left to right
    if direction==3:
      self.size = (self.size[1],self.size[0])
      self.image = pygame.Surface(self.size)
      self.image = pygame.transform.rotate(tmpimage, 90)
      self.image = pygame.transform.scale(tmpimage, self.image.get_size(), self.image)
      self.rect = self.image.get_rect()
      self.rect.top = random.randint(room_rect.top, room_rect.bottom-80)
      self.rect.right = room_rect.left
      self.direction=3
    #hall will be left to right
    if direction==4:
      self.size = (self.size[1],self.size[0])
      self.image = pygame.Surface(self.size)
      self.image = pygame.transform.rotate(tmpimage, 90)
      self.image = pygame.transform.scale(tmpimage, self.image.get_size(), self.image)
      self.rect = self.image.get_rect()
      self.rect.top = random.randint(room_rect.top, room_rect.bottom-80)
      self.rect.left = room_rect.right
      self.direction=4
    #set offset to use with the update function
    self.xoffset = self.rect.left
    self.yoffset = self.rect.top
    #init the parent class to create the walls and draw the floors
    RoomObj.__init__(self, wallimgset)

def checkForExisting(room, maplist):
  for i in maplist:
    if room.rect.contains(i.rect):
      return True
  return False
   
def genMap(size,roomimages,hallimages):
  """
  takes 3 perameters: size (string), roomimages (array of pygame images/surfaces),
  and hallimages (also an array of pygame images)
 
  size should be small, medium or large
  the two arrays should use pygame.image.load() objects or pygame.Surface objects
  e.g. [pygame.image.load("file1.png").convert_alpha(), pygame.image.load("file2.jpg)]
 
  returns an array containing room and hall objects.
 
  each room or hall can be moved through the use of an anchor point variable.
  e.g. mapobjects[0].update(anchorpoint) where anchorpoint is the (x, y) offset
  """
  #num of rooms will be number of nodes to create
  numofrooms=0
  if size=='small':
    numofrooms = random.randint(3,5)
  if size=='medium':
    numofrooms = random.randint(5,8)
  #add a few to account for joining hall nodes
  numofrooms = numofrooms+(numofrooms-1)
  rooms = []
  map = []
  #while map array length is less than total number of desired nodes...
  while len(map) < numofrooms:
    #first create a room and a hall
    if len(map)==0:
      tmproom = Room(roomimages)
      map.append(tmproom)
      tmphall = Hall(tmproom.direction, tmproom.rect, hallimages)
      map.append(tmphall)
    #create all other nodes
    else:
      # if there are at least 2 spots left on the list, create a room, then a hall
      if len(map) < numofrooms-2:
        tmproom = Room(roomimages, tmphall.direction, tmphall.rect)
        #loop several times to ensure the new room isn't put on top of an old one
        #counter is to ensure we don't end up with an endless loop
        counter = 0
        while checkForExisting(tmproom, map):
          tmproom = Room(roomimages, tmphall.direction, tmphall.rect)
          counter+=1
          #we tried 10 times and couldn't place the room, knock off the last
          #hall and room, and try again
          if counter >10:
            map = map[:-2]
        map.append(tmproom)
        tmphall=Hall(tmproom.direction, tmproom.rect, hallimages)
        map.append(tmphall)
      #only one spot left on our list? create a room
      if len(map) == numofrooms-1:
        tmproom = Room(roomimages, tmphall.direction, tmphall.rect)
        counter=0
        while checkForExisting(tmproom, map):
          tmproom = Room(roomimages, tmphall.direction, tmphall.rect)
          counter+=1
          if counter >10:
            map = map[:-2]
        #map.append(Room(roomimages, tmphall.direction, tmphall.rect))
        map.append(tmproom)
  return map

No comments:

Post a Comment