基于pygame的自定义游戏《the box》

Posted 西兹克利斯托夫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于pygame的自定义游戏《the box》相关的知识,希望对你有一定的参考价值。

目录

引言

创建一款什么样的游戏

需要实现什么样的功能

如何去实现基本的功能

背景模块

主角色模块

场景模块

逻辑模块


引言

        最近在学习java之余,利用空余时间写了一款基于pygame的游戏,主要是想测试pygame各方面的性能,其次是因为python语言的简洁性,写起来是真的舒服,最后是拿来自娱自乐一下还挺有趣的(划重点)。声明:该游戏所有代码均为原创,纯手工制作而成。我会在文末放上游戏运行需要的完整文件包,需要的朋友请自取。

创建一款什么样的游戏

       由于pygame本身是一款轻量级的游戏开发工具,可以实现的功能也是比较的有限,因此为了简单化,便于操作等目的,本文通过创建一个基本矩形方块来实现各种目的,因为是以矩形方块作为主要操作对象,所以游戏命名为《The Box》。

需要实现什么样的功能

        作为一款测试开发游戏,需要实现的功能比较的基本,由于是一款二维平面游戏,因此需要实现一些较为传统的功能:

  • 游戏主要操作对象的创建与交互
  • 游戏中基本的键鼠交互
  • 游戏中场景的构建与其他对象的交互
  • 游戏中其他非环境对象的创建与交互
  • 游戏中基本循环逻辑的实现

        除了以上几个需要实现的基本功能,还需要考虑游戏的基本逻辑构建,所谓的逻辑构建就是为了实现游戏需要达到的目的,比如游戏能否足够有趣,是否具有可玩性,是否有足够的交互性等,当然这些都在不在本文的考虑范围之内(悲)。

如何去实现基本的功能

        目前只是初步的实现了一下一些基本功能,因为最近比较忙就暂停开发了,所以先发布一下看看自己有没有可能哪天能捡起来(笑)。废话不多说,先看目前的实现效果:

《 The Box》

        看起来还行,就是太呆了(大笑)。好了关于如何实现的问题,其实我们只需要从模块化设计入手就行。这款游戏主要是通过几个基本模块来实现的:背景模块、主角色模块、场景模块、逻辑模块。上面几个模块主要实现的功能及代码如下:

背景模块

# 背景墙
class BackgroundWall(py.sprite.Sprite):
    def __init__(self, screen):
        py.sprite.Sprite.__init__(self)
        wallPath = '../素材库/背景墙素材/'
        self.index = 0
        self.imageFiles = [os.path.join(wallPath, i) for i in os.listdir(wallPath)]
        self.images = [py.transform.smoothscale(py.image.load(name), (screen.get_width(), screen.get_height())) for name
                       in self.imageFiles]
        self.image = self.images[self.index]
        self.rect = self.image.get_rect()
        self.rect.topleft = (0, 0)

    def update(self, game_time):
        timeH, timeM = divmod(game_time, 60)
        self.index = int(timeH)
        if self.index>17:
            self.index =17- self.index%17
        self.image = self.images[self.index]
        self.rect = self.image.get_rect()
        self.rect.topleft = (0, 0)

        背景模块顾名思义是用来创建游戏的背景,本文在此实现的功能也是极其简单,主要通过调用游戏时间来实现游戏背景的自动切换。如果你想实现各种功能,比如说随着BGM切换背景,或者通过某种事件切换背景都是可以的,只需要在update函数中添加各种需要的功能即可。

主角色模块

