【pygame游戏】

这篇博客详细介绍了使用Python的pygame库开发一款名为'Alien Invasion'的游戏。游戏包含初始化设置、屏幕管理、游戏循环、事件处理、对象更新、碰撞检测等功能。通过游戏主程序、游戏功能函数、设置类、飞船类、子弹类和外星人类等模块,展示了游戏从创建到运行的完整流程。此外,还包括了得分系统、游戏状态管理和用户交互设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

pygame游戏

pygame基本结构:

def run_game():

    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    # 每个元素都是surface,screen为整个游戏窗口的surface
    screen = pygame.display.set_mode((1200, 800))
    pygame.display.set_caption("Alien Invasion")
    
    # 游戏主循环
    running = True
    while running == True:
        
        # 监视键鼠事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                break  # 直接跳出for循环
            
            pass  # 其他事件  
                
        if running == False:break  # 直接跳出while循环
            
        pass
        pass
                
        # 让最近绘制的屏幕可见
        pygame.display.flip()  # 让背后的图像快速复制到屏幕
    # 跳出mainloop后
    pygame.quit()  # Call the quit() method outside the while loop to end the application
#     sys.exit()  # 结束程序? 会报错?
        
run_game()

alien_invasion.py

游戏主程序,while在此


"""

"""

# import sys

import pygame
from pygame.sprite import Group

from settings import Settings
from ship import Ship
from game_stats import GameStats
from button import Button, ScoreBoard
# 这让你能够了解实际的开发过程:一开始将代码编写得尽可能简单,并在项目越来越复杂时进行重构
import game_functions as gf


def run_game():

    # 初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    stats = GameStats(ai_settings)
    # 每个元素都是surface,screen为整个游戏窗口的surface
    screen = pygame.display.set_mode((
        ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")
    play_button = Button(ai_settings, screen, "Play")
    sb = ScoreBoard(ai_settings, screen, stats)
    ship = Ship(ai_settings, screen)
    bullets = Group()
    aliens = Group()
    gf.create_fleet(ai_settings, screen, ship, aliens)
    # 游戏主循环
    while ai_settings.while_loop:
        
        # 监视键鼠事件
        gf.check_events(ai_settings, stats, sb, screen, ship, aliens, bullets, 
                        play_button)
        if ai_settings.while_loop == False:break  # 直接跳出while循环直接
        # gf.update_screen(ai_settings, stats, sb, screen, ship, aliens, bullets,
        #                  play_button)
        if stats.game_active:
            # 更新飞船参数
            ship.update()
            # 更新子弹参数
            gf.update_bullets(ai_settings, stats, sb, screen, ship,
                              aliens, bullets)
        gf.update_screen(ai_settings, stats, sb, screen, ship, aliens, bullets,
                         play_button)
            # 我们在更新子弹后再更新外星人的位置,因为稍后要检查是否有子弹撞到了外星
        if stats.game_active:
            gf.update_aliens(ai_settings, stats, sb, screen, ship, aliens,
                             bullets)
        
        # 绘制后台屏幕并将其复制到前台显示
        # gf.update_screen(ai_settings, stats, sb, screen, ship, aliens, bullets,
        #                  play_button)
        
    # 跳出mainloop后
    pygame.quit()  # Call the quit() method outside the while loop to end the application
#     sys.exit()  # 结束程序? 会报错?
        
run_game()

game_function.py

游戏运行所需函数,游戏的大部分功能都在此实现

# -*- coding: utf-8 -*-
"""
Created on Fri Dec 31 20:53:37 2021

@author: NO ONE

对实例、函数进行更改,并非类
分三步:属性值改变、根据属性值绘制到后台屏幕、pygame.display.flip()

update_attri
prep_pic
update_screen()
"""

import pygame
from random import randint, choice
from time import sleep

from bullet import Bullet
from alien import Alien


#——————————————————————————————————————————————————————————————————————————————
def fire_bullet(ai_settings, screen, ship, bullets):
    """重构代码块,以便程序现得简洁易懂"""
    if len(bullets) < ai_settings.bullets_allowed:
        # 创建一颗子弹并将其加入到编组bullets中
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)

def check_keydown_events(event, ai_settings, stats, sb, screen, ship, aliens,
                         bullets):
    # print('event.type: ', event.type)
    # print('event.key: ',event.key)
    # print('pygame.QUIT ', pygame.QUIT)
    # print('event.key is equal to 97(a): ', event.key == 97)
    # print('pygame.K_UP: ', pygame.K_UP)
    if event.key == pygame.K_RIGHT:
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        ship.moving_left = True
    # 用elif,同时按下时,排在前面的有优先级
    elif event.key == pygame.K_UP:
        ship.moving_up = True
    elif event.key == pygame.K_DOWN:
        ship.moving_down = True
    elif event.key == pygame.K_SPACE:
        # and len(bullets) < ai_settings.bullets_allowed:
        fire_bullet(ai_settings, screen, ship, bullets) 
    elif event.key == pygame.K_q:
        ai_settings.while_loop = False 
    elif event.key == pygame.K_d:
        ai_settings.rand_kill_alien = True
    elif event.key == pygame.K_p:
        start_game(ai_settings, stats, sb, screen, ship, aliens, bullets)

def check_keyup_events(event, ship):
    if event.key == pygame.K_RIGHT:
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        ship.moving_left = False
    elif event.key == pygame.K_UP:
        ship.moving_up = False
    elif event.key == pygame.K_DOWN:
        ship.moving_down = False
             
def check_events(ai_settings, stats, sb, screen, ship, aliens, bullets,
                 play_button):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        # event事件
        # event.type事件类型
        # event.key 触发事件的是哪个键
        # 伪经验:一般建议用elif,除非必须用if
        if event.type == pygame.QUIT:
            ai_settings.while_loop = False
        # mouse click
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if stats.game_active == False:
                mouse_x, mouse_y = pygame.mouse.get_pos()
                check_play_button(ai_settings, stats, sb, screen, ship, aliens,
                                  bullets, play_button, mouse_x, mouse_y)
        # Pygame中不区分大小写,即,大小写返回值相同
        # Pygame的固定用法,必须先event.type再event.key
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ai_settings, stats, sb, screen, ship,
                                 aliens, bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)
            
