Learning notes of 2D game development course - pygame small game development

Keywords: Programming Pycharm

Introduction to the game

Use pycharm to develop the little game "ow big cat run". Is a relatively simple cool run game, only 5 levels.

Introduction to play

Press the space bar control can let the cat jump, to avoid bullets and dragons at the same time. There will be a high score list after the end.

Game Preview

Game development process

1, Material preparation

2, Programming

1. Initialize the game and set the game start interface

pygame.init()
audio_init()
screen = pygame.display.set_mode((800,600),0,32)
pygame.display.set_caption("Ow, meow, run!")
font = pygame.font.Font(None, 22)
font1 = pygame.font.Font(None, 40)
framerate = pygame.time.Clock()
upImageFilename = 'game_start_up.png'
downImageFilename = 'game_start_down.png'
#Create button object
button = Button(upImageFilename,downImageFilename, (400,500))
interface = pygame.image.load("first.png")

2. In the game, there are several kinds of elves: oudamiao, dragon, fire, explosion animation and fruit. These sprites are all instantiated objects of MySprite class. We integrated these common classes and modules into MyLibrary.py.

The code of MyLibrary.py is as follows:

# MyLibrary.py

import sys, time, random, math, pygame
from pygame.locals import *


# prints text using the supplied font
def print_text(font, x, y, text, color=(255,255,255)):
    imgText = font.render(text, True, color)
    screen = pygame.display.get_surface() #req'd when function moved into MyLibrary
    screen.blit(imgText, (x,y))

