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()
游戏主程序,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()
游戏运行所需函数,游戏的大部分功能都在此实现
# -*- 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)后刷新
# # 如果要添加 按下开始游戏就必须对该代码进行重构
# 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
#——————————————————————————————————————————————————————————————————————————————
游戏的所有设置都在这里
# -*- 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)
表示飞船的类,在其他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
# -*- 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)
# -*- 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
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
# -*- 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()