def start_game(ai_settings, stats, sb, screen, ship, aliens, bullets):
    ai_settings.initialize_dynamic_settings()
    stats.game_active = True
    stats.reset_stats()
    # --重置记分牌图案--
    sb.prep_score()
    sb.prep_level()
    sb.prep_ships()
    # --            --
    pygame.mouse.set_visible(False)
    ship.center_ship()
    bullets.empty()
    aliens.empty()
    create_fleet(ai_settings, screen, ship, aliens)
            
def check_play_button(ai_settings, stats, sb, screen, ship, aliens, bullets,
                      play_button, mouse_x, mouse_y):
    if play_button.rect.collidepoint(mouse_x, mouse_y):
        # # 多+了个1,在response_ship_hit函数会-掉多+的1,并完成aliens,bullets,ship的
        # # 重置工作;点击play后,画面sleep(0.5)后刷新
        # # 如果要添加 按下<P>开始游戏就必须对该代码进行重构
        # stats.ships_left += 1  # 参见response_ship_hit函数
        start_game(ai_settings, stats, sb, screen, ship, aliens, bullets)
        
#——————————————————————————————————————————————————————————————————————————————
def update_screen(ai_settings, stats, sb, screen, ship, aliens, bullets, 
                  play_button):
    """绘制后台的屏幕,并将其复制到前台"""
    screen.fill(ai_settings.bg_color)
    # bullets.draw_bullet() 思考以下为什么无此方法
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    for alien in aliens.sprites():
        alien.blitme()
    ship.blitme()
    sb.show_score()
    if not stats.game_active:
        play_button.draw_button()
    
    pygame.display.flip()
    
def update_bullets(ai_settings, stats, sb, screen, ship, aliens, bullets):
    """更新子弹数据,本质是更新Bullet类的attri
    当外星人数为0是,清空子弹、重置外星人"""
    bullets.update()
    # 删除已消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom < 0:
            bullets.remove(bullet)  # 这样编感觉很巧妙
    # 如果你留下这条语句,游戏的速度将大大降低,
    # 因为将输出写入到终端而花费的时间比将图形绘制到游戏窗口花费的时间还多
    # print(len(bullets))
    #
    check_bullet_alien_collisions(ai_settings, stats, sb, screen, ship,
                                  aliens, bullets)
    
def check_high_score(stats, sb):
    if stats.score > stats.high_score:
        stats.high_score = stats.score
        sb.prep_high_score()
        
