PyGame做了一个扫雷
效果
- 绿色的F表示右键点击插旗的地方
- 红色的格子表示点击到了地雷
原理
主时间循环
基本上GUI应用都会有一个主循环,用来接收各种事件,并按照时间类型进行不同的处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | while True : for event in pygame.event.get(): if event. type = = pygame.MOUSEBUTTONUP: if game.is_over(): init_game( 30 ) continue pos = event.pos if event.button = = 1 : game.clicked(pos) elif event.button = = 3 : game.right_clicked(pos) # 获取当前那个格子被点击了 if event. type = = pygame.QUIT: sys.exit( 0 ) pygame.display.update() |
主要自定义处理类
- class Game
- class Square
Game
表示一次游戏。当前游戏中所有的地雷分布;保存所有的格子信息;以及每个格子周围的地雷数量。Game点击后需要判断是否是否游戏结束。
如何获胜:
- 所有的雷都被标注
- 已经没有空白的格子可以点了
Square
表示一个格子,初始化的时候会传入是否包含地雷的信息,格子作为一圈的地雷数量。
Square本身含有一个状态机,根据点击状态和点击方式画出各种样式(空白,带数字,F,红色)等。
主循环中的事件会传给Game,最终根据位置传个某个Square。
PyGame
pygame.Screen 使用pygame.display.set_mode返回一个画布,然后可以将各种Rect,font之类的放到这画布上。
1 | pygame.display.set_mode((total_width, total_height)) |
pygame.Rect 输出格子
pygame.font 输出字体
重点方法
- pygame.Screen.blit 将绘制的内容叠放到屏幕上。
1 2 3 4 | self .face = pygame.Surface(( self .rect.width, self .rect.height)) self .face.fill( 'white' ) game.get_screen().blit( self .face, ( self .rect.left, self .rect.top)) pygame.draw.rect( self .surface, 'gray' , self .rect, 1 ) |
- pygame.display.update 刷新页面
参考
- https://www.pygame.org/news
代码
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 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | # 这是一个示例 Python 脚本。 # 按 ⌃R 执行或将其替换为您的代码。 # 按 双击 ⇧ 在所有地方搜索类、文件、工具窗口、操作和设置。 import sys import pygame import random game = None BOMB_COUNT = 80 class Game( object ): screen = None row_count = 0 bomb_location = [] squares_list = [] squares_val_dict = {} win = False def __init__( self , count): pygame.init() total_width = count * 18 + 100 * 2 total_height = count * 18 + 100 * 2 self .row_count = count self .screen = pygame.display.set_mode((total_width, total_height)) self .win = False self .bomb_location = [] self .squares_list = [] self .squares_val_dict = {} def __del__( self ): print ( 'del game' ) pygame.display.quit() def is_over( self ): return self .win def get_screen( self ): return self .screen # 初始化随机产生炸弹 def random_bomb( self ): # 随机产生炸弹 for i in range (BOMB_COUNT): while 1 : x = random.randint( 0 , 29 ) y = random.randint( 0 , 29 ) if (x, y) in self .bomb_location: continue else : self .bomb_location.append((x, y)) break # 计算某个位置范围的数字 for x in range ( self .row_count): for y in range ( self .row_count): count = 0 if (x - 1 , y) in self .bomb_location: count + = 1 if (x - 1 , y - 1 ) in self .bomb_location: count + = 1 if (x - 1 , y + 1 ) in self .bomb_location: count + = 1 if (x , y - 1 ) in self .bomb_location: count + = 1 if (x , y) in self .bomb_location: count + = 1 if (x , y + 1 ) in self .bomb_location: count + = 1 if (x + 1 , y - 1 ) in self .bomb_location: count + = 1 if (x + 1 , y) in self .bomb_location: count + = 1 if (x + 1 , y + 1 ) in self .bomb_location: count + = 1 # print('%s,%s %d' % (x, y, count)) self .squares_val_dict[(x, y)] = count def init_square( self ): for i in range ( self .row_count): self .squares_list.append([]) top = 100 + i * 18 for j in range ( self .row_count): left = 100 + j * 18 width = 18 height = 18 exist = False if (i, j) in self .bomb_location: # print('%s,%s exists' % (j, i)) exist = True # 周围的炸弹数量 around_count = self .squares_val_dict.get((i, j), 0 ) # print('init square %s,%s %d' % (i, j, around_count)) self .squares_list[i].append(Square(exist, around_count, self .screen, left, top, width, height)) self .squares_list[i][j].draw() pygame.display.update() def start_game( self ): pass # 胜利后显示的文字 def display_win( self ): font = pygame.font.SysFont( "Andale Mono" , 32 ) txt = font.render( "Winner Winner Winner" , True , 'Red' ) self .get_screen().blit(txt, ( 200 , 0 )) font = pygame.font.SysFont( "Andale Mono" , 16 ) txt = font.render( "uploading to dashboard..." , True , 'green' ) self .get_screen().blit(txt, ( 260 , 40 )) font = pygame.font.SysFont( "Andale Mono" , 16 ) txt = font.render( "click to continue..." , True , 'gray' ) self .get_screen().blit(txt, ( 280 , 60 )) self .win = True def display_flag( self ): for (x, y) in self .bomb_location: square = self .squares_list[x][y] square.right_click() square.draw() # 根据所有的旗帜来判断胜利 def check_win_by_flag( self ): for (x, y) in self .bomb_location: square = self .squares_list[x][y] if square.state = = 'flag' and square.exist: continue return False self .display_win() return True # 根据已经没有格子点击了来判断胜利 def check_win_by_click( self ): # print('check by click') for x in range ( self .row_count): for y in range ( self .row_count): square = self .squares_list[x][y] if square.state = = 'blank' or square.exist: # print(1) continue return False self .display_flag() self .display_win() return True def right_clicked( self , pos): left = pos[ 0 ] top = pos[ 1 ] x = int ((top - 100 ) / 18 ) y = int ((left - 100 ) / 18 ) # print('right clicked %s, %s' % (x, y)) if x in range ( 0 , self .row_count) and y in range ( 0 , self .row_count): square = self .squares_list[x][y] if not square.right_click(): return # 表示右键生效 square.draw() if square.state = = 'flag' and square.exist: # 只有当前标记是正确的时候才判断 # 判断是否已经将所有的炸弹标记出来 self .check_win_by_flag() pygame.display.update() def clicked( self , pos): left = pos[ 0 ] top = pos[ 1 ] x = int ((top - 100 ) / 18 ) y = int ((left - 100 ) / 18 ) def click_square( self , x, y): if x not in range ( 0 , self .row_count) or y not in range ( 0 , self .row_count): return False square = self .squares_list[x][y] if square.state ! = 'new' : return False if not square.click(): return False square.draw() if square.around_count = = 0 : # print('around is 0') for (x1, y1) in [ (x - 1 , y), (x - 1 , y - 1 ), (x - 1 , y + 1 ), (x, y - 1 ), (x, y), (x, y + 1 ), (x + 1 , y - 1 ), (x + 1 , y), (x + 1 , y + 1 ), ]: click_square( self , x1, y1) return True if x in range ( 0 , self .row_count) and y in range ( 0 , self .row_count): if click_square( self , x, y): # 判断是否成功 self .check_win_by_click() pygame.display.update() def refresh( self ): pygame.display.update() # 扫雷中的一个格子 class Square( object ): exist = False surface = None rect = None state = '' # new, blank, flag, bomed face = None around_count = 0 def __init__( self , exist, around_count, surface, left, top, width, height): self .rect = pygame.Rect(left, top, width, height) self .exist = exist self .surface = surface self .state = 'new' self .around_count = around_count # print('%s' % (self.around_count)) def exists( self ): return self .exist def draw( self ): global game if self .state = = 'new' : self .face = pygame.Surface(( self .rect.width, self .rect.height)) self .face.fill( 'white' ) game.get_screen().blit( self .face, ( self .rect.left, self .rect.top)) pygame.draw.rect( self .surface, 'gray' , self .rect, 1 ) elif self .state = = 'blank' : self .face.fill( 'gray' ) game.get_screen().blit( self .face, ( self .rect.left, self .rect.top)) pygame.draw.rect( self .surface, 'white' , self .rect, 1 ) # 在格子中间画上数字 font = pygame.font.SysFont( "Andale Mono" , 16 ) txt = font.render( "%s" % ( self .around_count if self .around_count > 0 else ' '), True, ' blue') # print('%s, %s' % (txt.get_rect(), self.around_count)) game.get_screen().blit(txt, ( self .rect.left + 4 , self .rect.top)) pass elif self .state = = 'flag' : # 在格子中间画上 F font = pygame.font.SysFont( "Andale Mono" , 16 ) txt = font.render( "F" , True , 'green' ) # print('%s, %s' % (txt.get_rect(), self.around_count)) game.get_screen().blit(txt, ( self .rect.left + 4 , self .rect.top)) elif self .state = = 'boom' : self .face.fill( 'red' ) game.get_screen().blit( self .face, ( self .rect.left, self .rect.top)) pygame.draw.rect( self .surface, 'white' , self .rect, 1 ) pass def click( self ): need_update = False if self .state = = 'new' : if self .exist: self .state = 'boom' need_update = True else : self .state = 'blank' need_update = True return need_update def right_click( self ): need_update = False if self .state = = 'new' : self .state = 'flag' need_update = True elif self .state = = 'flag' : self .state = 'new' need_update = True return need_update def init_game(count, x = 18 , y = 18 ): global game if game: del game game = Game(count) game.random_bomb() game.init_square() # 按间距中的绿色按钮以运行脚本。 if __name__ = = '__main__' : init_game( 30 ) # 主事件循环 while True : for event in pygame.event.get(): if event. type = = pygame.MOUSEBUTTONUP: if game.is_over(): init_game( 30 ) continue pos = event.pos if event.button = = 1 : game.clicked(pos) elif event.button = = 3 : game.right_clicked(pos) # 获取当前那个格子被点击了 if event. type = = pygame.QUIT: sys.exit( 0 ) pygame.display.update() |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!