Python game development, pygame module, python implementation of the reconstructed dinosaur jump game

preface

Today, we will reconstruct a version of the little dinosaur jump game and share it with you. There is no more nonsense. Let's start happily~

Effect display

development tool

Python version: 3.6 four

Related modules:

pygame module;

And some python built-in modules.

Environment construction

Install Python and add it to the environment variable. pip can install the relevant modules required.

Principle introduction

Here is the implementation principle of the game. First, for the convenience of dragon training, I didn't add the scene effect of night, that is, the game only has the scene of day.

First, we need to initialize the game:

# Game initialization
pygame.init()
screen = pygame.display.set_mode(cfg.SCREENSIZE)
pygame.display.set_caption('T-Rex Rush - Charles Picchu')
# Import all sound files
sounds = {}
for key, value in cfg.AUDIO_PATHS.items():
  sounds[key] = pygame.mixer.Sound(value)

Next, let's consider what game elements are in the game:

  • Little dinosaur: controlled by the player to avoid obstacles on the road;
  • Pavement: the background of the game;
  • Cloud: the background of the game;
  • Flying Dragon: one of the obstacles on the road, the little dinosaur will die if it meets;
  • Cactus: one of the obstacles on the road, the little dinosaur will die if it meets;
  • Scoreboard: record the current score and the highest score in history.

Let's define these game element classes in turn. For clouds, roads and cactus, the definition is very simple. We only need to load the corresponding game element pictures:

Then write two class internal methods update and draw.

The two methods are respectively used to move the scene to the left to realize the animation effect of the small dinosaur moving forward and to display the scene in the corresponding position of the game interface.
Specifically, the code implementation is as follows:

'''floor'''
class Ground(pygame.sprite.Sprite):
  def __init__(self, imagepath, position, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    # import picture
    self.image_0 = pygame.image.load(imagepath)
    self.rect_0 = self.image_0.get_rect()
    self.rect_0.left, self.rect_0.bottom = position
    self.image_1 = pygame.image.load(imagepath)
    self.rect_1 = self.image_1.get_rect()
    self.rect_1.left, self.rect_1.bottom = self.rect_0.right, self.rect_0.bottom
    # Define some necessary parameters
    self.speed = -10
  '''Renew floor'''
  def update(self):
    self.rect_0.left += self.speed
    self.rect_1.left += self.speed
    if self.rect_0.right < 0:
      self.rect_0.left = self.rect_1.right
    if self.rect_1.right < 0:
      self.rect_1.left = self.rect_0.right
  '''Draw floor to screen'''
  def draw(self, screen):
    screen.blit(self.image_0, self.rect_0)
    screen.blit(self.image_1, self.rect_1)

'''cloud'''
class Cloud(pygame.sprite.Sprite):
  def __init__(self, imagepath, position, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    # import picture
    self.image = pygame.image.load(imagepath)
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.top = position
    # Define some necessary parameters
    self.speed = -1
  '''Draw clouds to the screen'''
  def draw(self, screen):
    screen.blit(self.image, self.rect)
  '''Update cloud'''
  def update(self):
    self.rect = self.rect.move([self.speed, 0])
    if self.rect.right < 0:
      self.kill()

'''cactus'''
class Cactus(pygame.sprite.Sprite):
  def __init__(self, imagepaths, position=(600, 147), sizes=[(40, 40), (40, 40)], **kwargs):
    pygame.sprite.Sprite.__init__(self)
    # import picture
    self.images = []
    image = pygame.image.load(imagepaths[0])
    for i in range(3):
      self.images.append(pygame.transform.scale(image.subsurface((i*101, 0), (101, 101)), sizes[0]))
    image = pygame.image.load(imagepaths[1])
    for i in range(3):
      self.images.append(pygame.transform.scale(image.subsurface((i*68, 0), (68, 70)), sizes[1]))
    self.image = random.choice(self.images)
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.bottom = position
    self.mask = pygame.mask.from_surface(self.image)
    # Define some necessary variables
    self.speed = -10
  '''Draw to screen'''
  def draw(self, screen):
    screen.blit(self.image, self.rect)
  '''to update'''
  def update(self):
    self.rect = self.rect.move([self.speed, 0])
    if self.rect.right < 0:
      self.kill()

The definition of scoreboard is similar, but it does not need to be moved, but it needs to update the current score in real time:

'''Scoreboard'''
class Scoreboard(pygame.sprite.Sprite):
  def __init__(self, imagepath, position, size=(11, 13), is_highest=False, bg_color=None, **kwargs):
    pygame.sprite.Sprite.__init__(self)
    # import picture
    self.images = []
    image = pygame.image.load(imagepath)
    for i in range(12):
      self.images.append(pygame.transform.scale(image.subsurface((i*20, 0), (20, 24)), size))
    if is_highest:
      self.image = pygame.Surface((size[0]*8, size[1]))
    else:
      self.image = pygame.Surface((size[0]*5, size[1]))
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.top = position
    # Some necessary variables
    self.is_highest = is_highest
    self.bg_color = bg_color
    self.score = '00000'
  '''Set score'''
  def set(self, score):
    self.score = str(score).zfill(5)
  '''Draw to screen'''
  def draw(self, screen):
    self.image.fill(self.bg_color)
    for idx, digital in enumerate(list(self.score)):
      digital_image = self.images[int(digital)]
      if self.is_highest:
        self.image.blit(digital_image, ((idx+3)*digital_image.get_rect().width, 0))
      else:
        self.image.blit(digital_image, (idx*digital_image.get_rect().width, 0))
    if self.is_highest:
      self.image.blit(self.images[-2], (0, 0))
      self.image.blit(self.images[-1], (digital_image.get_rect().width, 0))
    screen.blit(self.image, self.rect)

The above code uses is_ The highest variable is used to distinguish whether the scoreboard is used to record the highest score of the game or only the current score. The reason for this distinction is that the highest score of the game is preceded by the HI logo, so it takes up more space:

The definition of flying dragon is a little more complicated, because it not only needs to move to the left, but also needs to make the effect of constantly flapping its wings. Specifically, Feilong has two pictures:

What you need to do is to switch the current flying dragon picture every other period of time to achieve the effect of flying dragon flapping its wings:

'''Flying dragon'''
class Ptera(pygame.sprite.Sprite):
  def __init__(self, imagepath, position, size=(46, 40), **kwargs):
    pygame.sprite.Sprite.__init__(self)
    # import picture
    self.images = []
    image = pygame.image.load(imagepath)
    for i in range(2):
      self.images.append(pygame.transform.scale(image.subsurface((i*92, 0), (92, 81)), size))
    self.image_idx = 0
    self.image = self.images[self.image_idx]
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.centery = position
    self.mask = pygame.mask.from_surface(self.image)
    # Define some necessary variables
    self.speed = -10
    self.refresh_rate = 10
    self.refresh_counter = 0
  '''Draw to screen'''
  def draw(self, screen):
    screen.blit(self.image, self.rect)
  '''to update'''
  def update(self):
    if self.refresh_counter % self.refresh_rate == 0:
      self.refresh_counter = 0
      self.image_idx = (self.image_idx + 1) % len(self.images)
      self.loadImage()
    self.rect = self.rect.move([self.speed, 0])
    if self.rect.right < 0:
      self.kill()
    self.refresh_counter += 1
  '''Load pictures in the current state'''
  def loadImage(self):
    self.image = self.images[self.image_idx]
    rect = self.image.get_rect()
    rect.left, rect.top = self.rect.left, self.rect.top
    self.rect = rect
    self.mask = pygame.mask.from_surface(self.image)

Finally, we need to define the little dinosaur class, which is the most complex game wizard class. It has three states: bow, jump and ordinary forward. For the bow:

You just need to switch two pictures with your head down to achieve the effect of small dinosaurs running, just like flying dragons flapping their wings.
The same is true for normal states:

For the jumping state, we can model the upward throwing and free fall motion formulas learned in junior high school, so as to calculate the position of the small dinosaur in the vertical direction. Specifically, the code implementation is as follows:

'''Little dinosaur'''
class Dinosaur(pygame.sprite.Sprite):
  def __init__(self, imagepaths, position=(40, 147), size=[(44, 47), (59, 47)], **kwargs):
    pygame.sprite.Sprite.__init__(self)
    # Import all pictures
    self.images = []
    image = pygame.image.load(imagepaths[0])
    for i in range(5):
      self.images.append(pygame.transform.scale(image.subsurface((i*88, 0), (88, 95)), size[0]))
    image = pygame.image.load(imagepaths[1])
    for i in range(2):
      self.images.append(pygame.transform.scale(image.subsurface((i*118, 0), (118, 95)), size[1]))
    self.image_idx = 0
    self.image = self.images[self.image_idx]
    self.rect = self.image.get_rect()
    self.rect.left, self.rect.bottom = position
    self.mask = pygame.mask.from_surface(self.image)
    # Define some necessary variables
    self.init_position = position
    self.refresh_rate = 5
    self.refresh_counter = 0
    self.speed = 11.5
    self.gravity = 0.6
    self.is_jumping = False
    self.is_ducking = False
    self.is_dead = False
    self.movement = [0, 0]
  '''jump'''
  def jump(self, sounds):
    if self.is_dead or self.is_jumping:
      return
    sounds['jump'].play()
    self.is_jumping = True
    self.movement[1] = -1 * self.speed
  '''Bow your head'''
  def duck(self):
    if self.is_jumping or self.is_dead:
      return
    self.is_ducking = True
  '''Don't bow your head'''
  def unduck(self):
    self.is_ducking = False
  '''dead '''
  def die(self, sounds):
    if self.is_dead:
      return
    sounds['die'].play()
    self.is_dead = True
  '''Draw dinosaurs to the screen'''
  def draw(self, screen):
    screen.blit(self.image, self.rect)
  '''Load pictures in the current state'''
  def loadImage(self):
    self.image = self.images[self.image_idx]
    rect = self.image.get_rect()
    rect.left, rect.top = self.rect.left, self.rect.top
    self.rect = rect
    self.mask = pygame.mask.from_surface(self.image)
  '''Update little dinosaur'''
  def update(self):
    if self.is_dead:
      self.image_idx = 4
      self.loadImage()
      return
    if self.is_jumping:
      self.movement[1] += self.gravity
      self.image_idx = 0
      self.loadImage()
      self.rect = self.rect.move(self.movement)
      if self.rect.bottom >= self.init_position[1]:
        self.rect.bottom = self.init_position[1]
        self.is_jumping = False
    elif self.is_ducking:
      if self.refresh_counter % self.refresh_rate == 0:
        self.refresh_counter = 0
        self.image_idx = 5 if self.image_idx == 6 else 6
        self.loadImage()
    else:
      if self.refresh_counter % self.refresh_rate == 0:
        self.refresh_counter = 0
        if self.image_idx == 1:
          self.image_idx = 2
        elif self.image_idx == 2:
          self.image_idx = 3
        else:
          self.image_idx = 1
        self.loadImage()
    self.refresh_counter += 1

After defining the game wizard class, we can instantiate the TA S:

# Define some necessary elements and variables in the game
score = 0
score_board = Scoreboard(cfg.IMAGE_PATHS['numbers'], position=(534, 15), bg_color=cfg.BACKGROUND_COLOR)
highest_score = highest_score
highest_score_board = Scoreboard(cfg.IMAGE_PATHS['numbers'], position=(435, 15), bg_color=cfg.BACKGROUND_COLOR, is_highest=True)
dino = Dinosaur(cfg.IMAGE_PATHS['dino'])
ground = Ground(cfg.IMAGE_PATHS['ground'], position=(0, cfg.SCREENSIZE[1]))
cloud_sprites_group = pygame.sprite.Group()
cactus_sprites_group = pygame.sprite.Group()
ptera_sprites_group = pygame.sprite.Group()
add_obstacle_timer = 0
score_timer = 0

Then write the main cycle of the game:

# Game main loop
clock = pygame.time.Clock()
while True:
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      pygame.quit()
      sys.exit()
    elif event.type == pygame.KEYDOWN:
      if event.key == pygame.K_SPACE or event.key == pygame.K_UP:
        dino.jump(sounds)
      elif event.key == pygame.K_DOWN:
        dino.duck()
    elif event.type == pygame.KEYUP and event.key == pygame.K_DOWN:
      dino.unduck()
  screen.fill(cfg.BACKGROUND_COLOR)
  # --Add cloud randomly
  if len(cloud_sprites_group) < 5 and random.randrange(0, 300) == 10:
    cloud_sprites_group.add(Cloud(cfg.IMAGE_PATHS['cloud'], position=(cfg.SCREENSIZE[0], random.randrange(30, 75))))
  # --Add cactus / flying dragon randomly
  add_obstacle_timer += 1
  if add_obstacle_timer > random.randrange(50, 150):
    add_obstacle_timer = 0
    random_value = random.randrange(0, 10)
    if random_value >= 5 and random_value <= 7:
      cactus_sprites_group.add(Cactus(cfg.IMAGE_PATHS['cacti']))
    else:
      position_ys = [cfg.SCREENSIZE[1]*0.82, cfg.SCREENSIZE[1]*0.75, cfg.SCREENSIZE[1]*0.60, cfg.SCREENSIZE[1]*0.20]
      ptera_sprites_group.add(Ptera(cfg.IMAGE_PATHS['ptera'], position=(600, random.choice(position_ys))))
  # --Update game elements
  dino.update()
  ground.update()
  cloud_sprites_group.update()
  cactus_sprites_group.update()
  ptera_sprites_group.update()
  score_timer += 1
  if score_timer > (cfg.FPS//12):
    score_timer = 0
    score += 1
    score = min(score, 99999)
    if score > highest_score:
      highest_score = score
    if score % 100 == 0:
      sounds['point'].play()
    if score % 1000 == 0:
      ground.speed -= 1
      for item in cloud_sprites_group:
        item.speed -= 1
      for item in cactus_sprites_group:
        item.speed -= 1
      for item in ptera_sprites_group:
        item.speed -= 1
  # --Collision detection
  for item in cactus_sprites_group:
    if pygame.sprite.collide_mask(dino, item):
      dino.die(sounds)
  for item in ptera_sprites_group:
    if pygame.sprite.collide_mask(dino, item):
      dino.die(sounds)
  # --Draw game elements to the screen
  dino.draw(screen)
  ground.draw(screen)
  cloud_sprites_group.draw(screen)
  cactus_sprites_group.draw(screen)
  ptera_sprites_group.draw(screen)
  score_board.set(score)
  highest_score_board.set(highest_score)
  score_board.draw(screen)
  highest_score_board.draw(screen)
  # --Update screen
  pygame.display.update()
  clock.tick(cfg.FPS)
  # --Is the game over
  if dino.is_dead:
    break

The logic of the main cycle of the game is very simple, that is, we need to check the player's operation for each game screen. If the player presses the space bar or ↑ key, the little dinosaur jumps. If the player presses the ↓ key, the little dinosaur lowers its head, otherwise the little dinosaur rushes forward normally.

Then in the game, we randomly generate game scenes and obstacles such as clouds, flying dragons and cactus, and move to the left at the same speed with the road, so as to realize the visual effect of the little dinosaur moving to the right. In the process of moving, we need to detect the collision between the small dinosaur and the cactus, the small dinosaur and the flying dragon. When the small dinosaur encounters these obstacles, the small dinosaur will die, and the game of this bureau will end.

Note that we should use collide_mask function for more accurate collision detection, rather than the previous collapse_ Rect function:

That is, when the minimum circumscribed rectangles of two targets overlap, collapse_ Rect will judge that the two targets collide, which is obviously unreasonable and will bring poor game experience to players.

In addition, when the score increases by 1000 points, we will increase the speed of the scene and obstacles moving to the left (that is, increase the speed of the little dinosaur moving to the right) as in the original game.

Finally, bind all the current game elements to the screen and update the current screen.

This is the end of the article. Thank you for watching, Python 24 games series , the next article will share the Python Network Security Series

In order to thank readers, I would like to share some of my recent collection of programming dry goods with you and give back to every reader. I hope I can help you.

Dry goods mainly include:

① More than 2000 Python e-books (both mainstream and classic books should be available)

② Python standard library materials (the most complete Chinese version)

③ Project source code (forty or fifty interesting and classic hand training projects and source code)

④ Videos on basic introduction to Python, crawler, web development and big data analysis (suitable for Xiaobai)

⑤ Python learning roadmap (bid farewell to non stream learning)

⑥ Two days of Python crawler training camp live rights

All done ~ get the complete source code by private letter..

Python implementation of remake flappybird games

Python implementation of upgraded tank war games

Python implementation of minesweeping games

Python 2048 games

Python Gobang online game

Python implementation of bomber games

Python implementation of classic bean eating games

Python Xiaole games

Python real dinosaur jump games

Python implementation of simple version of aircraft war games

Tetris games implemented in Python

Python alien invasion games

Python implements "Bunny and Bun" game

Python implementation of eight tone symbol games

Python puzzle games

Python skiing games

Python implements the classic 90 tank war

Python FlappyBird games

Python tower defense games

Python implementation of fruit and gold coin games

Python push box games

Python 24 point games

Python implementation of table tennis games

Python implementation of brick games

Python has implemented maze games

Python implementation of hamster games

Keywords: Python Game Development pygame

Added by blindtoad on Fri, 24 Dec 2021 10:43:44 +0200