night_Mr 2020-04-17 15:51 采纳率: 100%
浏览 752
已采纳

《外星人入侵项目》飞船碰撞外星人后不在底部生成,而是在原位置的问题

图片说明

我按照书上写的代码,运行游戏的时候发现飞船碰撞到外星人以后,是在原位置刷新,不会在屏幕底部刷新,不清楚为什么。
主程序:

import pygame

from settings import Settings

from game_stats import GameStats

from button import Button

from ship import Ship

from pygame.sprite import Group

import game_functions as gf

from alien import Alien

def run_game():
    """初始化游戏,设置屏幕对象"""
    pygame.init()    # 初始化所有导入的pygame模块
    ai_settings = Settings()    # 用ai_settings代替Settings()

    screen = pygame.display.set_mode(
        (ai_settings.screen_width, ai_settings.screen_height))    # 设置屏幕的长宽, pygame.display.set_mode((width, height))

    pygame.display.set_caption("Alien Invasion")    # 设置当前窗口的标题

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

    # 创建一个用于存储游戏统计信息的实例
    stats = GameStats(ai_settings)

    ship = Ship(ai_settings, screen)    # 将设置好的屏幕参数传给Ship里面,然后用ship代替Ship类

    bullets = Group()    # 创建一个用于储存子弹的编组,回头会存储bullet模块新建的子弹,Group类用于保存和管理多个Sprite对象的容器类

    # 创建一个用于储存外星人的编组
    aliens = Group()

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

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

        # 监视键盘和鼠标事件
        gf.check_events(ai_settings, screen, stats, play_button, ship, aliens, bullets)    # 返回ship是由于gf需要ship的返回值,用于调用ship中update函数 

        if stats.game_active:
            ship.update()    # 移动飞船, 更新飞船的位置      
            gf.update_bullets(ai_settings, screen, ship, aliens, bullets)    # 更新子弹位置
            gf.update_aliens(ai_settings, stats, screen, ship, aliens, bullets)    # 更新外星人位置

        # 每次循环时都会重新绘制屏幕,让最近绘制的屏幕可见    
        gf.update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button)     


run_game()

游戏功能程序:

import sys

from time import sleep

import pygame

from bullet import Bullet

from alien import Alien

def check_keydown_events(event, ai_settings, screen, ship, bullets):
    """响应按键"""
    if event.key == pygame.K_RIGHT:    # 我们读取属性event.key,查看它是否是键盘右键
        ship.moving_right = True    # 是的话就向右移动飞船
    elif event.key == pygame.K_LEFT:    # 我们读取属性event.key,查看它是否是键盘左键
        ship.moving_left = True    # 是的话就向左移动飞船
    elif event.key == pygame.K_UP:    # 我们读取属性event.key,查看它是否是键盘上键
        ship.moving_up = True    # 是的话就向上移动飞船
    elif event.key == pygame.K_DOWN:    # 我们读取属性event.key,查看它是否是键盘下键
        ship.moving_down = True    # 是的话就向下移动飞船
    elif event.key == pygame.K_SPACE:    # 我们读取属性event.key,查看它是否是键盘空格键
        fire_bullet(ai_settings, screen, ship, bullets)    # 是的话我们将运行函数fire_bullet
    elif event.key == pygame.K_q:
            sys.exit()     

def check_keyup_events(event, ship):
    """响应松开"""
    if event.key == pygame.K_RIGHT:    # 当event按键属性key遇到右键的KEYUP事件时就会让移动标志变成False
        ship.moving_right = False 
    elif event.key == pygame.K_LEFT:    # 当event按键属性key遇到左键的KEYUP事件时就会让移动标志变成False
        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, screen, stats, play_button, ship, aliens, bullets):
    """响应按键和鼠标事件"""
    for event in pygame.event.get():    # 获得事件储存到event里面
        if event.type == pygame.QUIT:    # 检查event事件的类型是不是鼠标点×
                sys.exit()    # 是的话就退出游戏      
        elif event.type == pygame.KEYDOWN:    # 检测event事件是不是KEYDOWN事件
            check_keydown_events(event, ai_settings, screen, ship, bullets)     # 如果发生按键事件,将各种参数带入到check_keydown_events()
        elif event.type == pygame.KEYUP:      # 我们添加了一个新的elif模块,用于响应KEYUP事件,让事件type类型知道是按键key
            check_keyup_events(event, ship)
        elif event.type == pygame.MOUSEBUTTONDOWN:    # 检测鼠标按下的事件
            mouse_x, mouse_y, = pygame.mouse.get_pos()    # 它返回一个元组,其中包含玩家单击时鼠标的x和y的坐标
            check_play_button(ai_settings, screen, stats, play_button, ship, 
                aliens, bullets, mouse_x, mouse_y) 