def check_bullet_alien_collisions(ai_settings, stats, sb, screen, ship,
                                  aliens, bullets):
    """"响应子弹和外星人的碰撞,当外星人数为0是,清空子弹、重置外星人"""
    # 历编组bullets 中的每颗子弹,再遍历编组aliens 中的每个外星人
    # 每当有子弹和外星人的rect 重叠时,groupcollide()就在它返回的字典中添加一个键-值对
    # dict{发生了碰撞的子弹1:list[g1,g2,...],子弹2:lst[...],...}
    collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)
    if collisions:
        for alien_lst in collisions.values():
            stats.score += ai_settings.alien_points*len(alien_lst)
            sb.prep_score()
        check_high_score(stats, sb)
    elif len(aliens) == 0:  # 这里用elif有讲究,如果改成if
        # 删除现有的所有子弹,并创建一个新的外星人群
        bullets.empty()
        create_fleet(ai_settings, screen, ship, aliens)
        ai_settings.increase_speed()
        stats.level += 1
        sb.prep_level()
    
def create_fleet(ai_settings, screen, ship, aliens):
    """create fleet本函数需要重构,以使代码显得简洁、易懂"""
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    num_aliens_x = int(ai_settings.screen_width/(2*alien_width))
    alien_height = alien.rect.height
    screen_sapce_y = ai_settings.screen_height - ship.rect.height \
        - 2*alien_height
    num_aliens_y = int(screen_sapce_y/(2*alien_height))
    for alien_row in range(num_aliens_y):
        for alien_num in range(num_aliens_x-1): # 这里空出一个位置
            alien = Alien(ai_settings, screen)
            alien.x = 2*alien_width*alien_num + alien_width + randint(-40,40)
            alien.rect.x = alien.x
            alien.y = alien_height*(2*alien_row + 1) + randint(-20,20)
            alien.rect.y = alien.y
            aliens.add(alien)         
#——————————————————————————————————————————————————————————————————————————————    
def response_ship_hit(ai_settings, stats, sb, screen, ship, aliens, bullets):
    """飞船数-1,清空,重置"""
    if stats.ships_left > 0:
        # 飞船剩余-1
        stats.ships_left -= 1
        sb.prep_ships()
        # 清空外星人和子弹
        aliens.empty()
        bullets.empty()
        #
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()
        # 暂停
        sleep(0.5)
    else:
        stats.game_active = False
        pygame.mouse.set_visible(True)

def check_fleet_edges(ai_settings, aliens):
    """"有外星人到达边缘时采取相应的措施"""
    for alien in aliens.sprites():
        if alien.check_edges():
            change_fleet_direction(ai_settings, aliens)
            break
    
def change_fleet_direction(ai_settings, aliens):
    """改变实例的移动方向"""
    for alien in aliens.sprites():
        alien.rect.y += ai_settings.drop_speed_factor
    ai_settings.fleet_direction *= -1
    
def random_kill_alien(ai_settings, aliens):
    """根据rand_kill_alien判断,是否从aliens中随机移除"""
    if ai_settings.rand_kill_alien:
        # 随机移除 random choice
        aliens.remove(choice(aliens.sprites()))
        ai_settings.rand_kill_alien = False    
            
def update_aliens(ai_settings, stats, sb, screen, ship, aliens, bullets):
    """更新外星人,本质是更新属性"""
    random_kill_alien(ai_settings, aliens)
    check_fleet_edges(ai_settings, aliens)
    aliens.update()  # Group类将自动对每个Alien类调用update()方法
    #
    if pygame.sprite.spritecollideany(ship, aliens):
        response_ship_hit(ai_settings, stats, sb, screen, ship, aliens, bullets)
    # 检查是否有飞船到底
    for alien in reversed(aliens.sprites()):
        if alien.rect.bottom >= screen.get_rect().bottom:
            response_ship_hit(ai_settings, stats, sb, screen, ship, aliens,bullets)
            break
#——————————————————————————————————————————————————————————————————————————————


settings

游戏的所有设置都在这里

# -*- coding: utf-8 -*-
"""
Created on Fri Dec 31 19:10:16 2021

@author: NO ONE
"""