class CubeHuman(object):
    def __init__(self, screen):
        self.x = screen.get_width()
        self.y = screen.get_height()
        self.CubeBodyGroup = py.sprite.Group()
        self.CubeEyeballGroup = py.sprite.Group()
        self.CubeMouthGroup = py.sprite.Group()
        self.CubeEyesGroup = py.sprite.Group()
        self.cube_body = self.CubeBody()
        self.cube_eyes = self.CubeEyes()
        self.cube_eyeball = self.CubeEyeball()
        self.cube_mouth = self.CubeMouth()
        self.CubeBodyGroup.add(self.cube_body)
        self.CubeEyeballGroup.add(self.cube_eyeball)
        self.CubeMouthGroup.add(self.cube_mouth)
        self.CubeEyesGroup.add(self.cube_eyes)

        # 伤害,血量等自身参数
        self.basicDamage = 10  # 基础伤害
        self.basic = 10  # 基础防御
        self.basicHp = 100  # 基础血量
        self.basicMp = 100  # 基础蓝量
        self.HP = 100
        self.MP = 100

        self.rate = 4
        hpImage = py.image.load('../素材库/血条和蓝条/血条.png')
        hpRect = hpImage.get_rect()
        self.hpImage = py.transform.smoothscale(hpImage,
                                                (int(hpRect.width / self.rate), int(hpRect.height / self.rate)))
        self.hpRect = self.hpImage.get_rect()
        mpImage = py.image.load('../素材库/血条和蓝条/蓝条.png')
        self.mpImage = py.transform.smoothscale(mpImage, (self.hpRect.width, self.hpRect.height))
        self.mpRect = self.mpImage.get_rect()

        hpWordImage = py.image.load('../素材库/血条和蓝条/HP.png')
        hpWordRect = hpWordImage.get_rect()
        self.hpWordImage = py.transform.smoothscale(hpWordImage,
                                                    (int(hpWordRect.width / self.rate),
                                                     int(hpWordRect.height / self.rate)))

        self.hpWordRect = self.hpWordImage.get_rect()
        mpWordImage = py.image.load('../素材库/血条和蓝条/MP.png')
        self.mpWordImage = py.transform.smoothscale(mpWordImage, (self.hpWordRect.width, self.hpWordRect.height))
        self.mpWordRect = self.mpWordImage.get_rect()
        self.lossImage = py.image.load('../素材库/血条和蓝条/空白条.png')

        self.hpWordRect.topleft = (5, 5)
        self.mpWordRect.topleft = (5, self.hpWordRect.height + 10)
        self.hpRect.topleft = (self.hpWordRect.right, self.hpWordRect.top)
        self.mpRect.topleft = (self.mpWordRect.right, self.mpWordRect.top)

    def DrawAttributes(self, screen):
        rateHp = 1 - (self.HP / self.basicHp)
        rateMp = 1 - (self.MP / self.basicMp)
        lossHPImage = py.transform.smoothscale(self.lossImage, (int(self.hpRect.width * rateHp), self.hpRect.height))
        lossHPRect = lossHPImage.get_rect()
        lossHPRect.topright = self.hpRect.topright
        lossMPImage = py.transform.smoothscale(self.lossImage, (int(self.mpRect.width * rateMp), self.mpRect.height))
        lossMPRect = lossMPImage.get_rect()
        lossMPRect.topright = self.mpRect.topright

        screen.blit(self.hpWordImage, self.hpWordRect)
        screen.blit(self.mpWordImage, self.mpWordRect)
        screen.blit(self.hpImage, self.hpRect)
        screen.blit(self.mpImage, self.mpRect)
        screen.blit(lossHPImage, lossHPRect)
        screen.blit(lossMPImage, lossMPRect)

    class CubeBody(py.sprite.Sprite):
        def __init__(self):
            rate = 3  # 缩放比例,下同
            py.sprite.Sprite.__init__(self)
            self.speed = 1
            self.image = py.image.load('../素材库/人物素材/方块人/body/body_basic.png').convert_alpha()
            self.rect = self.image.get_rect()
            self.image = py.transform.smoothscale(self.image,
                                                  (int(self.rect.width / rate), int(self.rect.height / rate)))
            self.rect = self.image.get_rect()
            self.rect.topleft = (0, 0)  # 初始生成位置
            self.gravity = 2  # 重力系数为2,表示相邻每帧图像下降2个像素
            self.startJumpPos = 0
            self.on_ground = False
            self.jump = False
            self.jumpMaxDis = 100  # 最大跳跃高度(像素)
            self.lastPos = None
            self.nowPos = None

        def KeyControl(self, key):
            if key[py.K_d]:
                if key[py.K_LSHIFT]:
                    self.rect.x += self.speed + 1
                else:
                    self.rect.x += self.speed
            if key[py.K_a]:
                if key[py.K_LSHIFT]:
                    self.rect.x -= self.speed + 1
                else:
                    self.rect.x -= self.speed
            if key[py.K_SPACE] and self.on_ground:
                self.gravity = -4  # 跳越速度
                self.on_ground = False
                self.jump = True
                self.startJumpPos = self.rect.y

            self.lastPos = self.nowPos

        def update(self, floor, screen):

            if len(self.rect.collidelistall(floor.floorBody)) > 1:  # 与墙体碰撞检测
                self.rect.x = self.lastPos

            if self.rect.collidelist(floor.floorTop) == -1:  # 与地面碰撞检测
                self.on_ground = False

            if self.rect.right > screen.get_width() or self.rect.left < 0:  # 防止移动超越屏幕边缘
                self.rect.x = self.lastPos

            if not self.on_ground and not self.jump:
                self.rect.y += self.gravity
                if self.rect.collidelist(floor.floorTop) != -1:
                    self.on_ground = True

            if not self.on_ground and self.jump and (self.startJumpPos - self.rect.y) <= self.jumpMaxDis:
                self.rect.y += self.gravity
            else:
                self.jump = False
                self.gravity = 2
                self.startJumpPos = 0

            self.nowPos = self.rect.x

    class CubeEyes(py.sprite.Sprite):
        def __init__(self):
            rate = 2
            py.sprite.Sprite.__init__(self)
            self.image = py.image.load('../素材库/人物素材/方块人/eyes/eyes_black.png').convert_alpha()
            self.rect = self.image.get_rect()
            self.image = py.transform.smoothscale(self.image,
                                                  (int(self.rect.width / rate), int(self.rect.width / rate)))
            self.rect = self.image.get_rect()

        def EyesMove(self, pos, mouse_pos, eyeball):
            x0, y0 = pos[0], pos[1]
            x1, y1 = mouse_pos[0], mouse_pos[1]

            R = eyeball.rect.width / 2
            r = self.rect.width / 2
            sita_Angel = np.sign(y1 - y0) * np.pi / 2
            if x1 != x0:
                sita_Angel = np.arctan((y1 - y0) / (x1 - x0))
            x2, y2 = x0 + np.sign(x1 - x0) * R * np.cos(sita_Angel), y0 + np.sign(x1 - x0) * R * np.sin(sita_Angel)
            x3, y3 = x2 - np.sign(x1 - x0) * r * np.cos(sita_Angel), y2 - np.sign(x1 - x0) * r * np.sin(sita_Angel)
            return x3, y3

        def update(self, eyeball, body, screen):
            pos = eyeball.rect.center
            self.rect.center = pos
            rect = self.rect.copy()
            rect.center = (rect.center[0] + int(body.rect.width / 2), rect.center[1])

            '''鼠标控制眼睛方向'''
            mousePos = py.mouse.get_pos()
            x1, y1 = self.EyesMove(self.rect.center, mousePos, eyeball)
            x2, y2 = self.EyesMove(rect.center, mousePos, eyeball)
            self.rect.center = (int(x1), int(y1))
            rect.center = (int(x2), int(y2))
            screen.blit(self.image, rect)

    class CubeEyeball(py.sprite.Sprite):
        def __init__(self):
            rate = 2
            py.sprite.Sprite.__init__(self)
            self.image = py.image.load('../素材库/人物素材/方块人/eyeball/eyeball_basic.png').convert_alpha()
            self.rect = self.image.get_rect()
            self.image = py.transform.smoothscale(self.image,
                                                  (int(self.rect.width / rate), int(self.rect.width / rate)))
            self.rect = self.image.get_rect()

        def update(self, body, screen):
            pos = body.rect.center
            self.rect.center = (pos[0] - int(body.rect.width / 4), pos[1] - int(body.rect.height / 4))
            rect_right = self.rect.copy()
            rect_right.center = (rect_right.center[0] + int(body.rect.width / 2), rect_right.center[1])
            screen.blit(self.image, rect_right)

    class CubeMouth(py.sprite.Sprite):
        def __init__(self):
            rate = 3
            py.sprite.Sprite.__init__(self)
            self.image = py.image.load('../素材库/人物素材/方块人/mouth/mouth_basic.png').convert_alpha()
            self.rect = self.image.get_rect()
            self.image = py.transform.smoothscale(self.image,
                                                  (int(self.rect.width / rate), int(self.rect.height / rate)))
            self.rect = self.image.get_rect()

        def update(self, body):
            pos = body.rect.center
            self.rect.center = (pos[0], pos[1] + int(body.rect.height / 4))

    def update(self, floor, screen):
        self.DrawAttributes(screen)
        self.CubeBodyGroup.update(floor, screen)
        self.CubeBodyGroup.draw(screen)
        self.CubeEyeballGroup.update(self.cube_body, screen)
        self.CubeEyeballGroup.draw(screen)
        self.CubeMouthGroup.update(self.cube_body)
        self.CubeMouthGroup.draw(screen)
        self.CubeEyesGroup.update(self.cube_eyeball, self.cube_body, screen)
        self.CubeEyesGroup.draw(screen)

        这段代码看起来十分冗长,主要是因为创建了几个内部类来实现主角色关于眼睛,身体,嘴巴等部位的控制,通过修改每个部件,可以实现极为复杂的功能,这里只是简单的实现了一下眼睛的鼠标控制功能,即眼睛跟随鼠标移动,如果你想的话甚至嘴巴也能跟随鼠标移动(笑)。其次是通过键盘控制物体的前后移动,比如按住shift可以加快物体移动,按下空格可以实现物体的跳跃等。最后关于物体与场景之间的交互,比如判断物体是否处于地面上,是否达到了游戏边界等。还有就是角色的一些属性的生成,比如血条什么的。