def check_play_button(ai_settings, screen, stats, play_button, ship, aliens, 
    bullets, mouse_x, mouse_y):
    """在玩家单机Play按钮时开始新的游戏"""
    if play_button.rect.collidepoint(mouse_x, mouse_y):    # 使用collidepoint()检查鼠标单击的位置是否在Play按钮的rect内
        # 重置游戏统计信息
        stats.reset_stats()
        stats.game_active = True

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

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

def fire_bullet(ai_settings, screen, ship, bullets):
    """创建一颗子弹,并将其加入到编组bullets中"""
    if len(bullets) < ai_settings.bullets_allowed:    # 限制子弹数量在10以内
        new_bullet = Bullet(ai_settings,  screen,  ship)    # 新建一个子弹
        bullets.add(new_bullet)    # 在bullets中再添加一个new_bullet

def update_bullets(ai_settings, screen, ship, aliens, bullets):
    """更新子弹的位置,并删除已经消失的子弹"""
    # 更新子弹的位置
    bullets.update()    # 通过执行这个函数来让我们绘制的东西显示在屏幕上

    # 删除已消失的子弹
    for bullet in bullets.copy():    # 为了不从列表和编组中删除条目,我们遍历编组的副本
        if bullet.rect.bottom <= 0:     # 如果子弹飞出屏幕
            bullets.remove(bullet)    # 将子弹从bullets中删除

    check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)

def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
    # 检查是否有子弹击中了外星人
    # 如果是这样就删除外星人和子弹
    collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)

    if len(aliens) == 0:
        # 删除现有的子弹并且新建一群外星人
        bullets.empty()
        create_fleet(ai_settings, screen, ship, aliens)

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循环,创建相应行数的外星人
    for row_number in range(number_rows):
        # 创建第一行外星人
        for alien_number in range(number_aliens_x):
            create_alien(ai_settings, screen, aliens, alien_number, 
                row_number)

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_height, alien_height):
    """计算屏幕可以容纳多少行外星人"""
    available_space_y = (ai_settings.screen_height - 
                            (3 * alien_height) - ship_height)    # 计算屏幕的空白高度
    number_rows = int(available_space_y / (2 * alien_height))    # 得到空白部分能够放多少行外星人
    return number_rows

def create_alien(ai_settings, screen, aliens, alien_number, row_number):
    """创建一个外星人并将其放在当前行"""
    alien = Alien(ai_settings, screen)    # 创建一个外星人
    alien_width = alien.rect.width    # 获得外星人宽度
    alien.x = alien_width + 2 * alien_width * alien_number    # 设置每个外星人在当前行的位置
    alien.rect.x = alien.x    # rect.x和rect.y是分别自动等于rect.left和rect.top的
    alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number    # 这相当于将外星人依次下放到各行
    aliens.add(alien)

def check_fleet_edges(ai_settings, aliens):
    """有外星人到达边缘时采取相应的措施"""
    for alien in aliens.sprites():    # 这里的aliens.sprites()返回一个列表包含所有精灵
        if alien.check_edges():    # 如果alien里面的check_edges函数返回是Ture,就执行下面的
            change_fleet_direction(ai_settings, aliens)    # 下移外星人并改变方向
            break

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

def ship_hit(ai_settings, stats, screen, ship, aliens, bullets):
    """响应被外星人撞到的飞船"""
    if stats.ships_left > 0:
        # 将ships_left减1
        stats.ships_left -= 1

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

        # 创建一群新的外星人,并将飞船放到屏幕底端中央
        create_fleet(ai_settings, screen, ship, aliens)
        ship.center_ship()
    else:
        stats.game_active = False

    # 暂停
    sleep(0.5)

def check_aliens_bottom(ai_settings, stats, screen, ship, aliens, bullets):
    """检查是否有外星人到达了屏幕底端"""
    screen_rect = screen.get_rect()
    for alien in aliens.sprites():
        if alien.rect.bottom >= screen_rect.bottom:
            # 像飞船被撞到一样进行处理
            ship_hit(ai_settings, stats, screen, ship, aliens, bullets)
            break

def update_aliens(ai_settings, stats, 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, screen, ship, aliens, bullets)

