Python编程:从入门到实践 第12章 pygame

319 阅读2分钟

12-1 蓝色天空:创建一个背景为蓝色的Pygame窗口。

12-2 游戏角色 :找一幅你喜欢的游戏角色位图图像或将一幅图像转换为位图。创建一个类,将该角色绘制到屏幕中央,并将该图像的背景色设置为屏幕背景色,或将屏幕背景色设置为该图像的背景色。

两问合在一起回答了:

setting.py

class Settings:
    """第12章练习题的设置"""

    def __init__(self):
        """初始化设置"""
        self.bg_color = (100, 149, 237)
        self.screen_width = 1200
        self.screen_height = 800

game_function.py


import sys
import pygame


def check_events():
    """响应鼠标事件"""
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()


def update_screen(pcc_setting, screen, person):
    """更新屏幕上的图像,并切换屏幕"""
    # 每次循环都重绘屏幕
    screen.fill(pcc_setting.bg_color)
    person.blitme()

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

person.py

import pygame


class Person:
    def __init__(self, screen):
        """初始化人物并设置人物位置"""
        self.screen = screen

        # 加载人物外形并获取其外接矩形
        self.image = pygame.image.load('images/person.svg')
        self.image = pygame.transform.smoothscale(self.image, (80, 80))
        self.rect = self.image.get_rect()
        self.screen_rect = screen.get_rect()

        # 将人物放到画面中央
        self.rect.centerx = self.screen_rect.centerx
        self.rect.centery = self.screen_rect.centery

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

如果要调整图片大小,也可以使用下面的代码:

self.image = pygame.transform.scale(self.image, (50, 50))

12-3 火箭:编写一个游戏,开始时屏幕中央有一个火箭,而玩家可使用四个方向键上下左右移动火箭。请务必确保火箭不会移到屏幕外面。

settings.py

#!/usr/bin/python
# -*- coding: utf-8 -*-


class GlobalSettings():
    """关于游戏的全局设置"""

    def __init__(self):
        self.canvas_width = 1200
        self.canvas_height = 800
        self.bg_color = (100, 149, 237)
        self.rocket_speed_factor = 1.3

rocket.py

#!/usr/bin/python
# -*- coding: utf-8 -*-


import pygame as pg


class Rocket():
    def __init__(self, roc_settings, canvas):
        """初始化火箭的设置"""
        # 图像在canvas上
        self.canvas = canvas
        self.settings = roc_settings

        # 导入火箭,调整大小,get rect
        self.image = pg.image.load('images/rocket.svg')
        self.image = pg.transform.scale(self.image, (80, 80))

        self.rect = self.image.get_rect()
        self.canvas_rect = canvas.get_rect()

        # 火箭放到屏幕中央
        self.rect.centerx = self.canvas_rect.centerx
        self.rect.centery = self.canvas_rect.centery

        # 因为centerx和centery只能存整数,而rocket_speed_factor可能是小数,所以需要调整
        self.center_x = float(self.rect.centerx)
        self.center_y = float(self.rect.centery)

        # self.center_y_position = float(self.rect.centery)

        # 不断移动的标志
        self.move_right = False
        self.move_left = False
        self.move_up = False
        self.move_down = False

    def refresh_rocket(self):
        """刷新火箭位置"""
        if self.move_right and self.rect.right < self.canvas_rect.right:
            self.center_x += self.settings.rocket_speed_factor

        if self.move_left and self.rect.left > self.canvas_rect.left:
            self.center_x -= self.settings.rocket_speed_factor

        if self.move_up and self.rect.top > self.canvas_rect.top:
            self.center_y -= self.settings.rocket_speed_factor

        if self.move_down and self.rect.bottom < self.canvas_rect.bottom:
            self.center_y += self.settings.rocket_speed_factor

        # 根据self.center更新rect对象 #看了两个小时,才发现这个地方没写。。或者说,没抄,囧。
        self.rect.centerx = self.center_x
        self.rect.centery = self.center_y

    def blitRocket(self):
        """在canvas上显示火箭"""
        self.canvas.blit(self.image, self.rect)

game_function.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import pygame as pg


def listen_key_mouse_events(rocket):
    """监听鼠标和键盘"""
    # 退出
    for event in pg.event.get():
        if event.type == pg.QUIT:
            sys.exit()

        # 监听鼠标按键事件(按下按键)
        elif event.type == pg.KEYDOWN:  # 不停的忘了这个.type,哎~~~
            # 如果有按下的动作,则调用 keydown
            listen_keydown(event, rocket)


        # 监听鼠标按键事件(按键抬起)
        elif event.type == pg.KEYUP:
            # 如果有抬起的动作,则调用 keyup
            listen_keyup(event, rocket)


def listen_keydown(event, rocket):
    """监听按下按键的动作"""
    # 如果是按下方向键,则rocket“不断移动的标志”为True
    if event.key == pg.K_RIGHT:
        rocket.move_right = True
    elif event.key == pg.K_LEFT:
        rocket.move_left = True
    elif event.key == pg.K_UP:
        rocket.move_up = True
    elif event.key == pg.K_DOWN:
        rocket.move_down = True


