pygame游戏编程03,走!到森林里去



前言

小乖在得到我们发送的手机定位后,终于找到了正确的方向,呼呼地赶了过来。但当她来到“金暗之森”跟前的时候,一扫一路上的疲态,立马就变得跃跃欲试,恨不得马上进入其中探险。
“小乖!小乖!”你可等等啊,这里面麻烦不断,得先做些准备工作呀。好吧,哪里还能等我们把话说完,附近早已经不见了小乖的影子啦。。。

出现在森林之中

1.Ship类

在本章,由于小乖的到来,我们需要建立一个新类来对小乖的行为作出管理约束。这便是我们的Ship类啦。Ship类目前的主要功能为在游戏窗口中绘制人物、通过键盘来控制人物的行动。来看以下代码:

import pygame


class Ship:

    def __init__(self, screen, img_path="rc/pic/ic_actor.png", speed_factor=1):
        """
        构造方法,目前负责在指定位置绘制游戏人物,使人物对键盘作出响应
        :param screen: 当前游戏窗口的Surface对象
        :param img_path: 人物图片的路径
        :param speed_factor: 控制人物移动的快慢
        """
        self.screen = screen
        self.img = pygame.image.load(img_path)      # 获得当前人物图片的Surface对象
        self.rect = self.img.get_rect()             # 获得人物图片外接矩形的Rect对象
        self.screen_rect = self.screen.get_rect()

        self.rect.centerx = self.screen_rect.centerx        # 确定人物在游戏窗口中的X位置
        self.rect.centery = self.screen_rect.bottom - 30    # 确定人物在游戏窗口中的Y位置

    def blit_actor(self):
        """
        负责在游戏中绘制人物
        :return: 无
        """
        self.screen.blit(self.img, self.rect)

以上代码没有什么出现什么新的知识点,都是对之前所讲的再熟悉利用,所以,这里就不再赘述了。相应的,game_frame.py中的代码也需要做一定的调整。

    def run_game(self):     # 当游戏开始时候调用此方法
        if self.ship is None:
            self.ship = Ship(self.screen)	#实例化人物
        for ev in pygame.event.get():   # 从队列获取事件
            if ev.type == pygame.QUIT:  # 获取退出事件,并退出游戏
                sys.exit()
        self.ship.blit_actor()	#绘制人物

仔细观察以上代码可以发现,实际上就是多了对Ship类的实例化和绘制,通过对Ship类的实例化和绘制,使得人物出现在游戏之中。注意这里只是截取的game_frame.py文件中的一个代码片段,大家在实际操作过程中莫要忘了把Ship类导入进来哦。
运行main.py。哦,看哪!小乖终于来到了森林里!好吧,虽然到了森林之中,但是小乖目前啥也干不了,只能静静地站在那里,如若失了神一般。
“叫你跑那么快!”我们缓步追上,轻笑道。刚刚正准备为你写一段代码,让你在森林之中行动自如的,现在傻眼了吧。
在这里插入图片描述
好吧,我们也不能让小乖一直站在那个地方,太(没)可(法)怜(探)了(路),现在我们就来聊一聊如何让小乖动起来吧。

2.为人物添加键盘响应

还记得我们之前讲的pygame的事件机制嘛,其实当我们在游戏中按下键盘的时候,pygame就已经检测到了,并且也已经将这个事件存入了事件队列当中,只是当时我们没有处理这些事件。
那么现在是时候处理这些事件了,来看以下代码:

class Ship:

    def __init__(self, screen, img_path="rc/pic/ic_actor.png", speed_factor=1):
        """
        构造方法,目前负责在指定位置绘制游戏人物,使人物对键盘作出响应
        :param screen: 当前游戏窗口的Surface对象
        :param img_path: 人物图片的路径
        :param speed_factor: 控制人物移动的快慢
        """
        self.screen = screen
        self.img = pygame.image.load(img_path)      # 获得当前人物图片的Surface对象
        self.rect = self.img.get_rect()             # 获得人物图片外接矩形的Rect对象
        self.screen_rect = self.screen.get_rect()

        self.speed_factor = speed_factor
        self.cx = float(self.screen_rect.centerx)
        self.cy = float(self.screen_rect.bottom)

        self.rect.centerx = self.cx                 # 确定人物在游戏窗口中的X位置
        self.rect.centery = self.cy - 30            # 确定人物在游戏窗口中的Y位置

        self.moving_right = False                   # 开关变量,当为真的时候改变人物X坐标
        self.moving_left = False                    # 开关变量,当为真的时候改变人物X坐标
        self.moving_up = False                      # 开关变量,当为真的时候改变人物Y坐标
        self.moving_down = False                    # 开关变量,当为真的时候改变人物Y坐标

    def blit_actor(self):
        """
        负责在游戏中绘制人物
        :return: 无
        """
        self.screen.blit(self.img, self.rect)

    def check_ev(self, ev):
        """
        检测当前事件是键盘按键被按下还是按键复位
        :param ev: 当前事件,从事件迭代中获得
        :return: 无
        """
        if ev.type == pygame.KEYDOWN:
            self.check_ev_keydown(ev)
        elif ev.type == pygame.KEYUP:
            self.check_ev_keyup(ev)

    def check_ev_keydown(self, ev):
        """
        当有按键被按下的时候,判断是哪个按键被按下
        :param ev: 当前事件,从事件迭代中获得
        :return: 无
        """
        if ev.key == pygame.K_LEFT:         # 如果左键被按下,将对应开关变量置为真,改变人物X坐标
            self.moving_left = True
        elif ev.key == pygame.K_RIGHT:      # 如果右键被按下,将对应开关变量置为真,改变人物X坐标
            self.moving_right = True
        elif ev.key == pygame.K_UP:         # 如果上键被按下,将对应开关变量置为真,改变人物Y坐标
            self.moving_up = True
        elif ev.key == pygame.K_DOWN:       # 如果下键被按下,将对应开关变量置为真,改变人物Y坐标
            self.moving_down = True
        elif ev.key == pygame.K_SPACE:      # 如果空格被按下,发射子弹(预留)
            pass

    def check_ev_keyup(self, ev):
        if ev.key == pygame.K_LEFT:         # 左键弹起,将对应开关变量置为假,停止改变人物X坐标
            self.moving_left = False
        elif ev.key == pygame.K_RIGHT:      # 右键弹起,将对应开关变量置为假,停止改变人物X坐标
            self.moving_right = False
        elif ev.key == pygame.K_UP:         # 上键弹起,将对应开关变量置为假,停止改变人物Y坐标
            self.moving_up = False
        elif ev.key == pygame.K_DOWN:       # 下键弹起,将对应开关变量置为假,停止改变人物Y坐标
            self.moving_down = False

    def moving_update(self):
        """
        在游戏主循环中被调用,通过改变人物外接矩形坐标使人物移动
        :return:
        """
        if self.moving_left and self.rect.left > self.screen_rect.left:
            self.cx -= self.speed_factor
            self.rect.centerx = self.cx
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.cx += self.speed_factor
            self.rect.centerx = self.cx
        if self.moving_up and self.rect.top > self.screen_rect.top:
            self.cy -= self.speed_factor
            self.rect.centery = self.cy
        if self.moving_down and self.rect.bottom < self.screen_rect.bottom:
            self.cy += self.speed_factor
            self.rect.centery = self.cy
        self.blit_actor()

以上代码结合我给的注解理解起来还是非常容易的。只是大家要注意,Surface对象是不能直接改变它的坐标的,必须要通过它的外接矩形来改变(前面说过,Rect对象就是用来存储这些坐标的)。以上代码通过四个开关变量来控制人物的移动,键盘的按下与弹起来控制开关变量,开关变量来控制是否改变人物外接矩形坐标。同时,在moving_update方法中,我们还增加了避免让人物移出窗口之外的判断。
好了,运行main.py吧,现在小乖已经能够在“金暗之森”中愉快地玩(探)耍(路)了。

源代码下载

本章节源代码可以在[https://download.csdn.net/download/linuxlike/10831121]下载