python学习-外星人入侵项目

python学习-外星人入侵项目

该项目主要学习到以下几点

  1. 面向对象的变成方式
  2. pygame库的基本使用方式
  3. 项目从开始建立,到项目逐渐复杂,其中的优化以及代码重构

附上gitee项目地址(欢迎大家优化):https://gitee.com/ChillaxLXL/alien_invasion

下面则附上具体代码

alien_invasion.py:项目主函数

import sys
import pygame
from pygame.sprite import Group
from settings import Settings
from ship import Ship
from gamestats import Gamestats
from button import Button
from scoreboard import Scoreboard
import game_functions as gf 


def run_game():
    #初始化游戏并创建一个屏幕对象
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    #创建Play按钮
    play_button = Button(ai_settings,screen,"Play")

    #创建一个用于信息统计的实例
    stats = Gamestats(ai_settings)
    sb = Scoreboard(ai_settings,screen,stats)

    #创建一艘飞船
    ship = Ship(screen,ai_settings)

    #创建一个外星人编组
    aliens = Group()

    #创建外星人群
    gf.create_fleet(ai_settings,screen,ship,aliens)

    #创建一个用于存储子弹的编组
    bullets = Group()

    #开始游戏的主循环
    while True:

        #监视键盘和鼠标事件
        gf.check_events(ai_settings,stats,play_button,sb,screen,ship,aliens,bullets)

        if stats.game_active:
            #更新飞船位置信息
            ship.update()
            #更新子弹的位置
            gf.update_bullets(ai_settings,stats,sb,screen,ship,aliens,bullets)
            #更新外星人的位置
            gf.update_alien(ai_settings,stats,sb,screen,ship,aliens,bullets)
        
        #每次循环时都重新绘制屏幕
        gf.update_screen(ai_settings,stats,sb,screen,ship,aliens,bullets,play_button)

run_game()

game_function.py:项目功能函数

import sys
import pygame
from bullet import Bullet
from alien import Alien
from time import sleep

"""响应按键"""
def check_keydown_events(event,ai_settings,screen,ship,bullets):
    if event.key == pygame.K_RIGHT:
        #向右移动飞船
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        #向左移动飞船
        ship.moving_left = True
    elif event.key == pygame.K_SPACE:
        fire_bullet(ai_settings,screen,ship,bullets)
    elif event.key == pygame.K_q:
        sys.exit()
        
"""响应松开"""
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