def listen_keyup(event, rocket):
    """监听抬起按键的动作"""
    # 如果是抬起方向键,则rocket“不断移动的标志”为False
    if event.key == pg.K_RIGHT:
        rocket.move_right = False
    elif event.key == pg.K_LEFT:
        rocket.move_left = False
    elif event.key == pg.K_UP:
        rocket.move_up = False
    elif event.key == pg.K_DOWN:
        rocket.move_down = False


def refresh_canvas(roc_setting, canvas, rocket):
    """刷新画布"""
    # 填充背景色
    canvas.fill(roc_setting.bg_color)
    # 刷新火箭
    rocket.blitRocket()
    # 刷新画布canvas
    pg.display.flip()

    

main.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

import pygame as pg
from settings import GlobalSettings
import game_function as gf
from rocket import Rocket


def run_game():
    """启动游戏"""
    pg.init()

    # 根据setting的内容创建一个对象,roc = rocket。
    roc_setting = GlobalSettings()

    # 设置画布
    canvas = pg.display.set_mode(
        (roc_setting.canvas_width, roc_setting.canvas_height))

    # 设置标题
    pg.display.set_caption("python从入门到实践第12章 12-3习题")

    # 创建火箭
    rocket = Rocket(roc_setting, canvas)

    # 开始全局循环
    while True:
        gf.listen_key_mouse_events(rocket)
        rocket.blitRocket()
        rocket.refresh_rocket()
        gf.refresh_canvas(roc_setting, canvas, rocket)


run_game()

12-4 按键:创建一个程序,显示一个空屏幕。在事件循环中,每当检测到pygame.KEYDOWN 事件时都打印属性event.key 。运行这个程序,并按各种键,看看 Pygame如何响应。

import sys, pygame

def run_game():
        pygame.init()
        
    screen = pygame.display.set_mode((1200,800))
    pygame.display.set_caption("python从入门到实践 12-4")

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                print(event.key)

        pygame.display.flip()

run_game()

这题简单粗暴,按下去不同的按键显示不同数字啊~

12-5 侧面射击:编写一个游戏,将一艘飞船放在屏幕左边,并允许玩家上下移动飞船。在玩家按空格键时,让飞船发射一颗在屏幕中向右穿行的子弹,并在子弹离开 屏幕而消失后将其删除。

settings.py

class Settings():
    def __init__(self):
        # 屏幕设置
        self.canvas_width = 1200
        self.canvas_height = 900
        self.canvas_bg_color = (60,179,113)
        self.pilot_speed_factor = 0.8

        # 子弹设置
        self.bullet_width = 15
        self.bullet_height = 3
        self.bullet_color = 60, 60, 60
        self.bullet_speed_factor = 1.3
        self.bullet_max = 4

bullet.py

import pygame
from pygame.sprite import Sprite


class Bullet(Sprite):
    def __init__(self, glob_setting, pilot, canvas):
        super(Bullet, self).__init__()
        # 代码super(Bullet, self).__init__() 使用了Python 2.7语法。
        # 这种语法也适用于Python 3,但你也可以将这行代码简写为super().__init__() 。
        self.canvas = canvas
        # 创建一个位于0,0的子弹,再移到右边去
        self.rect = pygame.Rect(0, 0, glob_setting.bullet_width,
                                glob_setting.bullet_height)
        self.rect.left = pilot.rect.left
        self.rect.centery = pilot.rect.centery

        # 小数存储子弹飞行轨迹x,y位置
        self.x = float(self.rect.x)
        # self.y = float(self.rect.y)

        # self.bullet_firing_ticker = False

        # 子弹颜色和速度
        self.color = glob_setting.bullet_color
        self.speed_factor = glob_setting.bullet_speed_factor

    def update(self):  # 这个地方一定要用update,是pygame.sprites()的内建函数
        # 子弹从右向左移动
        self.x -= self.speed_factor
        # 更新子弹位置
        self.rect.x = self.x

    def draw_bullet(self):
        pygame.draw.rect(self.canvas, self.color, self.rect)

pilot.py

import pygame


# 创建飞机
class Pilot():
    def __init__(self, canvas, glob_setting):
        self.setting = glob_setting
        self.canvas = canvas
        self.image = pygame.image.load('images/pilot.svg')
        self.image = pygame.transform.smoothscale(self.image, (100, 100))

        # 获取飞机图像外框矩形
        self.rect = self.image.get_rect()
        # 获取画布矩形
        self.canvas_rect = canvas.get_rect()

        # 设置右边中间为初始位置
        self.rect.right = self.canvas_rect.right
        self.rect.centery = self.canvas_rect.centery

        self.move_right_ticker = False
        self.move_left_ticker = False
        self.move_up_ticker = False
        self.move_down_ticker = False

        # centerx和centery不能是小数,过渡一下
        self.center_x = float(self.rect.centerx)
        self.center_y = float(self.rect.centery)

    # 设置飞机四个方向移动位置
    def refresh_pilot_position(self):
        if self.move_right_ticker and self.rect.right < self.canvas_rect.right:
            self.center_x += self.setting.pilot_speed_factor

        if self.move_left_ticker and self.rect.left > self.canvas_rect.left:
            self.center_x -= self.setting.pilot_speed_factor

        if self.move_up_ticker and self.rect.top > self.canvas_rect.top:
            self.center_y -= self.setting.pilot_speed_factor

        if self.move_down_ticker and self.rect.bottom < self.canvas_rect.bottom:
            self.center_y += self.setting.pilot_speed_factor

        self.rect.centerx = self.center_x
        self.rect.centery = self.center_y

    # 显示飞机
    def blitPilot(self):
        self.canvas.blit(self.image, self.rect)