class Settings():
    """存储《外星人入侵》的所有设置的类"""
    
    def __init__(self):
        self.while_loop = True
        # 屏幕及飞船速度设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230, 230, 230)
        self.ship_limit = 3
        # 子弹设置
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        # 外星人设置
        self.drop_speed_factor = 10
        # 
        self.speedup_scale = 1.1
        self.score_scale = 1.5
        # 不要动
        self.counter = 0
        self.rand_kill_alien = False
        
        self.initialize_dynamic_settings()
        
    def initialize_dynamic_settings(self):
        """随着游戏的进行二发生改变的设置"""
        # ship
        self.ship_speed_factor = 1
        # bullets
        self.bullet_speed_factor = 0.5
        self.bullets_allowed = 4
        # aliens
        self.alien_speed_factor = .5
        self.alien_points = 50
        # direction
        self.fleet_direction = 1  # fleet_direction为1表示向右移,为-1表示向左移
        
    def increase_speed(self):
        # 提高速度设置
        self.ship_speed_factor *= self.speedup_scale
        self.bullet_speed_factor *= self.speedup_scale
        self.alien_speed_factor *= self.speedup_scale
        self.alien_points = int(self.alien_points * self.score_scale)

ship.py

表示飞船的类,在其他py文件中会将其进行实例化操作

# -*- coding: utf-8 -*-
"""
Created on Fri Dec 31 19:39:49 2021

@author: NO ONE
"""

import pygame
from pygame.sprite import Sprite


class Ship(Sprite):
    """负责管理飞船的大部分行为"""
    
    def __init__(self, ai_settings, screen):
        super().__init__()
        self.screen = screen
        self.ai_settings = ai_settings
        image = pygame.image.load("images/ship.bmp")
        self. image = pygame.transform.scale(image,(50,88))
        self.rect = self.image.get_rect()  # 只能是整数
        self.screen_rect = screen.get_rect()  # 只能是整数
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom
        self.centerx = float(self.rect.centerx)
        self.bottom = float(self.rect.bottom)
        self.moving_right = False
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False
        
    def update(self):
        """更新位置坐标"""
        # 这里用if是优秀的选择,而不是用elif
        # 建议尽量用elif
        if self.moving_right and self.rect.right <= self.screen_rect.right:
            self.centerx += self.ai_settings.ship_speed_factor
        if self.moving_left and self.rect.left >= self.screen_rect.left:
            self.centerx -= self.ai_settings.ship_speed_factor
        if self.moving_up and self.rect.top >= self.screen_rect.top:
            self.bottom -= self.ai_settings.ship_speed_factor
        if self.moving_down and self.rect.bottom <= self.screen_rect.bottom:
            self.bottom += self.ai_settings.ship_speed_factor
        
        self.rect.centerx = self.centerx
        self.rect.bottom = self.bottom
        
    def scaleme(self, ratio=1):
        self.image = self.image
        
    def blitme(self):
        # self.rect attri=[left,top,right,bottom,centerx,centery,width,height]
        self.screen.blit(self.image, self.rect)  
                                     # 传入的数值只取前两个的整数部分
        # print("self.rect.width", self.rect.width)
        
    def center_ship(self):
        """将飞船(属性值)居中(居中)"""
        # self.rect.midbottom = self.screen_rect.midbottom
        self.centerx = self.screen_rect.centerx
        self.bottom = self.screen_rect.bottom
        
        

bullet.py

# -*- coding: utf-8 -*-
"""
Created on Sat Jan  1 10:46:03 2022

@author: NO ONE
"""

import pygame
from pygame.sprite import Sprite


class Bullet(Sprite):
    """一个对飞船发射的子弹进行管理的类"""
    
    def __init__(self, ai_settings, screen, ship):
        super().__init__()
        self.screen = screen
        self.rect = pygame.Rect(
            0,0,ai_settings.bullet_width,ai_settings.bullet_height)
        self.rect.midtop = ship.rect.midtop
        self.color = ai_settings.bullet_color
        self.speed_factor = ai_settings.bullet_speed_factor
        self.y = float(self.rect.y)
      
    def update(self):
        """子弹向上移动"""
        self.y -= self.speed_factor
        self.rect.y = self.y
        
    def draw_bullet(self):
        """在屏幕上绘制子弹"""
        pygame.draw.rect(self.screen, self.color, self.rect)

alien.py

# -*- coding: utf-8 -*-
"""
Created on Sat Jan  1 23:51:58 2022

@author: NO ONE
"""

import pygame
from pygame.sprite import Sprite