场景模块

场景模块又分为以下几个小模块:

地板模块:

class FloorGen(object):
    def __init__(self, screen, cube):
        self.x = screen.get_width()
        self.y = screen.get_height()
        self.floorPos = []
        self.floorImage = []
        self.floorRect = []
        self.floorTop = []
        self.floorBody = []
        self.jumpMaxDis = cube.cube_body.jumpMaxDis
        self.roadLength = random.randint(20, 25)  # 游戏界面总长度
        '''地板自身参数设置
           分别表示单位地板宽度,高度'''
        self.width = int(self.x / 4)
        self.heightA = int(self.y / 25)
        self.heightB = int(self.heightA / 4)

    '''地板生成按照一定规律,相邻地板之间的最大距离差不超过方块的最大跳跃极限'''

    def floor_pos_gen(self):
        self.floorPos = [[(0, random.randint(int(self.y * (1 - 1 / 3)), self.y - self.heightB))]]
        for group in range(1, self.roadLength):
            body = [(group * self.width,
                     random.randint(max(self.floorPos[group - 1][0][1] - self.jumpMaxDis, self.y / 2),
                                    min(self.floorPos[group - 1][0][1] + self.jumpMaxDis, self.y - 5)))]

            self.floorPos.append(body)
        for floor in self.floorPos:
            num = int(np.ceil(self.y - floor[0][1] - self.heightB) / self.heightA) + 1
            head = floor[0][1] + self.heightB
            floor.append((floor[0][0], head))
            for member in range(1, num):
                nextHead = head + self.heightA * member
                floor.append((floor[0][0], nextHead))

    def floor_Image(self):
        floorImage = []
        for i in range(5):
            locals()['image'.format(i)] = py.image.load('../素材库/地板素材/floor_lv.png'.format(i)).convert_alpha()
            floorImage.append(eval('image'.format(i)))
            floorImage[i] = py.transform.smoothscale(floorImage[i], (self.width, self.heightA))
        floorImage[0] = py.transform.smoothscale(floorImage[0], (self.width, self.heightB))
        self.floorImage = floorImage

    def generator(self, screen):
        self.floor_pos_gen()
        self.floor_Image()
        for floor in self.floorPos:
            rectLs = []
            for i in range(len(floor)):
                floorPos = floor[i]
                image = [self.floorImage[-1] if i > len(self.floorImage) - 1 else self.floorImage[i]][0].copy()
                rect = image.get_rect()
                rect.topleft = floorPos
                screen.blit(image, rect)
                rectLs.append(rect)
                self.floorBody.append(rect)
            self.floorRect.append(rectLs)
            self.floorTop.append(rectLs[0])

    # 更新所有地板图像
    def update(self, screen):
        for floorL in self.floorRect:
            for i in range(len(floorL)):
                floorRect = floorL[i]
                floorImage = [self.floorImage[-1] if i > len(self.floorImage) - 1 else self.floorImage[i]][0].copy()
                screen.blit(floorImage, floorRect)

    def updatePre(self, cube, point):
        for floorL in self.floorRect:
            for floorRect in floorL:
                floorRect.x -= cube.cube_body.nowPos - cube.cube_body.lastPos
        cube.cube_body.rect.x = cube.cube_body.lastPos

        地板模块用来创建地面。本文通过随机生成一定长度的地面用来实现游戏场景的移动,同时相邻地板之间的高度不能超过主角色的极限跳跃高度,这是为了避免方块无法穿越游戏场景。