def update_screen(ai_settings, screen, stats, ship, aliens, bullets, play_button):
    """更新屏幕上的图像,并切换到新屏幕上"""
    # 每次循环时都重绘屏幕
    screen.fill(ai_settings.bg_color)
    # 在飞船和外星人后面重绘所有子弹
    for bullet in bullets.sprites():    # 这里的bullets.sprites()返回一个列表包含所有精灵
        bullet.draw_bullet()    # 将遍历的精灵都调用draw_bullet函数
    ship.blitme()    # 绘制飞船
    aliens.draw(screen)    # 绘制外星人

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

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

ship设置程序:

# Ship主要是获取飞船外形,然后

import pygame

class Ship():

    def __init__(self, ai_settings, screen):    # 形参列表中添加screen让飞船能够获取屏幕设置,添加ai_settings获取速度设置
        """初始化飞船并设置其初始位置"""
        self.screen = screen    # 我们将形参screen存储在一个属性中,以便能够在update()中使用它
        self.ai_settings = ai_settings    # 我们将形参ai_settings存储在一个属性中,以便能够在update()中使用它

        # 加载飞船图像并获取其外接矩形
        self.image = pygame.image.load('images/ship.bmp')    # 从文件中加载图片,并存储在self.image里面
        self.rect = self.image.get_rect()    # get_rect()是处理矩形图形的方法,返回值包含矩形的居中属性center centerx centery
                                             # 给这个属性赋值到self.rect中去
        self.screen_rect = screen.get_rect()    # self.screen也是获得了rect属性,返回值也是矩形的居中属性

        # 将每艘船的新飞船放在屏幕底部中央
        self.rect.centerx = self.screen_rect.centerx    # self.rect代表的是加载图形的rect属性,将它的x坐标
        self.rect.centery = self.screen_rect.centery                                                # 和背景图的x坐标居中属性相等, 意味着加载图片在背景图的中心位置
        self.rect.bottom = self.screen_rect.bottom     # 加载图片底部和背景图底部相等       

        # 在飞船的属性center中存储小数值
        self.center1 = float(self.rect.centerx)
        self.center2 = float(self.rect.centery)
        """其他关于矩形操作的位置关键词有:top,  left,  bottom,  right
                                           topleft,  bottomleft,  topright,  bottomright
                                           midtop,  midleft,  midbottom,  midright
                                           center,  centerx,  centery
                                           size,  width,  height"""
        # 移动标志
        self.moving_right = False
        self.moving_left = False
        self.moving_up = False
        self.moving_down = False

    def update(self):    
        """根据移动标志调整飞船的位置"""
        # 更新飞船的center值,而不是rect
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center1 += self.ai_settings.ship_speed_factor
        if self.moving_left and self.rect.left > 0:    # 0代表背景的左侧
            self.center1 -= self.ai_settings.ship_speed_factor
        if self.moving_up and self.rect.top > 0:
            self.center2 -= self.ai_settings.ship_speed_factor
        if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
            self.center2 += self.ai_settings.ship_speed_factor

        # 根据self.center更新rect对象
        self.rect.centerx = self.center1
        self.rect.centery = self.center2

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

    def center_ship(self):
        """让飞船在屏幕上居中"""
        self.center = self.screen_rect.centerx
  • 写回答

4条回答 默认 最新

  • 币粥仔 2021-07-20 15:25
    关注

    是因为原本书上本就没有设置上下移动,你自己添加了上下移动,所以要多一个y轴的中心位置信息。还有就是你的代码,center_ship那里虽然更新self,但没有更新x和y的位置信息,所以碰到飞船就会回到原位。
    总的来说就是少了两个位置信息,要改的话就在center_ship那里加:
    self.x = float(self.rect.x)
    self.y = float(self.rect.y)

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

问题事件

  • 已采纳回答 8月12日

悬赏问题

  • ¥15 鼠标右键,撤销删除 复制 移动,要怎样删除
  • ¥15 使用MATLAB进行余弦相似度计算加速
  • ¥15 服务器安装php5.6版本
  • ¥15 我想用51单片机和数码管做一个从0开始的计数表 我写了一串代码 但是放到单片机里面数码管只闪烁一下然后熄灭
  • ¥20 系统工程中,状态空间模型中状态方程的应用。请猛男来完整讲一下下面所有问题
  • ¥15 我想在WPF的Model Code中获取ViewModel Code中的一个参数
  • ¥15 arcgis处理土地利用道路 建筑 林地分类
  • ¥20 使用visual studio 工具用C++语音,调用openslsx库读取excel文件的sheet问题
  • ¥100 寻会做云闪付tn转h5支付链接的技术
  • ¥15 DockerSwarm跨节点无法访问问题