This is my fourth day of the August Challenge

preface

Today for you to reconstruct a version of a small dinosaur jump game to share with you, nonsense is not said, let us happily begin ~

Results show

The development tools

Python version: 3.6.4

Related modules:

Pygame module;

And some modules that come with Python.

Environment set up

Install Python and add it to the environment variables. PIP installs the required modules.

Introduction of the principle

Here is the implementation principle of the game, first to explain, in order to facilitate the later training dragon, I did not add the night scene effect, that is, the game only daytime scene.

First, let’s do some necessary initialization of the game:

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

Next, let’s consider what game elements are in a game:

  • Small dinosaur: Controlled by the player to avoid obstacles in the road;
  • Road: the background of the game;
  • Cloud: the background of the game;
  • Flying dragon: one of the obstacles on the road, small dinosaurs will die when they meet;
  • Cactus: one of the obstacles on the road, small dinosaurs will die when they meet;
  • Scoreboard: Records current and all-time high scores.

Let’s define each of these game element classes in turn. For clouds, roads, and cactuses, the definition is simple, we just load the corresponding game element image:

Then write two internal class methods update and draw to make it ok.

The two methods are used to animate the dinosaur moving forward by moving the scene to the left, and to display the scene in the corresponding position on the game interface. Specifically, the code is implemented as follows:

"' floor '
class Ground(pygame.sprite.Sprite) :
  def __init__(self, imagepath, position, **kwargs) :
    pygame.sprite.Sprite.__init__(self)
    # Import image
    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
  "Update 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 the floor to the 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 image
    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 the cloud 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 image
    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)
  "' update '
  def update(self) :
    self.rect = self.rect.move([self.speed, 0])
    if self.rect.right < 0:
      self.kill()
Copy the code

The scoreboard has a similar definition, except it doesn’t need to move, 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 image
    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)
Copy the code

The above code uses the is_highest variable to distinguish whether the scoreboard is used to record the game’s highest score or just the current score. This distinction is made because the game’s highest score has an HI in front of it and therefore takes up more space:

The definition of a flying dragon is a little more complicated, as it not only needs to move to the left, but also needs to create the effect of constantly flapping its wings. Specifically, there are two pictures of the flying dragon:

All you need to do is switch the current dragon image every once in a while to get the effect of the dragon flapping its wings:

"' dragon '
class Ptera(pygame.sprite.Sprite) :
  def __init__(self, imagepath, position, size=(46.40), **kwargs) :
    pygame.sprite.Sprite.__init__(self)
    # Import image
    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)
  "' 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 the image in its 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)
Copy the code

Finally, we need to define the little dinosaur class, which is one of the most complex game sprites. It has three states: head down, jump, and normal forward. For the bow:

All you have to do is switch between the two pictures with your head down like a flying dragon flapping its wings to achieve the effect of the baby dinosaur running. The same is true for normal states:

For the jumping state, we can model the upward and free-fall motion formulas of middle and high school, so as to calculate the vertical position of the baby dinosaur. Specifically, the code is implemented 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 images
    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
  "' down ' ' '
  def duck(self) :
    if self.is_jumping or self.is_dead:
      return
    self.is_ducking = True
  Do not bow.
  def unduck(self) :
    self.is_ducking = False
  "" is dead" "
  def die(self, sounds) :
    if self.is_dead:
      return
    sounds['die'].play()
    self.is_dead = True
  Drawing a dinosaur to the screen
  def draw(self, screen) :
    screen.blit(self.image, self.rect)
  Load the image in its 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
Copy the code

Having defined our game Sprite classes, we can instantiate them:

Define some of the elements and variables necessary for 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
Copy the code

Then write the main loop:

# 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 a random cloud
  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))))
  # -- Randomly add cactus/dragon
  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 onto 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
Copy the code

The logic of the main loop is simple. Every frame of the game, we need to check what the player is doing. If the player presses the spacebar or ↑, the baby dinosaur jumps, if the player presses the ↓, the baby dinosaur heads down, otherwise the baby dinosaur runs forward.

Then in the game, we randomly generate game scenes and obstacles such as clouds, dragons and cactus, and move them to the left at the same speed as the road to achieve the visual effect of the baby dinosaur moving to the right. In the process of moving, we need to conduct collision detection between the baby dinosaur and the cactus, the baby dinosaur and the flying dragon. When the baby dinosaur touches these obstacles, the baby dinosaur will die, and the game will be over.

Note that we should use the collide_mask function for more accurate collision detection, instead of the previous collide_rect function:

Collide_rect determines that the two objects are colliding when the minimal external rectangles of the two objects overlap, which is obviously not reasonable and will result in a poor experience for the player.

In addition, for every thousand points we increase the score, we increase the speed at which scenes and obstacles move to the left (which increases the speed at which little dinosaurs move to the right) just like in the original.

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

That’s the end of this article. Thanks for watching Python24 mini-games. The next article will share the Python Network security series

To thank the readers, I’d like to share some of my recent programming gems to give back to each and every one of you.

Dry goods are mainly:

① More than 2000 Python ebooks (both mainstream and classic)

Python Standard Library (Chinese version)

(3) project source code (forty or fifty interesting and classic practice projects and source code)

④Python basic introduction, crawler, Web development, big data analysis videos (suitable for small white learning)

⑤Python Learning Roadmap

⑥ Two days of Python crawler boot camp live permissions

All done~ see personal home profile or private message for complete source code.

Python implementation of the redo version of FlappyBird small game

Python to achieve the upgraded version of tank war games

Python minesweeper small game

Python 2048 small games

Python to achieve gobang online games

Python to achieve bomb people small game

Python to achieve classic pac-man games

Python to achieve elimination of music games

Python real dinosaur jump a jump small game is

Python simple version of the aircraft war games

Python tetris small game implementation

Python alien invasion to achieve small games

Python to achieve “little rabbit and Bun” game

Python eight notes to achieve small games

Python to achieve the puzzle game

Python to achieve ski games

Python for classic 90 tank wars

Python FlappyBird implementation of small games

Python to achieve tower defense games

Python to achieve fruit and gold coins small games

Python to achieve a small game push box

Python to achieve 24 small games

Python to achieve table tennis games

Python brick to achieve small games

Python to achieve a maze of small games

Python to achieve whack-a-mole games