云模块:

class Cloud(py.sprite.Sprite):
    def __init__(self, screen, game_time):
        py.sprite.Sprite.__init__(self)
        cloudPath = '../素材库/云/'
        rate = 3
        self.speed = 1  # 移动速度
        self.genTime = game_time
        self.imageFiles = [os.path.join(cloudPath, i) for i in os.listdir(cloudPath)]
        self.images = [py.image.load(name) for name in self.imageFiles]
        self.rects = [image.get_rect() for image in self.images]
        self.images = [
            py.transform.smoothscale(self.images[i],
                                     (int(self.rects[i].width / rate), int(self.rects[i].height / rate)))
            for i in range(len(self.images))]
        self.index = random.randint(0, len(self.images) - 1)
        self.image = self.images[self.index]
        self.rect = self.image.get_rect()
        self.width = screen.get_width()
        self.height = screen.get_height()
        self.x = random.randint(int(self.width + self.rect.width / 2), int(self.width + self.rect.width / 2 + 100))
        self.y = random.randint(int(self.rect.height / 2), int(self.height / 4))
        self.rect.center = (self.x, self.y)

    def update(self):
        self.rect.x -= self.speed
        if self.rect.right < 0:
            self.kill()


# 控制云朵生成速率
def cloudGen(cloud_group, screen, game_time, ls):
    if len(cloud_group) == 0:
        cloud = Cloud(screen, game_time)
        cloud_group.add(cloud)
    elif 0 < len(cloud_group) < 3:
        spriteTime = list(cloud_group.spritedict.keys())[-1].genTime
        if len(ls) == 0:
            timeSlot = random.randint(3, 50)
            ls.append(timeSlot)
        else:
            timeSlot = ls[-1]
        timeWait = (game_time - spriteTime)
        if timeWait >= timeSlot:
            ls.pop(-1)
            cloud = Cloud(screen, game_time)
            cloud_group.add(cloud)

        这个模块用来生成一些云朵,同时按照一定的速度进入和离开游戏界面,这样使得场景看起来更加具有动感。(别说贴图太丑了,这都是用wps一个个设计出来的)