class Alien(Sprite):
    """A class represented to a single alien"""
    def __init__(self, ai_settings, screen):
        super().__init__()
        self.screen = screen
        self.ai_settings = ai_settings
        image = pygame.image.load("images/alien.bmp")
        self.image = pygame.transform.scale(image, (100,50))
        self.rect = self.image.get_rect()
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height
        
        self.x = float(self.rect.x)
        self.y = float(self.rect.y)
        
    def blitme(self):
        self.screen.blit(self.image, self.rect)
        
    def update(self):
        """向左或向右移动外星人"""
        self.x += (self.ai_settings.alien_speed_factor * 
                   self.ai_settings.fleet_direction)
        self.rect.x = self.x
        
    def check_edges(self):
        """如果外星人位于屏幕边缘,就返回True"""
        screen_rect = self.screen.get_rect()
        if self.rect.right > screen_rect.right:
            return True
        elif self.rect.left < screen_rect.left:
            return True

game_stats.py

class GameStats():
    """跟踪游戏的统计信息"""
    def __init__(self, ai_settings):
        self.ai_settings = ai_settings
        self.game_active = False
        self.high_score = 0  # 任何情况下都不会修改该值,所以在init中
        self.reset_stats()
        
    def reset_stats(self):
        self.ships_left = self.ai_settings.ship_limit
        self.score = 0
        self.level = 1

button.py

# -*- coding: utf-8 -*-
"""
Created on Mon Jan  3 20:43:05 2022

@author: NO ONE
"""

import pygame.font
from pygame.sprite import Group

from ship import Ship


class Button():
    def __init__(self, ai_settings, screen, msg):
        self.screen = screen
        self.screen_rect = self.screen.get_rect()
        
        self.width, self.height = 200, 50
        self.button_color = 0, 255, 0
        self.text_color = 255, 255, 255
        self.font = pygame.font.SysFont(None, 48)
        self.rect = pygame.Rect(0, 0, self.width, self.height)
        self.rect.center = self.screen_rect.center
        
        self.prep_msg(msg)
        
    def prep_msg(self, msg):
        self.msg_image = self.font.render(msg, True, self.text_color, 
                                          self.button_color)
        self.msg_image_rect = self.msg_image.get_rect()
        self.msg_image_rect.center = self.rect.center
        
    def draw_button(self):
        self.screen.fill(self.button_color, self.rect)
        self.screen.blit(self.msg_image, self.msg_image_rect)
        
        
class ScoreBoard():
    def __init__(self, ai_settings, screen, stats):
        self.ai_settings = ai_settings
        self.screen = screen
        self.screen_rect = self.screen.get_rect()
        self.stats = stats
        
        self.text_color = 30, 30, 30
        self.font = pygame.font.SysFont(None, 48)
        
        self.prep_score()  # 准备分数图图像
        self.prep_high_score() # 准备最高分图像
        self.prep_level()  # 准备等级图像
        self.prep_ships()  # 准备飞船图像
        
    def prep_score(self):
        rounded_score = int(round(self.stats.score, -1))
        score_str = "{:,}".format(rounded_score)
        self.score_image = self.font.render(score_str, True, self.text_color)
        
        self.score_rect = self.score_image.get_rect()
        self.score_rect.right = self.screen_rect.right - 20
        self.score_rect.top = 20
        
    def prep_high_score(self):
        rounded_score = int(round(self.stats.high_score, -1))
        high_score_str = "{:,}".format(rounded_score)
        self.high_score_image = self.font.render(high_score_str, True, 
                                                 self.text_color)
        self.high_score_rect = self.high_score_image.get_rect()
        self.high_score_rect.top = self.score_rect.top
        self.high_score_rect.centerx = self.screen_rect.centerx
        
    def prep_level(self):
        level_str = "Level: " + str(self.stats.level)
        self.level_image = self.font.render(level_str, True, self.text_color)
        self.level_image_rect = self.level_image.get_rect()
        self.level_image_rect.right = self.screen_rect.right - 20
        self.level_image_rect.top = self.score_rect.bottom + 20
        
    def prep_ships(self):
        self.ships = Group()
        for l in range(self.stats.ships_left):
            ship = Ship(self.ai_settings, self.screen)
            ship.rect.top = 5
            ship.rect.left = 10 + l*ship.rect.width
            self.ships.add(ship)      
        
    def show_score(self):  # 显示图像,先准备,再显示
        self.screen.blit(self.score_image, self.score_rect)
        self.screen.blit(self.high_score_image, self.high_score_rect)
        self.screen.blit(self.level_image, self.level_image_rect)
        self.ships.draw(self.screen)  # Group类方法,自动调用surface.blit()         
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值