game_function.py

import pygame
import sys

from bullet import Bullet


# 聆听游戏鼠标键盘事件
def listen_events(pilot, glob_setting, canvas, bullets):
    # event队列中如果有quit,则退出
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        # 按下键盘后的事件
        elif event.type == pygame.KEYDOWN:
            listen_events_KEYDOWN(event, pilot, glob_setting, canvas, bullets)

        # 抬起键盘后的事件
        elif event.type == pygame.KEYUP:
            listen_events_KEYUP(event, pilot)


def listen_events_KEYDOWN(event, pilot, glob_setting, canvas, bullets):
    if event.key == pygame.K_UP:
        pilot.move_up_ticker = True
    elif event.key == pygame.K_DOWN:
        pilot.move_down_ticker = True
    elif event.key == pygame.K_LEFT:
        pilot.move_left_ticker = True
    elif event.key == pygame.K_RIGHT:
        pilot.move_right_ticker = True

    # 按下空格生成子弹
    elif event.key == pygame.K_SPACE:
        if len(bullets) < glob_setting.bullet_max:
            new_bullet = Bullet(glob_setting, pilot, canvas)
            bullets.add(new_bullet)


def listen_events_KEYUP(event, pilot):
    if event.key == pygame.K_UP:
        pilot.move_up_ticker = False
    elif event.key == pygame.K_DOWN:
        pilot.move_down_ticker = False
    elif event.key == pygame.K_LEFT:
        pilot.move_left_ticker = False
    elif event.key == pygame.K_RIGHT:
        pilot.move_right_ticker = False


# 刷新画布
def refresh_canvas(canvas, glob_setting, pilot, bullets):
    # 画布添加背景色
    canvas.fill(glob_setting.canvas_bg_color)

    # 在小图像上画子弹
    for bullet in bullets.sprites():
        bullet.draw_bullet()

    # 显示pilot
    pilot.blitPilot()

    # 方向键后刷新pilot位置
    pilot.refresh_pilot_position()

    # 显示最新绘制的canvas
    pygame.display.flip()


# 子弹状态
def bullets_conditions(bullets):
    # 删除屏幕外的子弹
    for bullet in bullets.copy():
        if bullet.rect.right < 0:
            bullets.remove(bullet)
    # print(len(bullets))

    # 刷新画布上的子弹
    bullets.update()

main.py

import pygame
import game_function as gf
from settings import Settings
from pilot import Pilot
from pygame.sprite import Group


def run_game():
    # 初始化pygame
    pygame.init()
    # 根据setting的数值,创建对象
    glob_setting = Settings()
    # 创建屏幕画布
    canvas = pygame.display.set_mode(
        (glob_setting.canvas_width, glob_setting.canvas_height)
    )
    # 设置标题
    pygame.display.set_caption('python从入门到提高 12-5')
    # 根据class Pilot创建飞机pilot
    pilot = Pilot(canvas, glob_setting)
    # 创建子弹群
    bullets = Group()

    # 开始游戏循环
    while True:
        # 监听键盘鼠标事件
        gf.listen_events(pilot, glob_setting, canvas, bullets)

        # 刷新画布
        gf.refresh_canvas(canvas, glob_setting, pilot, bullets)

        # 子弹管理
        gf.bullets_conditions(bullets)


run_game()

这一章做的有点慢,看代码,学,然后找bug。

昨天晚上写的,斜着移动只能先x,y方向移动(看按着哪个按键),然后再往另外一个方向移动,bug还没找出来呢。。。。囧。

搞了一天,找到问题所在了: 昨天晚上写的代码,在pilot.pydef refresh_position()这个地方,用的是if - elif - elif -elif,这回导致第一个if优先级最高,表现出来的就是按住右之后,只要不松手,一直往右走(这时候按上下无反应)导致我说的没有办法斜着走。这个地方要如本文代码一样,全部用if - if - if - if,课本中说了,但是当时这一部分没仔细看,直接撸代码去了。。。

在方法update() 中,我们添加了一个if 代码块而不是elif 代码块,这样如果玩家同时按下了左右箭头 键,将先增大飞船的rect.centerx 值,再降低这个值,即飞船的位置保持不变。如果使用一个elif 代码块来处理向左移动的情况,右箭头键将始终处于优先地位。从向左移 动切换到向右移动时,玩家可能同时按住左右箭头键,在这种情况下,前面的做法让移动更准确。