太阳和月亮:

class SunAndMoon(py.sprite.Sprite):
    def __init__(self):
        py.sprite.Sprite.__init__(self)
        self.rate = 3
        self.MoonFilePath = '../素材库/月亮/'
        self.imageRaw = py.image.load('../素材库/太阳/sun.png').convert_alpha()
        self.MoonFiles = [os.path.join(self.MoonFilePath, i) for i in os.listdir(self.MoonFilePath)]
        self.imageMoonRaw = [py.image.load(name).convert_alpha() for name in self.MoonFiles]
        self.MoonRect = [image.get_rect() for image in self.imageMoonRaw]
        self.imageMoon = [py.transform.smoothscale(self.imageMoonRaw[i], (int(self.MoonRect[i].width / self.rate),
                                                                          int(self.MoonRect[i].width / self.rate))) for
                          i in range(len(self.imageMoonRaw))]
        self.MoonRect = [image.get_rect() for image in self.imageMoonRaw]
        self.rect = self.imageRaw.get_rect()
        self.imageRaw = py.transform.smoothscale(self.imageRaw,
                                                 (int(self.rect.width / self.rate), int(self.rect.width / self.rate)))
        self.rect = self.imageRaw.get_rect()
        self.y = self.rect.height
        self.image = self.imageRaw.copy()
        self.time = 0

    def update(self, game_time, screen):
        hour, minute = divmod(game_time, 60)
        if hour <= 9:
            self.time += 1
            self.image = py.transform.rotate(self.imageRaw, self.time).convert_alpha()
            self.rect = self.image.get_rect()
            self.rect.center = (int(screen.get_width() / 2), int(self.y / 2))
        elif 17 >= hour > 9:
            self.image = self.imageMoon[int(hour) - 10]
            self.rect = self.MoonRect[int(hour) - 10]
            self.rect.center = (int(screen.get_width() / 2), int(self.y / 2))

        这个模块实现了太阳的旋转,月亮的生成。

