Advertisement
asweigart

Squirrel Eat Squirrel source code

Nov 21st, 2017
328
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.61 KB | None | 0 0
  1. # Squirrel Eat Squirrel (a 2D Katamari Damacy clone)
  2. # By Al Sweigart al@inventwithpython.com
  3. # http://inventwithpython.com/pygame
  4. # Released under a "Simplified BSD" license
  5.  
  6. import random, sys, time, math, pygame
  7. from pygame.locals import *
  8.  
  9. FPS = 30 # frames per second to update the screen
  10. WINWIDTH = 640 # width of the program's window, in pixels
  11. WINHEIGHT = 480 # height in pixels
  12. HALF_WINWIDTH = int(WINWIDTH / 2)
  13. HALF_WINHEIGHT = int(WINHEIGHT / 2)
  14.  
  15. GRASSCOLOR = (24, 255, 0)
  16. WHITE = (255, 255, 255)
  17. RED = (255, 0, 0)
  18.  
  19. CAMERASLACK = 90     # how far from the center the squirrel moves before moving the camera
  20. MOVERATE = 9         # how fast the player moves
  21. BOUNCERATE = 6       # how fast the player bounces (large is slower)
  22. BOUNCEHEIGHT = 30    # how high the player bounces
  23. STARTSIZE = 25       # how big the player starts off
  24. WINSIZE = 300        # how big the player needs to be to win
  25. INVULNTIME = 2       # how long the player is invulnerable after being hit in seconds
  26. GAMEOVERTIME = 4     # how long the "game over" text stays on the screen in seconds
  27. MAXHEALTH = 3        # how much health the player starts with
  28.  
  29. NUMGRASS = 80        # number of grass objects in the active area
  30. NUMSQUIRRELS = 30    # number of squirrels in the active area
  31. SQUIRRELMINSPEED = 3 # slowest squirrel speed
  32. SQUIRRELMAXSPEED = 7 # fastest squirrel speed
  33. DIRCHANGEFREQ = 2    # % chance of direction change per frame
  34. LEFT = 'left'
  35. RIGHT = 'right'
  36.  
  37. """
  38. This program has three data structures to represent the player, enemy squirrels, and grass background objects. The data structures are dictionaries with the following keys:
  39.  
  40. Keys used by all three data structures:
  41.    'x' - the left edge coordinate of the object in the game world (not a pixel coordinate on the screen)
  42.    'y' - the top edge coordinate of the object in the game world (not a pixel coordinate on the screen)
  43.    'rect' - the pygame.Rect object representing where on the screen the object is located.
  44. Player data structure keys:
  45.    'surface' - the pygame.Surface object that stores the image of the squirrel which will be drawn to the screen.
  46.    'facing' - either set to LEFT or RIGHT, stores which direction the player is facing.
  47.    'size' - the width and height of the player in pixels. (The width & height are always the same.)
  48.    'bounce' - represents at what point in a bounce the player is in. 0 means standing (no bounce), up to BOUNCERATE (the completion of the bounce)
  49.    'health' - an integer showing how many more times the player can be hit by a larger squirrel before dying.
  50. Enemy Squirrel data structure keys:
  51.    'surface' - the pygame.Surface object that stores the image of the squirrel which will be drawn to the screen.
  52.    'movex' - how many pixels per frame the squirrel moves horizontally. A negative integer is moving to the left, a positive to the right.
  53.    'movey' - how many pixels per frame the squirrel moves vertically. A negative integer is moving up, a positive moving down.
  54.    'width' - the width of the squirrel's image, in pixels
  55.    'height' - the height of the squirrel's image, in pixels
  56.    'bounce' - represents at what point in a bounce the player is in. 0 means standing (no bounce), up to BOUNCERATE (the completion of the bounce)
  57.    'bouncerate' - how quickly the squirrel bounces. A lower number means a quicker bounce.
  58.    'bounceheight' - how high (in pixels) the squirrel bounces
  59. Grass data structure keys:
  60.    'grassImage' - an integer that refers to the index of the pygame.Surface object in GRASSIMAGES used for this grass object
  61. """
  62.  
  63. def main():
  64.     global FPSCLOCK, DISPLAYSURF, BASICFONT, L_SQUIR_IMG, R_SQUIR_IMG, GRASSIMAGES
  65.  
  66.     pygame.init()
  67.     FPSCLOCK = pygame.time.Clock()
  68.     pygame.display.set_icon(pygame.image.load('gameicon.png'))
  69.     DISPLAYSURF = pygame.display.set_mode((WINWIDTH, WINHEIGHT))
  70.     pygame.display.set_caption('Squirrel Eat Squirrel')
  71.     BASICFONT = pygame.font.Font('freesansbold.ttf', 32)
  72.  
  73.     # load the image files
  74.     L_SQUIR_IMG = pygame.image.load('squirrel.png')
  75.     R_SQUIR_IMG = pygame.transform.flip(L_SQUIR_IMG, True, False)
  76.     GRASSIMAGES = []
  77.     for i in range(1, 5):
  78.         GRASSIMAGES.append(pygame.image.load('grass%s.png' % i))
  79.  
  80.     while True:
  81.         runGame()
  82.  
  83.  
  84. def runGame():
  85.     # set up variables for the start of a new game
  86.     invulnerableMode = False  # if the player is invulnerable
  87.     invulnerableStartTime = 0 # time the player became invulnerable
  88.     gameOverMode = False      # if the player has lost
  89.     gameOverStartTime = 0     # time the player lost
  90.     winMode = False           # if the player has won
  91.  
  92.     # create the surfaces to hold game text
  93.     gameOverSurf = BASICFONT.render('Game Over', True, WHITE)
  94.     gameOverRect = gameOverSurf.get_rect()
  95.     gameOverRect.center = (HALF_WINWIDTH, HALF_WINHEIGHT)
  96.  
  97.     winSurf = BASICFONT.render('You have achieved OMEGA SQUIRREL!', True, WHITE)
  98.     winRect = winSurf.get_rect()
  99.     winRect.center = (HALF_WINWIDTH, HALF_WINHEIGHT)
  100.  
  101.     winSurf2 = BASICFONT.render('(Press "r" to restart.)', True, WHITE)
  102.     winRect2 = winSurf2.get_rect()
  103.     winRect2.center = (HALF_WINWIDTH, HALF_WINHEIGHT + 30)
  104.  
  105.     # camerax and cameray are the top left of where the camera view is
  106.     camerax = 0
  107.     cameray = 0
  108.  
  109.     grassObjs = []    # stores all the grass objects in the game
  110.     squirrelObjs = [] # stores all the non-player squirrel objects
  111.     # stores the player object:
  112.     playerObj = {'surface': pygame.transform.scale(L_SQUIR_IMG, (STARTSIZE, STARTSIZE)),
  113.                  'facing': LEFT,
  114.                  'size': STARTSIZE,
  115.                  'x': HALF_WINWIDTH,
  116.                  'y': HALF_WINHEIGHT,
  117.                  'bounce':0,
  118.                  'health': MAXHEALTH}
  119.  
  120.     moveLeft  = False
  121.     moveRight = False
  122.     moveUp    = False
  123.     moveDown  = False
  124.  
  125.     # start off with some random grass images on the screen
  126.     for i in range(10):
  127.         grassObjs.append(makeNewGrass(camerax, cameray))
  128.         grassObjs[i]['x'] = random.randint(0, WINWIDTH)
  129.         grassObjs[i]['y'] = random.randint(0, WINHEIGHT)
  130.  
  131.     while True: # main game loop
  132.         # Check if we should turn off invulnerability
  133.         if invulnerableMode and time.time() - invulnerableStartTime > INVULNTIME:
  134.             invulnerableMode = False
  135.  
  136.         # move all the squirrels
  137.         for sObj in squirrelObjs:
  138.             # move the squirrel, and adjust for their bounce
  139.             sObj['x'] += sObj['movex']
  140.             sObj['y'] += sObj['movey']
  141.             sObj['bounce'] += 1
  142.             if sObj['bounce'] > sObj['bouncerate']:
  143.                 sObj['bounce'] = 0 # reset bounce amount
  144.  
  145.             # random chance they change direction
  146.             if random.randint(0, 99) < DIRCHANGEFREQ:
  147.                 sObj['movex'] = getRandomVelocity()
  148.                 sObj['movey'] = getRandomVelocity()
  149.                 if sObj['movex'] > 0: # faces right
  150.                     sObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (sObj['width'], sObj['height']))
  151.                 else: # faces left
  152.                     sObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (sObj['width'], sObj['height']))
  153.  
  154.  
  155.         # go through all the objects and see if any need to be deleted.
  156.         for i in range(len(grassObjs) - 1, -1, -1):
  157.             if isOutsideActiveArea(camerax, cameray, grassObjs[i]):
  158.                 del grassObjs[i]
  159.         for i in range(len(squirrelObjs) - 1, -1, -1):
  160.             if isOutsideActiveArea(camerax, cameray, squirrelObjs[i]):
  161.                 del squirrelObjs[i]
  162.  
  163.         # add more grass & squirrels if we don't have enough.
  164.         while len(grassObjs) < NUMGRASS:
  165.             grassObjs.append(makeNewGrass(camerax, cameray))
  166.         while len(squirrelObjs) < NUMSQUIRRELS:
  167.             squirrelObjs.append(makeNewSquirrel(camerax, cameray))
  168.  
  169.         # adjust camerax and cameray if beyond the "camera slack"
  170.         playerCenterx = playerObj['x'] + int(playerObj['size'] / 2)
  171.         playerCentery = playerObj['y'] + int(playerObj['size'] / 2)
  172.         if (camerax + HALF_WINWIDTH) - playerCenterx > CAMERASLACK:
  173.             camerax = playerCenterx + CAMERASLACK - HALF_WINWIDTH
  174.         elif playerCenterx - (camerax + HALF_WINWIDTH) > CAMERASLACK:
  175.             camerax = playerCenterx - CAMERASLACK - HALF_WINWIDTH
  176.         if (cameray + HALF_WINHEIGHT) - playerCentery > CAMERASLACK:
  177.             cameray = playerCentery + CAMERASLACK - HALF_WINHEIGHT
  178.         elif playerCentery - (cameray + HALF_WINHEIGHT) > CAMERASLACK:
  179.             cameray = playerCentery - CAMERASLACK - HALF_WINHEIGHT
  180.  
  181.         # draw the green background
  182.         DISPLAYSURF.fill(GRASSCOLOR)
  183.  
  184.         # draw all the grass objects on the screen
  185.         for gObj in grassObjs:
  186.             gRect = pygame.Rect( (gObj['x'] - camerax,
  187.                                   gObj['y'] - cameray,
  188.                                   gObj['width'],
  189.                                   gObj['height']) )
  190.             DISPLAYSURF.blit(GRASSIMAGES[gObj['grassImage']], gRect)
  191.  
  192.  
  193.         # draw the other squirrels
  194.         for sObj in squirrelObjs:
  195.             sObj['rect'] = pygame.Rect( (sObj['x'] - camerax,
  196.                                          sObj['y'] - cameray - getBounceAmount(sObj['bounce'], sObj['bouncerate'], sObj['bounceheight']),
  197.                                          sObj['width'],
  198.                                          sObj['height']) )
  199.             DISPLAYSURF.blit(sObj['surface'], sObj['rect'])
  200.  
  201.  
  202.         # draw the player squirrel
  203.         flashIsOn = round(time.time(), 1) * 10 % 2 == 1
  204.         if not gameOverMode and not (invulnerableMode and flashIsOn):
  205.             playerObj['rect'] = pygame.Rect( (playerObj['x'] - camerax,
  206.                                               playerObj['y'] - cameray - getBounceAmount(playerObj['bounce'], BOUNCERATE, BOUNCEHEIGHT),
  207.                                               playerObj['size'],
  208.                                               playerObj['size']) )
  209.             DISPLAYSURF.blit(playerObj['surface'], playerObj['rect'])
  210.  
  211.  
  212.         # draw the health meter
  213.         drawHealthMeter(playerObj['health'])
  214.  
  215.         for event in pygame.event.get(): # event handling loop
  216.             if event.type == QUIT:
  217.                 terminate()
  218.  
  219.             elif event.type == KEYDOWN:
  220.                 if event.key in (K_UP, K_w):
  221.                     moveDown = False
  222.                     moveUp = True
  223.                 elif event.key in (K_DOWN, K_s):
  224.                     moveUp = False
  225.                     moveDown = True
  226.                 elif event.key in (K_LEFT, K_a):
  227.                     moveRight = False
  228.                     moveLeft = True
  229.                     if playerObj['facing'] != LEFT: # change player image
  230.                         playerObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (playerObj['size'], playerObj['size']))
  231.                     playerObj['facing'] = LEFT
  232.                 elif event.key in (K_RIGHT, K_d):
  233.                     moveLeft = False
  234.                     moveRight = True
  235.                     if playerObj['facing'] != RIGHT: # change player image
  236.                         playerObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (playerObj['size'], playerObj['size']))
  237.                     playerObj['facing'] = RIGHT
  238.                 elif winMode and event.key == K_r:
  239.                     return
  240.  
  241.             elif event.type == KEYUP:
  242.                 # stop moving the player's squirrel
  243.                 if event.key in (K_LEFT, K_a):
  244.                     moveLeft = False
  245.                 elif event.key in (K_RIGHT, K_d):
  246.                     moveRight = False
  247.                 elif event.key in (K_UP, K_w):
  248.                     moveUp = False
  249.                 elif event.key in (K_DOWN, K_s):
  250.                     moveDown = False
  251.  
  252.                 elif event.key == K_ESCAPE:
  253.                     terminate()
  254.  
  255.         if not gameOverMode:
  256.             # actually move the player
  257.             if moveLeft:
  258.                 playerObj['x'] -= MOVERATE
  259.             if moveRight:
  260.                 playerObj['x'] += MOVERATE
  261.             if moveUp:
  262.                 playerObj['y'] -= MOVERATE
  263.             if moveDown:
  264.                 playerObj['y'] += MOVERATE
  265.  
  266.             if (moveLeft or moveRight or moveUp or moveDown) or playerObj['bounce'] != 0:
  267.                 playerObj['bounce'] += 1
  268.  
  269.             if playerObj['bounce'] > BOUNCERATE:
  270.                 playerObj['bounce'] = 0 # reset bounce amount
  271.  
  272.             # check if the player has collided with any squirrels
  273.             for i in range(len(squirrelObjs)-1, -1, -1):
  274.                 sqObj = squirrelObjs[i]
  275.                 if 'rect' in sqObj and playerObj['rect'].colliderect(sqObj['rect']):
  276.                     # a player/squirrel collision has occurred
  277.  
  278.                     if sqObj['width'] * sqObj['height'] <= playerObj['size']**2:
  279.                         # player is larger and eats the squirrel
  280.                         playerObj['size'] += int( (sqObj['width'] * sqObj['height'])**0.2 ) + 1
  281.                         del squirrelObjs[i]
  282.  
  283.                         if playerObj['facing'] == LEFT:
  284.                             playerObj['surface'] = pygame.transform.scale(L_SQUIR_IMG, (playerObj['size'], playerObj['size']))
  285.                         if playerObj['facing'] == RIGHT:
  286.                             playerObj['surface'] = pygame.transform.scale(R_SQUIR_IMG, (playerObj['size'], playerObj['size']))
  287.  
  288.                         if playerObj['size'] > WINSIZE:
  289.                             winMode = True # turn on "win mode"
  290.  
  291.                     elif not invulnerableMode:
  292.                         # player is smaller and takes damage
  293.                         invulnerableMode = True
  294.                         invulnerableStartTime = time.time()
  295.                         playerObj['health'] -= 1
  296.                         if playerObj['health'] == 0:
  297.                             gameOverMode = True # turn on "game over mode"
  298.                             gameOverStartTime = time.time()
  299.         else:
  300.             # game is over, show "game over" text
  301.             DISPLAYSURF.blit(gameOverSurf, gameOverRect)
  302.             if time.time() - gameOverStartTime > GAMEOVERTIME:
  303.                 return # end the current game
  304.  
  305.         # check if the player has won.
  306.         if winMode:
  307.             DISPLAYSURF.blit(winSurf, winRect)
  308.             DISPLAYSURF.blit(winSurf2, winRect2)
  309.  
  310.         pygame.display.update()
  311.         FPSCLOCK.tick(FPS)
  312.  
  313.  
  314.  
  315.  
  316. def drawHealthMeter(currentHealth):
  317.     for i in range(currentHealth): # draw red health bars
  318.         pygame.draw.rect(DISPLAYSURF, RED,   (15, 5 + (10 * MAXHEALTH) - i * 10, 20, 10))
  319.     for i in range(MAXHEALTH): # draw the white outlines
  320.         pygame.draw.rect(DISPLAYSURF, WHITE, (15, 5 + (10 * MAXHEALTH) - i * 10, 20, 10), 1)
  321.  
  322.  
  323. def terminate():
  324.     pygame.quit()
  325.     sys.exit()
  326.  
  327.  
  328. def getBounceAmount(currentBounce, bounceRate, bounceHeight):
  329.     # Returns the number of pixels to offset based on the bounce.
  330.     # Larger bounceRate means a slower bounce.
  331.     # Larger bounceHeight means a higher bounce.
  332.     # currentBounce will always be less than bounceRate
  333.     return int(math.sin( (math.pi / float(bounceRate)) * currentBounce ) * bounceHeight)
  334.  
  335. def getRandomVelocity():
  336.     speed = random.randint(SQUIRRELMINSPEED, SQUIRRELMAXSPEED)
  337.     if random.randint(0, 1) == 0:
  338.         return speed
  339.     else:
  340.         return -speed
  341.  
  342.  
  343. def getRandomOffCameraPos(camerax, cameray, objWidth, objHeight):
  344.     # create a Rect of the camera view
  345.     cameraRect = pygame.Rect(camerax, cameray, WINWIDTH, WINHEIGHT)
  346.     while True:
  347.         x = random.randint(camerax - WINWIDTH, camerax + (2 * WINWIDTH))
  348.         y = random.randint(cameray - WINHEIGHT, cameray + (2 * WINHEIGHT))
  349.         # create a Rect object with the random coordinates and use colliderect()
  350.         # to make sure the right edge isn't in the camera view.
  351.         objRect = pygame.Rect(x, y, objWidth, objHeight)
  352.         if not objRect.colliderect(cameraRect):
  353.             return x, y
  354.  
  355.  
  356. def makeNewSquirrel(camerax, cameray):
  357.     sq = {}
  358.     generalSize = random.randint(5, 25)
  359.     multiplier = random.randint(1, 3)
  360.     sq['width']  = (generalSize + random.randint(0, 10)) * multiplier
  361.     sq['height'] = (generalSize + random.randint(0, 10)) * multiplier
  362.     sq['x'], sq['y'] = getRandomOffCameraPos(camerax, cameray, sq['width'], sq['height'])
  363.     sq['movex'] = getRandomVelocity()
  364.     sq['movey'] = getRandomVelocity()
  365.     if sq['movex'] < 0: # squirrel is facing left
  366.         sq['surface'] = pygame.transform.scale(L_SQUIR_IMG, (sq['width'], sq['height']))
  367.     else: # squirrel is facing right
  368.         sq['surface'] = pygame.transform.scale(R_SQUIR_IMG, (sq['width'], sq['height']))
  369.     sq['bounce'] = 0
  370.     sq['bouncerate'] = random.randint(10, 18)
  371.     sq['bounceheight'] = random.randint(10, 50)
  372.     return sq
  373.  
  374.  
  375. def makeNewGrass(camerax, cameray):
  376.     gr = {}
  377.     gr['grassImage'] = random.randint(0, len(GRASSIMAGES) - 1)
  378.     gr['width']  = GRASSIMAGES[0].get_width()
  379.     gr['height'] = GRASSIMAGES[0].get_height()
  380.     gr['x'], gr['y'] = getRandomOffCameraPos(camerax, cameray, gr['width'], gr['height'])
  381.     gr['rect'] = pygame.Rect( (gr['x'], gr['y'], gr['width'], gr['height']) )
  382.     return gr
  383.  
  384.  
  385. def isOutsideActiveArea(camerax, cameray, obj):
  386.     # Return False if camerax and cameray are more than
  387.     # a half-window length beyond the edge of the window.
  388.     boundsLeftEdge = camerax - WINWIDTH
  389.     boundsTopEdge = cameray - WINHEIGHT
  390.     boundsRect = pygame.Rect(boundsLeftEdge, boundsTopEdge, WINWIDTH * 3, WINHEIGHT * 3)
  391.     objRect = pygame.Rect(obj['x'], obj['y'], obj['width'], obj['height'])
  392.     return not boundsRect.colliderect(objRect)
  393.  
  394.  
  395. if __name__ == '__main__':
  396.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement