栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

pygame实现自定义游戏《the box》

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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        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)

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

        启动程序:

# 时间在0-34之间def ControlTime():    gameTime = np.floor(py.time.get_ticks() / 1000)    hour = np.floor(gameTime / 60)    if hour <= 34:        return gameTime    elif hour > 34:        loop, timeM = divmod(gameTime, 34 * 60)        return timeMdef 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:有时间我再去修改修改,比如写个启动界面什么的(恼),加个音乐包什么的,自动创建一个爬虫什么的(笑),也可能会写一个人工智能代替我玩(悲)。

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1026587.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号