# MySprite class extends pygame.sprite.Sprite
class MySprite(pygame.sprite.Sprite):
    
    def __init__(self):
        pygame.sprite.Sprite.__init__(self) #extend the base Sprite class
        self.master_image = None
        self.frame = 0
        self.old_frame = -1
        self.frame_width = 1
        self.frame_height = 1
        self.first_frame = 0
        self.last_frame = 0
        self.columns = 1
        self.last_time = 0
        self.direction = 0
        self.velocity = Point(0.0,0.0) 

    #X property
    def _getx(self): return self.rect.x
    def _setx(self,value): self.rect.x = value
    X = property(_getx,_setx)

    #Y property
    def _gety(self): return self.rect.y
    def _sety(self,value): self.rect.y = value
    Y = property(_gety,_sety)

    #position property
    def _getpos(self): return self.rect.topleft
    def _setpos(self,pos): self.rect.topleft = pos
    position = property(_getpos,_setpos)
        

    def load(self, filename, width, height, columns):
        self.master_image = pygame.image.load(filename).convert_alpha()
        self.frame_width = width
        self.frame_height = height
        self.rect = Rect(0,0,width,height)
        self.columns = columns
        #try to auto-calculate total frames
        rect = self.master_image.get_rect()
        self.last_frame = (rect.width // width) * (rect.height // height) - 1

    def update(self, current_time, rate=30):
        #update animation frame number
        if current_time > self.last_time + rate:
            self.frame += 1
            if self.frame > self.last_frame:
                self.frame = self.first_frame
            self.last_time = current_time

        #build current frame only if it changed
        if self.frame != self.old_frame:
            frame_x = (self.frame % self.columns) * self.frame_width
            frame_y = (self.frame // self.columns) * self.frame_height
            rect = Rect(frame_x, frame_y, self.frame_width, self.frame_height)
            self.image = self.master_image.subsurface(rect)
            self.old_frame = self.frame

    def __str__(self):
        return str(self.frame) + "," + str(self.first_frame) + \
               "," + str(self.last_frame) + "," + str(self.frame_width) + \
               "," + str(self.frame_height) + "," + str(self.columns) + \
               "," + str(self.rect)

#Point class
class Point(object):
    def __init__(self, x, y):
        self.__x = x
        self.__y = y

    #X property
    def getx(self): return self.__x
    def setx(self, x): self.__x = x
    x = property(getx, setx)

    #Y property
    def gety(self): return self.__y
    def sety(self, y): self.__y = y
    y = property(gety, sety)

    def __str__(self):
        return "{X:" + "{:.0f}".format(self.__x) + \
            ",Y:" + "{:.0f}".format(self.__y) + "}"
    

Create several sprite groups and write some sprites into them

#Create a sprite group
group = pygame.sprite.Group()
group_exp = pygame.sprite.Group()
group_fruit = pygame.sprite.Group()
#Create monster spirit
dragon = MySprite()
dragon.load("dragon.png", 260, 150, 3)
dragon.position = 100, 230
group.add(dragon)

#Create explosion animation
explosion = MySprite()
explosion.load("explosion.png",128,128,6)
#Create player Wizard
player = MySprite()
player.load("sprite.png", 100, 100, 4)
player.position = 400, 270
group.add(player)

#Create bullet Genie
arrow = MySprite()
arrow.load("flame.png", 40, 16, 1)
arrow.position = 800,320
group.add(arrow)

3. Define a rolling map class

class MyMap(pygame.sprite.Sprite):
    
    def __init__(self,x,y):
        self.x = x
        self.y = y
        self.bg = pygame.image.load("background.png").convert_alpha()
    def map_rolling(self):
        if self.x < -300:
            self.x = 300
        else:
            self.x -=5
    def map_update(self):
        screen.blit(self.bg, (self.x,self.y))
    def set_pos(x,y):
        self.x =x
        self.y =y

Create map objects

bg1 = MyMap(0,0)
bg2 = MyMap(300,0)

The way to call update and rolling in the program can make the map scroll up indefinitely.

 bg1.map_update()
 bg2.map_update()
 bg1.map_rolling()
 bg2.map_rolling()

4. Define a button class. When the mouse passes by, it will become gray.

Play the pressed sound and background music of the game when the button is pressed

class Button(object):
    def __init__(self, upimage, downimage,position):
        self.imageUp = pygame.image.load(upimage).convert_alpha()
        self.imageDown = pygame.image.load(downimage).convert_alpha()
        self.position = position
        self.game_start = False
        
    def isOver(self):
        point_x,point_y = pygame.mouse.get_pos()
        x, y = self. position
        w, h = self.imageUp.get_size()

        in_x = x - w/2 < point_x < x + w/2
        in_y = y - h/2 < point_y < y + h/2
        return in_x and in_y

    def render(self):
        w, h = self.imageUp.get_size()
        x, y = self.position
        
        if self.isOver():
            screen.blit(self.imageDown, (x-w/2,y-h/2))
        else:
            screen.blit(self.imageUp, (x-w/2, y-h/2))
    def is_start(self):
        if self.isOver():
            b1,b2,b3 = pygame.mouse.get_pressed()
            if b1 == 1:
                self.game_start = True
                bg_sound.play_pause()
                btn_sound.play_sound()
                bg_sound.play_sound()

def replay_music():
    bg_sound.play_pause()
    bg_sound.play_sound()

5. Define a class for controlling sound and a method for initial audio

def audio_init():#Initialize audio
    global hit_au,btn_au,bg_au,bullent_au
    pygame.mixer.init()
    hit_au = pygame.mixer.Sound("exlposion.wav")
    btn_au = pygame.mixer.Sound("button.wav")
    bg_au = pygame.mixer.Sound("background.ogg")
    bullent_au = pygame.mixer.Sound("bullet.wav")
class Music():#Control the playback and pause of sound
    def __init__(self,sound):
        self.channel = None
        self.sound = sound     
    def play_sound(self):
        self.channel = pygame.mixer.find_channel(True)
        self.channel.set_volume(0.5)
        self.channel.play(self.sound)
    def play_pause(self):
        self.channel.set_volume(0.0)
        self.channel.play(self.sound)

6. Write the function to reset the bullet and issue the bullet continuously

Update bullet

if not game_over:
   arrow.X -= arrow_vel
if arrow.X < -40: reset_arrow()

Reset bullet function

def reset_arrow():
    y = random.randint(270,350)
    arrow.position = 800,y
    bullent_sound.play_sound()

7. In order to move the fruits, you need to traverse the fruits in group fruit, and then move them in turn. It is also necessary to judge that the player eats the fruit, the fruit disappears and the player's points increase.

#Traverse the fruit to move it
            for e in group_fruit:
                e.X -=5
            collide_list = pygame.sprite.spritecollide(player,group_fruit,True)
            score +=len(collide_list)

8. Define some variables and write the game's loop system

arrow_vel = 10.0
game_over = False
you_win = False
player_jumping = False
jump_vel = 0.0
player_start_y = player.Y
player_hit = False
monster_hit = False
p_first = True
m_first = True
best_score = 0
global bg_sound,hit_sound,btn_sound,bullent_sound
bg_sound=Music(bg_au)
hit_sound=Music(hit_au)
btn_sound=Music(btn_au)
bullent_sound =Music(bullent_au)
game_round = {1:'ROUND ONE',2:'ROUND TWO',3:'ROUND THREE',4:'ROUND FOUR',5:'ROUND FIVE'}
game_pause = True
index =0
current_time = 0
start_time = 0
music_time = 0
score =0
replay_flag = True
#loop
bg_sound.play_sound()
best_score = data_read()
while True:
    framerate.tick(60)
    ticks = pygame.time.get_ticks()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    keys = pygame.key.get_pressed()
    if keys[K_ESCAPE]:
        pygame.quit()
        sys.exit()
        
    elif keys[K_SPACE]:
        if not player_jumping:
            player_jumping = True
            jump_vel = -12.0
            
    screen.blit(interface,(0,0))
    button.render()
    button.is_start()
    if button.game_start == True:
        if game_pause :
            index +=1
            tmp_x =0
            if score >int (best_score):
                best_score = score
            fd_2 = open("data.txt","w+")
            fd_2.write(str(best_score))
            fd_2.close()
            

9. Write game detection, mainly as follows:

(1) Whether the player enters the next level

(2) Collision detection: whether the bullet hits the player, whether the bullet hits the monster, whether the player is chased by the monster, and whether the player is jumping

           #Update sprite group
            if not game_over:
                group.update(ticks, 60)
                group_exp.update(ticks,60)
                group_fruit.update(ticks,60)
            #Loop background music
            music_time = time.clock()
            if music_time   > 150 and replay_flag:
                replay_music()
                replay_flag =False
            #Draw sprite group
            group.draw(screen)
            group_fruit.draw(screen)
            if player_hit or monster_hit:
                group_exp.draw(screen)
            print_text(font, 330, 560, "press SPACE to jump up!")
            print_text(font, 200, 20, "You have get Score:",(219,224,22))
            print_text(font1, 380, 10, str(score),(255,0,0))
            if game_over:
                start_time = time.clock()
                current_time =time.clock()-start_time
                while current_time<5:
                    screen.fill((200, 200, 200))
                    print_text(font1, 300, 150,"GAME OVER!",(240,20,20))
                    current_time =time.clock()-start_time
                    print_text(font1, 320, 250, "Best Score:",(120,224,22))
                    if score >int (best_score):
                        best_score = score
                    print_text(font1, 370, 290, str(best_score),(255,0,0))
                    print_text(font1, 270, 330, "This Game Score:",(120,224,22))
                    print_text(font1, 370, 380, str(score),(255,0,0))
                    pygame.display.update()
                fd_2 = open("data.txt","w+")
                fd_2.write(str(best_score))
                fd_2.close()
                pygame.quit()
                sys.exit()
 

The complete code of the game is as follows:

# -*- coding: utf-8 -*-
import sys, time, random, math, pygame,locale
from pygame.locals import *
from MyLibrary import *

#Reset bullet function
def reset_arrow():
    y = random.randint(270,350)
    arrow.position = 800,y
    bullent_sound.play_sound()

#Define a rolling map class
class MyMap(pygame.sprite.Sprite):

    def __init__(self,x,y):
        self.x = x
        self.y = y
        self.bg = pygame.image.load("background.png").convert_alpha()
    def map_rolling(self):
        if self.x < -300:
            self.x = 300
        else:
            self.x -=5
    def map_update(self):
        screen.blit(self.bg, (self.x,self.y))
    def set_pos(x,y):
        self.x =x
        self.y =y
#Define a button class
class Button(object):
    def __init__(self, upimage, downimage,position):
        self.imageUp = pygame.image.load(upimage).convert_alpha()
        self.imageDown = pygame.image.load(downimage).convert_alpha()
        self.position = position
        self.game_start = False
        
    def isOver(self):
        point_x,point_y = pygame.mouse.get_pos()
        x, y = self. position
        w, h = self.imageUp.get_size()

        in_x = x - w/2 < point_x < x + w/2
        in_y = y - h/2 < point_y < y + h/2
        return in_x and in_y

    def render(self):
        w, h = self.imageUp.get_size()
        x, y = self.position
        
        if self.isOver():
            screen.blit(self.imageDown, (x-w/2,y-h/2))
        else:
            screen.blit(self.imageUp, (x-w/2, y-h/2))
    def is_start(self):
        if self.isOver():
            b1,b2,b3 = pygame.mouse.get_pressed()
            if b1 == 1:
                self.game_start = True
                bg_sound.play_pause()
                btn_sound.play_sound()
                bg_sound.play_sound()

def replay_music():
    bg_sound.play_pause()
    bg_sound.play_sound()

#Define a method of data IO
def data_read():
    fd_1 = open("data.txt","r")
    best_score = fd_1.read()
    fd_1.close()
    return best_score

   
#Define a class for controlling sound and a method for initial audio
def audio_init():
    global hit_au,btn_au,bg_au,bullent_au
    pygame.mixer.init()
    hit_au = pygame.mixer.Sound("exlposion.wav")
    btn_au = pygame.mixer.Sound("button.wav")
    bg_au = pygame.mixer.Sound("background.ogg")
    bullent_au = pygame.mixer.Sound("bullet.wav")
class Music():
    def __init__(self,sound):
        self.channel = None
        self.sound = sound     
    def play_sound(self):
        self.channel = pygame.mixer.find_channel(True)
        self.channel.set_volume(0.5)
        self.channel.play(self.sound)
    def play_pause(self):
        self.channel.set_volume(0.0)
        self.channel.play(self.sound)
      
#Main program part
pygame.init()
audio_init()
screen = pygame.display.set_mode((800,600),0,32)
pygame.display.set_caption("Ow, meow, run!")
font = pygame.font.Font(None, 22)
font1 = pygame.font.Font(None, 40)
framerate = pygame.time.Clock()
upImageFilename = 'game_start_up.png'
downImageFilename = 'game_start_down.png'
#Create button object
button = Button(upImageFilename,downImageFilename, (400,500))
interface = pygame.image.load("first.png")

#Create map objects
bg1 = MyMap(0,0)
bg2 = MyMap(300,0)
#Create a sprite group
group = pygame.sprite.Group()
group_exp = pygame.sprite.Group()
group_fruit = pygame.sprite.Group()
#Create monster spirit
dragon = MySprite()
dragon.load("dragon.png", 260, 150, 3)
dragon.position = 100, 230
group.add(dragon)

#Create explosion animation
explosion = MySprite()
explosion.load("explosion.png",128,128,6)
#Create player Wizard
player = MySprite()
player.load("sprite.png", 100, 100, 4)
player.position = 400, 270
group.add(player)

#Create bullet Genie
arrow = MySprite()
arrow.load("flame.png", 40, 16, 1)
arrow.position = 800,320
group.add(arrow)



#Define some variables
arrow_vel = 10.0
game_over = False
you_win = False
player_jumping = False
jump_vel = 0.0
player_start_y = player.Y
player_hit = False
monster_hit = False
p_first = True
m_first = True
best_score = 0
global bg_sound,hit_sound,btn_sound,bullent_sound
bg_sound=Music(bg_au)
hit_sound=Music(hit_au)
btn_sound=Music(btn_au)
bullent_sound =Music(bullent_au)
game_round = {1:'ROUND ONE',2:'ROUND TWO',3:'ROUND THREE',4:'ROUND FOUR',5:'ROUND FIVE'}
game_pause = True
index =0
current_time = 0
start_time = 0
music_time = 0
score =0
replay_flag = True
#loop
bg_sound.play_sound()
best_score = data_read()
while True:
    framerate.tick(60)
    ticks = pygame.time.get_ticks()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
    keys = pygame.key.get_pressed()
    if keys[K_ESCAPE]:
        pygame.quit()
        sys.exit()
        
    elif keys[K_SPACE]:
        if not player_jumping:
            player_jumping = True
            jump_vel = -12.0
            
    screen.blit(interface,(0,0))
    button.render()
    button.is_start()
    if button.game_start == True:
        if game_pause :
            index +=1
            tmp_x =0
            if score >int (best_score):
                best_score = score
            fd_2 = open("data.txt","w+")
            fd_2.write(str(best_score))
            fd_2.close()
            #Judge whether the game is cleared
            if index == 6:
                you_win = True
            if you_win:
                start_time = time.clock()
                current_time =time.clock()-start_time
                while current_time<5:
                    screen.fill((200, 200, 200))
                    print_text(font1, 270, 150,"YOU WIN THE GAME!",(240,20,20))
                    current_time =time.clock()-start_time
                    print_text(font1, 320, 250, "Best Score:",(120,224,22))
                    print_text(font1, 370, 290, str(best_score),(255,0,0))
                    print_text(font1, 270, 330, "This Game Score:",(120,224,22))
                    print_text(font1, 385, 380, str(score),(255,0,0))
                    pygame.display.update()
                pygame.quit()
                sys.exit()
                
            for i in range(0,100):
                element = MySprite()
                element.load("fruit.png", 75, 20, 1)
                tmp_x +=random.randint(50,120)
                element.X = tmp_x+300
                element.Y = random.randint(80,200)
                group_fruit.add(element)
            start_time = time.clock()
            current_time =time.clock()-start_time
            while current_time<3:
                screen.fill((200, 200, 200))
                print_text(font1, 320, 250,game_round[index],(240,20,20))
                pygame.display.update()
                game_pause = False
                current_time =time.clock()-start_time
            
        else:
            #Update bullet
            if not game_over:
                arrow.X -= arrow_vel
            if arrow.X < -40: reset_arrow()
            #Collision detection, whether the bullet hits the player
            if pygame.sprite.collide_rect(arrow, player):
                reset_arrow()
                explosion.position =player.X,player.Y
                player_hit = True
                hit_sound.play_sound()
                if p_first:
                    group_exp.add(explosion)
                    p_first = False
                player.X -= 10

            #Collision detection, whether the bullet hit the monster
            if pygame.sprite.collide_rect(arrow, dragon):
                reset_arrow()
                explosion.position =dragon.X+50,dragon.Y+50
                monster_hit = True
                hit_sound.play_sound()
                if m_first:
                    group_exp.add(explosion)
                    m_first = False
                dragon.X -= 10

            #Collision detection, whether players are chased by monsters
            if pygame.sprite.collide_rect(player, dragon):
                game_over = True
            #Traverse the fruit to move it
            for e in group_fruit:
                e.X -=5
            collide_list = pygame.sprite.spritecollide(player,group_fruit,True)
            score +=len(collide_list)
            #Pass the gate or not
            if dragon.X < -100:
                game_pause = True
                reset_arrow()
                player.X = 400
                dragon.X = 100
                
            

            #Check whether the player is in jumping state
            if player_jumping:
                if jump_vel <0:
                    jump_vel += 0.6
                elif jump_vel >= 0:
                    jump_vel += 0.8
                player.Y += jump_vel
                if player.Y > player_start_y:
                    player_jumping = False
                    player.Y = player_start_y
                    jump_vel = 0.0


            #Drawing background
            bg1.map_update()
            bg2.map_update()
            bg1.map_rolling()
            bg2.map_rolling()
            
            #Update sprite group
            if not game_over:
                group.update(ticks, 60)
                group_exp.update(ticks,60)
                group_fruit.update(ticks,60)
            #Loop background music
            music_time = time.clock()
            if music_time   > 150 and replay_flag:
                replay_music()
                replay_flag =False
            #Draw sprite group
            group.draw(screen)
            group_fruit.draw(screen)
            if player_hit or monster_hit:
                group_exp.draw(screen)
            print_text(font, 330, 560, "press SPACE to jump up!")
            print_text(font, 200, 20, "You have get Score:",(219,224,22))
            print_text(font1, 380, 10, str(score),(255,0,0))
            if game_over:
                start_time = time.clock()
                current_time =time.clock()-start_time
                while current_time<5:
                    screen.fill((200, 200, 200))
                    print_text(font1, 300, 150,"GAME OVER!",(240,20,20))
                    current_time =time.clock()-start_time
                    print_text(font1, 320, 250, "Best Score:",(120,224,22))
                    if score >int (best_score):
                        best_score = score
                    print_text(font1, 370, 290, str(best_score),(255,0,0))
                    print_text(font1, 270, 330, "This Game Score:",(120,224,22))
                    print_text(font1, 370, 380, str(score),(255,0,0))
                    pygame.display.update()
                fd_2 = open("data.txt","w+")
                fd_2.write(str(best_score))
                fd_2.close()
                pygame.quit()
                sys.exit()
    pygame.display.update()

Posted by Maharg105 on Mon, 06 Apr 2020 08:38:44 -0700