def check_events(ai_settings,stats,play_button,sb,screen,ship,aliens,bullets):  
    """响应按键和鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event,ai_settings,screen,ship,bullets)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event,ship)
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x,mouse_y = pygame.mouse.get_pos()
            check_play_button(ai_settings,stats,play_button,sb,screen,ship,aliens,bullets,mouse_x,mouse_y)

def check_play_button(ai_settings,stats,play_button,sb,screen,ship,aliens,bullets,mouse_x,mouse_y):
    """在玩家单机play按钮时开始游戏"""
    button_clicked = play_button.rect.collidepoint(mouse_x,mouse_y)
    if button_clicked and not stats.game_active:
        #重置游戏设置
        ai_settings.initialize_dynamic_settings()
        #隐藏光标
        pygame.mouse.set_visible(False)
        
        #重新游戏统计信息
        stats.reset_stats()
        stats.game_active = True

        #重置积分牌图像
        sb.prep_level()
        sb.prep_high_score()
        sb.prep_score()
        sb.prep_ships()

        #清空外星人列表和子弹列表
        aliens.empty()
        bullets.empty()

        #创建一群新的外星人,并让飞船居中
        create_fleet(ai_settings,screen,ship,aliens)
        ship.center_ship()
    

"""刷新屏幕"""
def update_screen(ai_settings,stats,sb,screen,ship,aliens,bullets,play_button):
    """更新屏幕上的图像,并切换到新屏幕"""
    #每次循环都重新绘制屏幕
    screen.fill(ai_settings.bg_color)
    sb.show_score()
    ship.blitme()
    aliens.draw(screen)

    #在飞船和外星人后绘制所有子弹
    for bullet in bullets:
        bullet.blitme()

    #如果游戏处于非活动状态,就绘制Play按钮
    if not stats.game_active:
        play_button.draw_button()

    #让最近绘制的屏幕可见
    pygame.display.flip()

def update_bullets(ai_settings,stats,sb,screen,ship,aliens,bullets):
    """更新子弹的位置,并删除消失的子弹"""
    #更新子弹的位置
    bullets.update()

    #删除已经消失的子弹
    for bullet in bullets.copy():
        if bullet.rect.bottom < 0:
            bullets.remove(bullet)
    check_aliens_bullets_collisions(ai_settings,stats,sb,screen,ship,aliens,bullets)
    

def update_alien(ai_settings,stats,sb,screen,ship,aliens,bullets):
    """检查是否有外星人位于屏幕边缘,并更新外星人的位置"""
    check_fleet_edges(ai_settings,aliens)
    aliens.update()

    #检测外星人和飞船之间的碰撞
    if pygame.sprite.spritecollideany(ship,aliens):
        ship_hit(ai_settings,stats,screen,ship,aliens,bullets)

    check_aliens_bottom(ai_settings,stats,sb,screen,ship,aliens,bullets)
    
def check_aliens_bullets_collisions(ai_settings,stats,sb,screen,ship,aliens,bullets):
    #检查是否有子弹击中了外星人
    #如果击中,就删除对应的子弹和外星人
    collisions = pygame.sprite.groupcollide(bullets,aliens,True,True)

    if collisions:
        for aliens in collisions.values():
            stats.score += ai_settings.alien_points * len(aliens)
            sb.prep_score()
        check_high_score(stats,sb)
        
    if len(aliens) == 0:
        #删除所有子弹新建一群外星人
        bullets.empty()
        ai_settings.increase_speed()

        #提高等级
        stats.level += 1
        sb.prep_level()

        create_fleet(ai_settings,screen,ship,aliens)

def check_aliens_bottom(ai_settings,stats,sb,screen,ship,aliens,bullets):
    #检查是否有外星人到达屏幕底端
    screen_rect = screen.get_rect()
    for alien in aliens:
        if alien.rect.bottom > screen_rect.bottom:
            #飞船坠毁 
            ship_hit(ai_settings,stats,sb,screen,ship,aliens,bullets)
            break

def ship_hit(ai_settings,stats,sb,screen,ship,aliens,bullets):
    """响应被外星人撞到的飞船"""
    if stats.ships_left > 0 :
        #将ship_left减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 fire_bullet(ai_settings,screen,ship,bullets):
    """根据子弹限制创建新子弹对象"""
    if len(bullets) < ai_settings.bullets_allowed:
            new_bullet = Bullet(ai_settings,screen,ship)
            bullets.add(new_bullet)

def get_number_aliens_x(ai_settings,alien_width):
    """计算每行可容纳多少个外星人"""
    available_space_x = ai_settings.screen_width - 2 * alien_width
    number_aliens_x = int(available_space_x/(2*alien_width))
    return number_aliens_x

def get_number_rows(ai_settings,ship_heigh,alien_heigh):
    """计算屏幕可以容纳多少行外星人"""
    avaliable_space_y = (ai_settings.screen_height - 3 * alien_heigh - 2 *  ship_heigh)
    number_rows = int(avaliable_space_y/(2 * alien_heigh))
    return number_rows

def creat_alien(ai_settings,screen,aliens,alien_number,number_row):
    """创建一个外星人并将其放在当前行"""
    alien = Alien(ai_settings,screen)
    alien_width = alien.rect.width
    alien_heigh = alien.rect.height
    alien.x = alien_width + 2 * alien_width * alien_number
    alien.y =  100 + alien_heigh + 2 * alien_heigh * number_row
    alien.rect.x = alien.x
    alien.rect.y = alien.y
    aliens.add(alien)

def create_fleet(ai_settings,screen,ship,aliens):
    """创建外星人群"""
    #创建一个外形人,并计算一行可容纳多少个外星人
    #外星人间距为外星人宽度
    alien = Alien(ai_settings,screen)
    number_aliens_x = get_number_aliens_x(ai_settings,alien.rect.width)
    number_rows = get_number_rows(ai_settings,ship.rect.height,alien.rect.height)
    #创建一行外星人
    for number_row in range(number_rows):
        for alien_number in range(number_aliens_x):
            creat_alien(ai_settings,screen,aliens,alien_number,number_row)

def check_fleet_edges(ai_settings,aliens):
    """有外星人到达边缘时采取相应的措施"""
    for alien in aliens:
        if alien.check_edges():
            change_fleet_direction(ai_settings,aliens)
            break

def change_fleet_direction(ai_settings,aliens):
    """将整群外星人下移,并改变他们的方向"""
    for alien in aliens.copy():
        alien.rect.y += ai_settings.fleet_dorp_speed
    ai_settings.fleet_direction *= -1

def check_high_score(stats,sb):
    """检查是否诞生了新的最高分"""
    if stats.score > stats.high_score:
        stats.high_score = stats.score
        sb.prep_high_score()

setting.py:设置类

class Settings():
    """存储《外星人入侵》所有的设置的类"""

    def __init__(self):
        """初始化游戏的设置"""
        #屏幕设置
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (230,230,230)

        #飞船设置
        self.ship_speed_factor = 1.5
        self.ship_limit = 3

        #子弹设置
        self.bullet_speed_factor = 1
        self.bullets_allowed = 3

        #外星人设置
        self.alien_speed_factor = 1
        self.fleet_dorp_speed = 10
        #fleet_direction为1表示向右移动,为-1表示向左移动
        self.fleet_direction = 1

        #以什么样的素的加快游戏节奏
        self.speedup_scaie = 1.1

        self.initialize_dynamic_settings()
    
    def initialize_dynamic_settings(self):
        """初始化随游戏进行而变化的设置"""
        self.ship_speed_factor = 1.5
        self.bullet_speed_factor = 1
        self.alien_speed_factor = 1
        self.alien_points = 50
        #fleet_direction为1表示向右移动,为-1表示向左移动
        self.fleet_direction = 1

    def increase_speed(self):
        """提高速度和分数设置"""
        self.ship_speed_factor *= self.speedup_scaie
        self.bullet_speed_factor *= self.speedup_scaie
        self.alien_speed_factor *= self.speedup_scaie

        self.alien_points = int(self.alien_points * self.speedup_scaie)

ship.py:飞船类

import pygame
from pygame.sprite import Sprite


class Ship(Sprite):
    def __init__(self,screen,ai_settings):
        """初始化飞船并设置其初始位置"""
        super().__init__()
        self.screen = screen

        #加载图像并获取外接矩阵
        self.image = pygame.image.load('images/hero1.png')
        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

        #再飞船的属性center中存储小数值
        self.center = float(self.rect.centerx)

        #移动标志
        self.moving_right = False
        self.moving_left = False

        self.ai_settings = ai_settings


    def blitme(self):
        """指定位置绘制飞船"""
        self.screen.blit(self.image,self.rect)

    def update(self):
        """根据移动标志调整飞船的位置"""
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor
        if self.moving_left and self.rect.left > 0:
            self.center -= self.ai_settings.ship_speed_factor

        #根据self.center更新rect对象
        self.rect.centerx = self.center
    
    def center_ship(self):
        """让飞船在屏幕中居中"""
        self.center = self.screen_rect.centerx
    

alien.py:外星人类

import pygame
from pygame.sprite import Sprite

class Alien(Sprite):
    """表示单个外星人"""

    def __init__(self,ai_settings,screen):
        #初始化外星人及其位置
        super().__init__()
        self.screen = screen
        self.ai_settings = ai_settings

        #加载外星人图像,并初始化其位置
        self.image = pygame.image.load("images/enemy0.png")
        self.rect = self.image.get_rect()

        #每个外星人最初在屏幕左上角附近
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height

        #存储外星人精确位置
        self.x = float(self.rect.x)

    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 < 0 :
            return True
        

bullet.py:子弹类

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    """一个对飞船发射的子弹进行管理的类"""

    def __init__(self,ai_settings,screen,ship):
        """在飞船所处的位置创建一个子弹对象"""
        super().__init__()
        self.screen = screen 

        #获取子弹的外接矩
        self.image = pygame.image.load("images/bullet1.png")
        self.rect = self.image.get_rect()

        #在(0,0)处创建一个表示子弹的矩形,再设置正确的位置
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top

        #存储小数表示子弹的位置
        self.y = float(self.rect.y)

        self.speed_factor = ai_settings.bullet_speed_factor

    def update(self):
        """向上移动子弹"""
        #更新表示子弹位置的小数值
        self.y -= self.speed_factor
        #更新表示子弹rect的位置
        self.rect.y = self.y

    def blitme(self):
        """在屏幕上绘制子弹"""
        self.screen.blit(self.image,self.rect)
        

button.py:按键类

import pygame

class Button():
    
    def __init__(self,ai_settings,screen,msg):
        """初始化按钮的属性"""
        self.screen = screen 
        self.screen_rect = 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)

        #设置按钮的rect对象,并居中
        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):
        #将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)


gamestats.py:统计信息类

class Gamestats():
    """跟踪游戏的统计信息"""

    def __init__(self,ai_settings):
        #初始化统计信息
        self.ai_settings = ai_settings
        self.reset_stats()
        self.game_active = False
        #最高得分
        self.high_score = 0

    def reset_stats(self):
        #初始化在游戏运行期间可能变化的统计信息
        self.ships_left = self.ai_settings.ship_limit
        self.score = 0
        self.level = 0

scoreboard.py:看板类

import pygame
from pygame.sprite import Group
from ship import Ship

class Scoreboard():
    """显示得分信息的类"""

    def __init__(self,ai_settings,screen,stats):
        """初始化陷时得分涉及的属性"""
        self.screen = screen
        self.screen_rect = screen.get_rect()
        self.ai_settings = ai_settings
        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.ai_settings.bg_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):
        """将最高分转换为渲染的图像"""
        high_score = int(round(self.stats.high_score,-1))
        high_score_str = "{:,}".format(high_score)
        self.high_score_image = self.font.render(high_score_str,True,self.text_color,self.ai_settings.bg_color)

        #将得分放在屏幕的右上角
        self.high_score_rect = self.score_image.get_rect()
        self.high_score_rect.centerx = self.screen_rect.centerx
        self.high_score_rect.top = 20

    def prep_level(self):
        """将等级转换为渲染的图像"""
        self.level_image = self.font.render(str(self.stats.level),True,self.text_color,self.ai_settings.bg_color)

        #将等级放在得分下方
        self.level_rect = self.level_image.get_rect()
        self.level_rect.right = self.score_rect.right
        self.level_rect.top = self.score_rect.bottom + 10 

    def prep_ships(self):
        """显示还余下多少艘飞船"""
        self.ships = Group()
        for ship_number in range(self.stats.ships_left):
            ship = Ship(self.screen,self.ai_settings)
            ship.rect.x = 10 + ship_number * ship.rect.width
            ship.rect.y = 10
            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_rect)
        #绘制飞船
        self.ships.draw(self.screen)


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值