逻辑模块

# 移动游戏镜头
def moveCamera(cube, floor, screen):
    rate = 0.70  # 镜头移动触发区域为屏幕右端(1-rate)/2范围,和屏幕左端(1-rate)/2范围
    triggerPointRight = screen.get_width() * (0.5 + rate / 2)
    triggerPointLeft = screen.get_width() * (1 - rate) / 2
    if cube.cube_body.rect.right > triggerPointRight:
        if floor.floorTop[0].left > -floor.roadLength * floor.width + screen.get_width():
            floor.updatePre(cube, triggerPointRight)

    elif cube.cube_body.rect.left < triggerPointLeft:
        if floor.floorTop[-1].right < floor.roadLength * floor.width:
            floor.updatePre(cube, triggerPointLeft)

    floor.update(screen)

        这个模块用来控制屏幕的镜头的移动,所谓的移动实际上是相对的,因为屏幕本身不会移动因此只能通过移动地板来实现,通过创建两条边界线来检测触发移动,这里我同时设置无法移动镜头的情况,以免超过了游戏地板的生成界限。

        启动程序:

def Main():
    py.init()
    FPS = 200  # 游戏帧率
    width, height = 800, 600  # 设置窗口大小
    py.display.set_caption('THE BOX')  # 设置游戏名称
    screen = py.display.set_mode((width, height))
    clock = py.time.Clock()

    backGround = BackgroundWall(screen)
    bgGroup = py.sprite.Group()
    bgGroup.add(backGround)

    cube = CubeHuman(screen)

    sunAndMoon = SunAndMoon()
    sunGroup = py.sprite.Group()
    sunGroup.add(sunAndMoon)

    floor = FloorGen(screen, cube)
    floor.generator(screen)

    cloudGroup = py.sprite.Group()
    cloudUpdateTimeLs = []

    while True:
        GameTime = ControlTime()
        bgGroup.update(GameTime)
        bgGroup.draw(screen)

        sunGroup.update(GameTime, screen)
        sunGroup.draw(screen)

        cloudGen(cloudGroup, screen, GameTime, cloudUpdateTimeLs)
        cloudGroup.update()
        cloudGroup.draw(screen)

        key = py.key.get_pressed()  # 获取键盘输入

        cube.cube_body.KeyControl(key)
        cube.cube_body.update(floor, screen)

        moveCamera(cube, floor, screen)  # 包含了地面刷新程序

        if key[py.K_ESCAPE]: exit()  # 退出设置
        for event in py.event.get():
            if event.type == py.QUIT:
                py.quit()
                sys.exit()

        ''''''
        cube.update(floor, screen)
        py.display.update()  # 刷新
        clock.tick(FPS)


if __name__ == '__main__':
    Main()

        启动程序中各位模块创建和刷新的先后顺序一定不要搞错。以上就是本文的全部的内容了,根据以往经验,我知道这篇文章大概率没什么人看,主要是为了以防万一哪天我想捡起来看看,故作此文。最后按照惯例给个懒人包:The Box 提取码:s2gj

PS:有时间我再去修改修改,比如写个启动界面什么的(恼),加个音乐包什么的,自动创建一个爬虫什么的(笑),也可能会写一个人工智能代替我玩(悲)。

pygame游戏检测矩形是否碰撞指定颜色的自定义函数(仅5行代码)

游戏中经常要检测角色之间的碰撞。pygame提供了各种图形之间碰撞检测,似乎并没有一个检测矩形与某颜色碰撞的函数。本文用pygame自定义一个函数,完成矩形与某颜色碰撞的检测。
pygame语句screen=pygame.display.set_mode((400,300))创建1个Surface实例作为显示窗体,这个screen水平方向400点,垂直方向300点的一个矩形,可用一个2维数组描述。在screen上可以画各种图形(如线和矩形)或Surface实例,先画的图形将被后画图形覆盖。完成1帧图像后,将screen拷贝到屏幕。如编写这样一个程序,在窗体两侧各画了1条黑色竖线,两线之间画一个小矩形,令其沿x轴方向不停移动,每当碰到黑线,反向运动。那么如何判断小矩形是否碰到黑色(线)呢?可在把小矩形画到screen前,检测在screen上小矩形所在新位置内是否有黑色点。具体方法是将screen每点颜色值保存到一个2维数组,从这个2维数组中创建一个矩形切片,矩形切片的位置和大小是小矩形在screen上新位置,然后判断该切片中是否有黑色的点。实际实现代码如下。

