Pygame实战练习之炸弹人学院游戏
炸弹人学院想必是很多人童年时期的经典游戏,我们依旧能记得抱个老人机娱乐的场景,下面这篇文章主要给大家介绍了关于如何利用python写一个简单的炸弹人学院小游戏的相关资料,需要的朋友可以参考下
导语
在现在这个浮躁的年代:小编每次登陆王者荣耀,还有每次登陆刺激战场Z!
看着里面的聊天界面,各种代打、各种的找cp。小小编觉得,我们已经失去了玩游戏的初心。
接下来,小台将带领你们走回童年时光,一起领略我们当初玩4399的单纯与天真!
还记得小时候小台每到放学时刻,就会拉着只比我小半岁的小表妹,一块去亲戚家里玩电脑
每一次打开电脑做的第一件事情就是,打开浏览器,输入4399这四个数字,那个时候觉得hao123真是一个神奇的主页!
可以让我打开4399玩各种游戏qwq,尤其是Q版泡泡堂深得我心!
那今天就带大家回忆一下童年也做一款经典的炸弹人的小游戏!
正文
游戏规则还清楚哈, 我就不多做介绍了不清楚玩法的可以百度下下!
首先准备好相应的素材:【部分如下】
炸弹人主程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | import sys import cfg import random import pygame from modules import * '''游戏主程序''' def main(cfg): # 初始化 pygame.init() pygame.mixer.init() pygame.mixer.music.load(cfg.BGMPATH) pygame.mixer.music.play( - 1 , 0.0 ) screen = pygame.display.set_mode(cfg.SCREENSIZE) pygame.display.set_caption( '炸弹人——源码基地:#959755565#' ) # 开始界面 Interface(screen, cfg, mode = 'game_start' ) # 游戏主循环 font = pygame.font.SysFont( 'Consolas' , 15 ) for gamemap_path in cfg.GAMEMAPPATHS: # -地图 map_parser = mapParser(gamemap_path, bg_paths = cfg.BACKGROUNDPATHS, wall_paths = cfg.WALLPATHS, blocksize = cfg.BLOCKSIZE) # -水果 fruit_sprite_group = pygame.sprite.Group() used_spaces = [] for i in range ( 5 ): coordinate = map_parser.randomGetSpace(used_spaces) used_spaces.append(coordinate) fruit_sprite_group.add(Fruit(random.choice(cfg.FRUITPATHS), coordinate = coordinate, blocksize = cfg.BLOCKSIZE)) # -我方Hero coordinate = map_parser.randomGetSpace(used_spaces) used_spaces.append(coordinate) ourhero = Hero(imagepaths = cfg.HEROZELDAPATHS, coordinate = coordinate, blocksize = cfg.BLOCKSIZE, map_parser = map_parser, hero_name = 'ZELDA' ) # -电脑Hero aihero_sprite_group = pygame.sprite.Group() coordinate = map_parser.randomGetSpace(used_spaces) aihero_sprite_group.add(Hero(imagepaths = cfg.HEROBATMANPATHS, coordinate = coordinate, blocksize = cfg.BLOCKSIZE, map_parser = map_parser, hero_name = 'BATMAN' )) used_spaces.append(coordinate) coordinate = map_parser.randomGetSpace(used_spaces) aihero_sprite_group.add(Hero(imagepaths = cfg.HERODKPATHS, coordinate = coordinate, blocksize = cfg.BLOCKSIZE, map_parser = map_parser, hero_name = 'DK' )) used_spaces.append(coordinate) # -炸弹bomb bomb_sprite_group = pygame.sprite.Group() # -用于判断游戏胜利或者失败的flag is_win_flag = False # -主循环 screen = pygame.display.set_mode(map_parser.screen_size) clock = pygame.time.Clock() while True : dt = clock.tick(cfg.FPS) for event in pygame.event.get(): if event. type = = pygame.QUIT: pygame.quit() sys.exit( - 1 ) # --↑↓←→键控制上下左右, 空格键丢炸弹 elif event. type = = pygame.KEYDOWN: if event.key = = pygame.K_UP: ourhero.move( 'up' ) elif event.key = = pygame.K_DOWN: ourhero.move( 'down' ) elif event.key = = pygame.K_LEFT: ourhero.move( 'left' ) elif event.key = = pygame.K_RIGHT: ourhero.move( 'right' ) elif event.key = = pygame.K_SPACE: if ourhero.bomb_cooling_count < = 0 : bomb_sprite_group.add(ourhero.generateBomb(imagepath = cfg.BOMBPATH, digitalcolor = cfg.YELLOW, explode_imagepath = cfg.FIREPATH)) screen.fill(cfg.WHITE) # --电脑Hero随机行动 for hero in aihero_sprite_group: action, flag = hero.randomAction(dt) if flag and action = = 'dropbomb' : bomb_sprite_group.add(hero.generateBomb(imagepath = cfg.BOMBPATH, digitalcolor = cfg.YELLOW, explode_imagepath = cfg.FIREPATH)) # --吃到水果加生命值(只要是Hero, 都能加) ourhero.eatFruit(fruit_sprite_group) for hero in aihero_sprite_group: hero.eatFruit(fruit_sprite_group) # --游戏元素都绑定到屏幕上 map_parser.draw(screen) for bomb in bomb_sprite_group: if not bomb.is_being: bomb_sprite_group.remove(bomb) explode_area = bomb.draw(screen, dt, map_parser) if explode_area: # --爆炸火焰范围内的Hero生命值将持续下降 if ourhero.coordinate in explode_area: ourhero.health_value - = bomb.harm_value for hero in aihero_sprite_group: if hero.coordinate in explode_area: hero.health_value - = bomb.harm_value fruit_sprite_group.draw(screen) for hero in aihero_sprite_group: hero.draw(screen, dt) ourhero.draw(screen, dt) # --左上角显示生命值 pos_x = showText(screen, font, text = ourhero.hero_name + '(our):' + str (ourhero.health_value), color = cfg.YELLOW, position = [ 5 , 5 ]) for hero in aihero_sprite_group: pos_x, pos_y = pos_x + 15 , 5 pos_x = showText(screen, font, text = hero.hero_name + '(ai):' + str (hero.health_value), color = cfg.YELLOW, position = [pos_x, pos_y]) # --我方玩家生命值小于等于0/电脑方玩家生命值均小于等于0则判断游戏结束 if ourhero.health_value < = 0 : is_win_flag = False break for hero in aihero_sprite_group: if hero.health_value < = 0 : aihero_sprite_group.remove(hero) if len (aihero_sprite_group) = = 0 : is_win_flag = True break pygame.display.update() clock.tick(cfg.FPS) if is_win_flag: Interface(screen, cfg, mode = 'game_switch' ) else : break Interface(screen, cfg, mode = 'game_end' ) '''run''' if __name__ = = '__main__' : while True : main(cfg) |
开始的界面如下:
定义地图类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | class mapParser(): def __init__( self , mapfilepath, bg_paths, wall_paths, blocksize, * * kwargs): self .instances_list = self .__parse(mapfilepath) self .bg_paths = bg_paths self .wall_paths = wall_paths self .blocksize = blocksize self .height = len ( self .instances_list) self .width = len ( self .instances_list[ 0 ]) self .screen_size = (blocksize * self .width, blocksize * self .height) '''地图画到屏幕上''' def draw( self , screen): for j in range ( self .height): for i in range ( self .width): instance = self .instances_list[j][i] if instance = = 'w' : elem = Wall( self .wall_paths[ 0 ], [i, j], self .blocksize) elif instance = = 'x' : elem = Wall( self .wall_paths[ 1 ], [i, j], self .blocksize) elif instance = = 'z' : elem = Wall( self .wall_paths[ 2 ], [i, j], self .blocksize) elif instance = = '0' : elem = Background( self .bg_paths[ 0 ], [i, j], self .blocksize) elif instance = = '1' : elem = Background( self .bg_paths[ 1 ], [i, j], self .blocksize) elif instance = = '2' : elem = Background( self .bg_paths[ 2 ], [i, j], self .blocksize) else : raise ValueError( 'instance parse error in mapParser.draw...' ) elem.draw(screen) '''随机获取一个空地''' def randomGetSpace( self , used_spaces = None ): while True : i = random.randint( 0 , self .width - 1 ) j = random.randint( 0 , self .height - 1 ) coordinate = [i, j] if used_spaces and coordinate in used_spaces: continue instance = self .instances_list[j][i] if instance in [ '0' , '1' , '2' ]: break return coordinate '''根据坐标获取元素类型''' def getElemByCoordinate( self , coordinate): return self .instances_list[coordinate[ 1 ]][coordinate[ 0 ]] '''解析.map文件''' def __parse( self , mapfilepath): instances_list = [] with open (mapfilepath) as f: for line in f.readlines(): instances_line_list = [] for c in line: if c in [ 'w' , 'x' , 'z' , '0' , '1' , '2' ]: instances_line_list.append(c) instances_list.append(instances_line_list) return instances_list |
定义必要的一些精灵类:角色,水果等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | '''墙类''' class Wall(pygame.sprite.Sprite): def __init__( self , imagepath, coordinate, blocksize, * * kwargs): pygame.sprite.Sprite.__init__( self ) self .image = pygame.image.load(imagepath) self .image = pygame.transform.scale( self .image, (blocksize, blocksize)) self .rect = self .image.get_rect() self .rect.left, self .rect.top = coordinate[ 0 ] * blocksize, coordinate[ 1 ] * blocksize self .coordinate = coordinate self .blocksize = blocksize '''画到屏幕上''' def draw( self , screen): screen.blit( self .image, self .rect) return True '''背景类''' class Background(pygame.sprite.Sprite): def __init__( self , imagepath, coordinate, blocksize, * * kwargs): pygame.sprite.Sprite.__init__( self ) self .image = pygame.image.load(imagepath) self .image = pygame.transform.scale( self .image, (blocksize, blocksize)) self .rect = self .image.get_rect() self .rect.left, self .rect.top = coordinate[ 0 ] * blocksize, coordinate[ 1 ] * blocksize self .coordinate = coordinate self .blocksize = blocksize '''画到屏幕上''' def draw( self , screen): screen.blit( self .image, self .rect) return True '''水果类''' class Fruit(pygame.sprite.Sprite): def __init__( self , imagepath, coordinate, blocksize, * * kwargs): pygame.sprite.Sprite.__init__( self ) self .kind = imagepath.split( '/' )[ - 1 ].split( '.' )[ 0 ] if self .kind = = 'banana' : self .value = 5 elif self .kind = = 'cherry' : self .value = 10 else : raise ValueError( 'Unknow fruit %s...' % self .kind) self .image = pygame.image.load(imagepath) self .image = pygame.transform.scale( self .image, (blocksize, blocksize)) self .rect = self .image.get_rect() self .rect.left, self .rect.top = coordinate[ 0 ] * blocksize, coordinate[ 1 ] * blocksize self .coordinate = coordinate self .blocksize = blocksize '''画到屏幕上''' def draw( self , screen): screen.blit( self .image, self .rect) return True '''炸弹类''' class Bomb(pygame.sprite.Sprite): def __init__( self , imagepath, coordinate, blocksize, digitalcolor, explode_imagepath, * * kwargs): pygame.sprite.Sprite.__init__( self ) self .image = pygame.image.load(imagepath) self .image = pygame.transform.scale( self .image, (blocksize, blocksize)) self .explode_imagepath = explode_imagepath self .rect = self .image.get_rect() # 像素位置 self .rect.left, self .rect.top = coordinate[ 0 ] * blocksize, coordinate[ 1 ] * blocksize # 坐标(元素块为单位长度) self .coordinate = coordinate self .blocksize = blocksize # 爆炸倒计时 self .explode_millisecond = 6000 * 1 - 1 self .explode_second = int ( self .explode_millisecond / 1000 ) self .start_explode = False # 爆炸持续时间 self .exploding_count = 1000 * 1 # 炸弹伤害能力 self .harm_value = 1 # 该炸弹是否还存在 self .is_being = True self .font = pygame.font.SysFont( 'Consolas' , 20 ) self .digitalcolor = digitalcolor '''画到屏幕上''' def draw( self , screen, dt, map_parser): if not self .start_explode: # 爆炸倒计时 self .explode_millisecond - = dt self .explode_second = int ( self .explode_millisecond / 1000 ) if self .explode_millisecond < 0 : self .start_explode = True screen.blit( self .image, self .rect) text = self .font.render( str ( self .explode_second), True , self .digitalcolor) rect = text.get_rect(center = ( self .rect.centerx - 5 , self .rect.centery + 5 )) screen.blit(text, rect) return False else : # 爆炸持续倒计时 self .exploding_count - = dt if self .exploding_count > 0 : return self .__explode(screen, map_parser) else : self .is_being = False return False '''爆炸效果''' def __explode( self , screen, map_parser): explode_area = self .__calcExplodeArea(map_parser.instances_list) for each in explode_area: image = pygame.image.load( self .explode_imagepath) image = pygame.transform.scale(image, ( self .blocksize, self .blocksize)) rect = image.get_rect() rect.left, rect.top = each[ 0 ] * self .blocksize, each[ 1 ] * self .blocksize screen.blit(image, rect) return explode_area '''计算爆炸区域''' def __calcExplodeArea( self , instances_list): explode_area = [] # 区域计算规则为墙可以阻止爆炸扩散, 且爆炸范围仅在游戏地图范围内 for ymin in range ( self .coordinate[ 1 ], self .coordinate[ 1 ] - 5 , - 1 ): if ymin < 0 or instances_list[ymin][ self .coordinate[ 0 ]] in [ 'w' , 'x' , 'z' ]: break explode_area.append([ self .coordinate[ 0 ], ymin]) for ymax in range ( self .coordinate[ 1 ] + 1 , self .coordinate[ 1 ] + 5 ): if ymax > = len (instances_list) or instances_list[ymax][ self .coordinate[ 0 ]] in [ 'w' , 'x' , 'z' ]: break explode_area.append([ self .coordinate[ 0 ], ymax]) for xmin in range ( self .coordinate[ 0 ], self .coordinate[ 0 ] - 5 , - 1 ): if xmin < 0 or instances_list[ self .coordinate[ 1 ]][xmin] in [ 'w' , 'x' , 'z' ]: break explode_area.append([xmin, self .coordinate[ 1 ]]) for xmax in range ( self .coordinate[ 0 ] + 1 , self .coordinate[ 0 ] + 5 ): if xmax > = len (instances_list[ 0 ]) or instances_list[ self .coordinate[ 1 ]][xmax] in [ 'w' , 'x' , 'z' ]: break explode_area.append([xmax, self .coordinate[ 1 ]]) return explode_area '''角色类''' class Hero(pygame.sprite.Sprite): def __init__( self , imagepaths, coordinate, blocksize, map_parser, * * kwargs): pygame.sprite.Sprite.__init__( self ) self .imagepaths = imagepaths self .image = pygame.image.load(imagepaths[ - 1 ]) self .image = pygame.transform.scale( self .image, (blocksize, blocksize)) self .rect = self .image.get_rect() self .rect.left, self .rect.top = coordinate[ 0 ] * blocksize, coordinate[ 1 ] * blocksize self .coordinate = coordinate self .blocksize = blocksize self .map_parser = map_parser self .hero_name = kwargs.get( 'hero_name' ) # 生命值 self .health_value = 50 # 炸弹冷却时间 self .bomb_cooling_time = 5000 self .bomb_cooling_count = 0 # 随机移动冷却时间(仅AI电脑用) self .randommove_cooling_time = 100 self .randommove_cooling_count = 0 '''角色移动''' def move( self , direction): self .__updateImage(direction) if direction = = 'left' : if self .coordinate[ 0 ] - 1 < 0 or self .map_parser.getElemByCoordinate([ self .coordinate[ 0 ] - 1 , self .coordinate[ 1 ]]) in [ 'w' , 'x' , 'z' ]: return False self .coordinate[ 0 ] = self .coordinate[ 0 ] - 1 elif direction = = 'right' : if self .coordinate[ 0 ] + 1 > = self .map_parser.width or self .map_parser.getElemByCoordinate([ self .coordinate[ 0 ] + 1 , self .coordinate[ 1 ]]) in [ 'w' , 'x' , 'z' ]: return False self .coordinate[ 0 ] = self .coordinate[ 0 ] + 1 elif direction = = 'up' : if self .coordinate[ 1 ] - 1 < 0 or self .map_parser.getElemByCoordinate([ self .coordinate[ 0 ], self .coordinate[ 1 ] - 1 ]) in [ 'w' , 'x' , 'z' ]: return False self .coordinate[ 1 ] = self .coordinate[ 1 ] - 1 elif direction = = 'down' : if self .coordinate[ 1 ] + 1 > = self .map_parser.height or self .map_parser.getElemByCoordinate([ self .coordinate[ 0 ], self .coordinate[ 1 ] + 1 ]) in [ 'w' , 'x' , 'z' ]: return False self .coordinate[ 1 ] = self .coordinate[ 1 ] + 1 else : raise ValueError( 'Unknow direction %s...' % direction) self .rect.left, self .rect.top = self .coordinate[ 0 ] * self .blocksize, self .coordinate[ 1 ] * self .blocksize return True '''随机行动(AI电脑用)''' def randomAction( self , dt): # 冷却倒计时 if self .randommove_cooling_count > 0 : self .randommove_cooling_count - = dt action = random.choice([ 'left' , 'left' , 'right' , 'right' , 'up' , 'up' , 'down' , 'down' , 'dropbomb' ]) flag = False if action in [ 'left' , 'right' , 'up' , 'down' ]: if self .randommove_cooling_count < = 0 : flag = True self .move(action) self .randommove_cooling_count = self .randommove_cooling_time elif action in [ 'dropbomb' ]: if self .bomb_cooling_count < = 0 : flag = True self .bomb_cooling_count = self .bomb_cooling_time return action, flag '''生成炸弹''' def generateBomb( self , imagepath, digitalcolor, explode_imagepath): return Bomb(imagepath = imagepath, coordinate = copy.deepcopy( self .coordinate), blocksize = self .blocksize, digitalcolor = digitalcolor, explode_imagepath = explode_imagepath) '''画到屏幕上''' def draw( self , screen, dt): # 冷却倒计时 if self .bomb_cooling_count > 0 : self .bomb_cooling_count - = dt screen.blit( self .image, self .rect) return True '''吃水果''' def eatFruit( self , fruit_sprite_group): eaten_fruit = pygame.sprite.spritecollide( self , fruit_sprite_group, True , None ) for fruit in eaten_fruit: self .health_value + = fruit.value '''更新角色朝向''' def __updateImage( self , direction): directions = [ 'left' , 'right' , 'up' , 'down' ] idx = directions.index(direction) self .image = pygame.image.load( self .imagepaths[idx]) self .image = pygame.transform.scale( self .image, ( self .blocksize, self .blocksize)) |
效果如下:
这精致的画面还可以吧~哈哈哈啊 快夸我快夸我~
总结
安啦!文章就写到这里,你们的支持是我最大的动力,记得三连哦~木啊!
温馨提示:不要忘记点赞、关注、评论哦!看我手上的炸弹~
到此这篇关于Pygame实战练习之炸弹人学院游戏的文章就介绍到这了
原文链接:https://blog.csdn.net/weixin_55822277/article/details/120415243