import pygame
def collide_color(aSurface,aRect,aColor):
    pixel=pygame.PixelArray(aSurface)#锁定aSurface,将各点颜色保存在2维数组
    aPixel=pixel[aRect.x:aRect.x+aRect.width,aRect.y:aRect.y+aRect.height]
    pygame.PixelArray.close(pixel) #解锁aSurface并删除数组,上句得到数组切片
    return aColor in aPixel#指定颜色在切片中返回真,两者颜色应同为rgb或rgba

函数的参数1是一个Surface实例,这里是上文创建的screen;参数2是矩形移动后新位置;参数3是要检测的颜色。第1条语句锁定aSurface,即不允许在aSurface画图形,并将aSurface上各点颜色值保存在一个2维数组中,注意数组是PixelArray实例,不是python的列表。第2条语句是做数组切片,切片范围是矩形在aSurface上新位置。第3条语句解锁aSurface对象,并删除第1条语句创建的2维数组。最后函数返回一个布尔值,即判断aColor颜色是否在切片数组中,两者颜色格式应相同,同是rgb或rgba。注意,很多网上文章介绍第3条语句使用del Pixel解锁,pygame1.9.4版本前是可以的,pygame1.9.4版本后,用del Pixel,不能解锁aSurface对象,但不报错。特别注意必须在画小矩形前检测,因画了小矩形后将会覆盖黑线,就无法检测了。另外要检测的黑色线必须在检查前画到screen上,因此在游戏窗体画图形的顺序十分重要。
本例并非必须采用检测矩形碰撞颜色方法,例如把两条黑线换成很窄Surface实例并填充黑色,就可以采用检测矩形碰撞矩形方法。但是如所画是任意黑色曲线,似乎只能采用检测矩形碰撞颜色方法了。
小矩形在两条黑色竖线间往返的程序运行效果图如下。

小矩形在两条黑色竖线间往返的完整程序如下。

import pygame
def collide_color(aSurface,aRect,aColor):
    pixel=pygame.PixelArray(aSurface)               #锁定aSurface,将各点颜色保存在2维数组
    aPixel=pixel[aRect.x:aRect.x+aRect.width,aRect.y:aRect.y+aRect.height]  #得到数组切片
    pygame.PixelArray.close(pixel)                  #解锁aSurface并删除数组
    return aColor in aPixel     #指定颜色在切片中返回真,否则返回假。两者颜色应同为rgb或rgba
bgcolor = pygame.Color('white')
red=pygame.Color('red')
black=pygame.Color('black')
pygame.init()
size = width, height = 200,100
screen = pygame.display.set_mode(size)
pygame.display.set_caption("检测矩形和颜色的碰撞")
rect = pygame.Rect( 100 ,35, 20, 20 )               #所画小矩形
dx=10                                               #小矩形移动速度
fclock = pygame.time.Clock()
fps = 4
angle=0
running = True
while running:    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False        
    screen.fill(bgcolor)                                    #背景色
    pygame.draw.line(screen,black,(10,10),(10,90),5)        #画窗体两侧的两条黑线
    pygame.draw.line(screen,black,(190,10),(190,90),5)      #必须在检测前完成画黑线
    rect.x+=dx                                              #小矩形移到新位置
    if collide_color(screen,rect,black):                    #开始检测,必须在画矩形前检测
        dx=-(dx)                                            #碰到黑色线,速度反向
    pygame.draw.rect(screen, red, rect)                     #画小矩形
    pygame.display.update()                           
    fclock.tick(fps)
pygame.quit()

以上是关于基于pygame的自定义游戏《the box》的主要内容,如果未能解决你的问题,请参考以下文章

pygame游戏检测矩形是否碰撞指定颜色的自定义函数(仅5行代码)

什么是pygame?怎样安装使用?

python编写游戏怎么打包——详解python+pygame游戏开发之使用Py2exe打包游戏为exe文件

python编写游戏怎么打包——详解python+pygame游戏开发之使用Py2exe打包游戏为exe文件

python编写游戏怎么打包——详解python+pygame游戏开发之使用Py2exe打包游戏为exe文件

《结对编项目作业名称-设计文档》