• pygame - 贪吃蛇小游戏


    蛇每吃掉一个身体块,蛇身就增加一个长度。为了统一计算,界面的尺寸和游戏元素的位置都是身体块长度的倍数
    1. 上下左右方向键(或者ASDW键)控制蛇的移动方向
    2. 空格键暂停和继续
    
    蛇的身体图片文件,复制到项目的asset\img目录下
    
    

    1. import sys
    2. import pygame
    3. from pygame import Rect, font
    4. import random
    5. # control panel contains the controllers and score
    6. ControlPanelColor = (100, 100, 100)
    7. # game panel is the main area for gaming
    8. GamePanelColor = (0, 0, 0)
    9. SnakeSize = 30
    10. MaxWidthBlock = 15
    11. MaxHeightBlock = 10
    12. ControlPanelHeightBlock = 2
    13. SnakeStartX = MaxWidthBlock // 2
    14. SnakeStartY = MaxHeightBlock // 2
    15. ControlPanelHeight = ControlPanelHeightBlock * SnakeSize
    16. GamePanelWidth = MaxWidthBlock * SnakeSize
    17. GamePanelHeight = MaxHeightBlock * SnakeSize
    18. ControlPanelRect = Rect((0, 0), (GamePanelWidth, ControlPanelHeight))
    19. GamePanelRect = Rect((0, ControlPanelHeight), (GamePanelWidth, GamePanelHeight - ControlPanelHeight))
    20. Tick = 20
    21. Tick_Snake_Move = 10
    22. # two buttons to increase and decrease the game speed
    23. minus_btn_rect = None
    24. plus_btn_rect = None
    25. # score
    26. score_value = 0
    27. # the SnakeBody
    28. class SnakeBody:
    29. def __init__(self, x, y, direction, ishead=False, istail=False):
    30. '''
    31. 身体块,蛇身是由多个身体块组成,头和尾也是图案不同的身体块
    32. :param x: 身体块坐标x
    33. :param y: 身体块坐标y
    34. :param direction: 身体块显示的方向
    35. :param ishead: 是否头身体块
    36. :param istail:是否尾身体块
    37. '''
    38. self.__x = x
    39. self.__y = y
    40. self.__ishead = ishead
    41. self.__istail = istail
    42. self.__direction = direction
    43. name = None
    44. if self.__ishead:
    45. name = "head.png"
    46. elif self.__istail:
    47. name = "tail.png"
    48. else:
    49. name = "body.png"
    50. angle = 0
    51. match direction:
    52. case pygame.K_UP:
    53. angle = 0
    54. case pygame.K_DOWN:
    55. angle = 180
    56. case pygame.K_LEFT:
    57. angle = 90
    58. case pygame.K_RIGHT:
    59. angle = -90
    60. img = pygame.image.load(f"asset/img/{name}")
    61. img = pygame.transform.rotate(img, angle)
    62. self.image = pygame.transform.scale(img, (SnakeSize, SnakeSize))
    63. def get_rect(self):
    64. return Rect((self.__x, self.__y), (SnakeSize, SnakeSize))
    65. def move(self, x, y):
    66. self.__x = self.__x + x
    67. self.__y = self.__y + y
    68. def set_direction(self, direction):
    69. if self.__direction == direction:
    70. return
    71. self.__direction = direction
    72. name = None
    73. if self.__ishead:
    74. name = "head.png"
    75. elif self.__istail:
    76. name = "tail.png"
    77. else:
    78. name = "body.png"
    79. angle = 0
    80. match direction:
    81. case pygame.K_UP:
    82. angle = 0
    83. case pygame.K_DOWN:
    84. angle = 180
    85. case pygame.K_LEFT:
    86. angle = 90
    87. case pygame.K_RIGHT:
    88. angle = -90
    89. img = pygame.image.load(f"asset/img/{name}")
    90. img = pygame.transform.rotate(img, angle)
    91. self.image = pygame.transform.scale(img, (SnakeSize, SnakeSize))
    92. def get_direction(self):
    93. return self.__direction
    94. class Snake:
    95. bodys = []
    96. new_body = None
    97. __new_direction = pygame.K_UP
    98. __tick_movement = 0
    99. __tick_create_body = 0
    100. __stop = False
    101. __is_paused = False
    102. def __init__(self):
    103. self.bodys.insert(0, SnakeBody(SnakeSize * SnakeStartX, SnakeSize * SnakeStartY, pygame.K_UP, True, False))
    104. self.bodys.insert(1,
    105. SnakeBody(SnakeSize * SnakeStartX, SnakeSize * (SnakeStartY + 1), pygame.K_UP, False, False))
    106. self.bodys.insert(2,
    107. SnakeBody(SnakeSize * SnakeStartX, SnakeSize * (SnakeStartY + 2), pygame.K_UP, False, True))
    108. def set_direction(self, direction):
    109. # do not set inverse direction
    110. if ((self.bodys[0].get_direction() == pygame.K_UP and direction != pygame.K_DOWN) or
    111. (self.bodys[0].get_direction() == pygame.K_DOWN and direction != pygame.K_UP) or
    112. (self.bodys[0].get_direction() == pygame.K_LEFT and direction != pygame.K_RIGHT) or
    113. (self.bodys[0].get_direction() == pygame.K_RIGHT and direction != pygame.K_LEFT)):
    114. self.__new_direction = direction
    115. def move(self):
    116. if self.__stop:
    117. return
    118. if self.__is_paused:
    119. return
    120. self.__tick_movement += 1
    121. if self.__tick_movement <= Tick_Snake_Move:
    122. return
    123. self.__tick_movement = 0
    124. length = len(self.bodys)
    125. head = self.bodys[0]
    126. oldheadpos = head.get_rect()
    127. oldheaddirection = head.get_direction()
    128. # update head direction and move
    129. head.set_direction(self.__new_direction)
    130. match self.__new_direction:
    131. case pygame.K_UP:
    132. head.move(0, -SnakeSize)
    133. case pygame.K_DOWN:
    134. head.move(0, SnakeSize)
    135. case pygame.K_LEFT:
    136. head.move(-SnakeSize, 0)
    137. case pygame.K_RIGHT:
    138. head.move(SnakeSize, 0)
    139. if ((self.new_body is not None) and
    140. (head.get_rect().x == self.new_body.get_rect().x and head.get_rect().y == self.new_body.get_rect().y)):
    141. # as head move, the old head position is empty,
    142. # add the new body at the second position
    143. self.new_body.set_direction(head.get_direction())
    144. offsetx = oldheadpos.x - self.new_body.get_rect().x
    145. offsety = oldheadpos.y - self.new_body.get_rect().y
    146. self.new_body.move(offsetx, offsety)
    147. self.bodys.insert(1, self.new_body)
    148. self.new_body = None
    149. global score_value
    150. score_value += 1
    151. else:
    152. # as head move, the old head position is empty,
    153. # move the second-to-last body to the second body
    154. second2lastbody = self.bodys[length - 2]
    155. second2lastpos = second2lastbody.get_rect()
    156. second2lastdirection = second2lastbody.get_direction()
    157. offsetx = oldheadpos.x - second2lastpos.x
    158. offsety = oldheadpos.y - second2lastpos.y
    159. second2lastbody.set_direction(oldheaddirection)
    160. second2lastbody.move(offsetx, offsety)
    161. self.bodys.remove(second2lastbody)
    162. self.bodys.insert(1, second2lastbody)
    163. # move tail to the direction of the second-to-last body
    164. tailbody = self.bodys[length - 1]
    165. tailbody.set_direction(second2lastdirection)
    166. offsetx = second2lastpos.x - tailbody.get_rect().x
    167. offsety = second2lastpos.y - tailbody.get_rect().y
    168. tailbody.move(offsetx, offsety)
    169. def stop(self):
    170. self.__stop = True
    171. def create_body(self):
    172. self.__tick_create_body += 1
    173. if self.__tick_create_body <= 30:
    174. return
    175. if self.is_paused():
    176. return
    177. self.__tick_create_body = 0
    178. if self.new_body is not None:
    179. return
    180. x, y = 0, 0
    181. while True:
    182. isspare = True
    183. intx = random.randint(0, MaxWidthBlock - 1)
    184. inty = random.randint(ControlPanelHeightBlock, MaxHeightBlock - 1)
    185. x = intx * SnakeSize
    186. y = inty * SnakeSize
    187. for b in self.bodys:
    188. rect = b.get_rect()
    189. if rect.x == x and rect.y == y:
    190. isspare = False
    191. break
    192. if isspare:
    193. break
    194. print(f"create body block at {intx}, {inty}")
    195. self.new_body = SnakeBody(x, y, pygame.K_UP, False, False)
    196. def is_collided(self):
    197. iscollided = False
    198. head = self.bodys[0]
    199. headrect = self.bodys[0].get_rect()
    200. # boundary collision
    201. if headrect.x <= (0 - SnakeSize) or headrect.x >= GamePanelWidth or \
    202. headrect.y <= (ControlPanelHeight - SnakeSize) or headrect.y >= (ControlPanelHeight + GamePanelHeight):
    203. iscollided = True
    204. # body collision
    205. else:
    206. if head.get_direction() == pygame.K_LEFT:
    207. pass
    208. for b in self.bodys[1:len(self.bodys)]:
    209. if head.get_rect().colliderect(b.get_rect()):
    210. iscollided = True
    211. break
    212. return iscollided
    213. def pause(self):
    214. self.__is_paused = not self.__is_paused
    215. def is_paused(self):
    216. return self.__is_paused
    217. def display_result():
    218. final_text1 = "Game Over"
    219. final_surf = pygame.font.SysFont("Arial", SnakeSize * 2).render(final_text1, 1, (242, 3, 36)) # 设置颜色
    220. screen.blit(final_surf, [screen.get_width() / 2 - final_surf.get_width() / 2,
    221. screen.get_height() / 2 - final_surf.get_height() / 2]) # 设置显示位置
    222. def display_paused():
    223. paused_text = "Paused"
    224. paused_surf = pygame.font.SysFont("Arial", SnakeSize * 2).render(paused_text, 1, (242, 3, 36))
    225. screen.blit(paused_surf, [screen.get_width() / 2 - paused_surf.get_width() / 2,
    226. screen.get_height() / 2 - paused_surf.get_height() / 2])
    227. def display_control_panel():
    228. global minus_btn_rect, plus_btn_rect
    229. color = (242, 3, 36)
    230. speed_text = "Speed"
    231. speed_surf = pygame.font.SysFont("Arial", SnakeSize).render(speed_text, 1, "blue") # 设置颜色
    232. speed_rect = speed_surf.get_rect()
    233. speed_rect.x, speed_rect.y = 0, 0
    234. screen.blit(speed_surf, speed_rect)
    235. offsetx = speed_rect.x + speed_rect.width + 10
    236. text_minus = "-"
    237. minus_btn = pygame.font.SysFont("Arial", SnakeSize).render(text_minus, 1, color) # 设置颜色
    238. minus_btn_rect = minus_btn.get_rect()
    239. minus_btn_rect.x, minus_btn_rect.y = offsetx, 0
    240. screen.blit(minus_btn, minus_btn_rect)
    241. offsetx = minus_btn_rect.x + minus_btn_rect.width + 10
    242. text_speed_value = str(Tick - Tick_Snake_Move)
    243. speed_value_surf = pygame.font.SysFont("Arial", SnakeSize).render(text_speed_value, 1, color) # 设置颜色
    244. speed_value_rect = speed_value_surf.get_rect()
    245. speed_value_rect.x, speed_value_rect.y = offsetx, 0
    246. screen.blit(speed_value_surf, speed_value_rect)
    247. offsetx = speed_value_rect.x + speed_value_rect.width + 10
    248. text_plus = "+"
    249. plus_btn = pygame.font.SysFont("Arial", SnakeSize).render(text_plus, 1, color) # 设置颜色
    250. plus_btn_rect = plus_btn.get_rect()
    251. plus_btn_rect.x, plus_btn_rect.y = offsetx, 0
    252. screen.blit(plus_btn, plus_btn_rect)
    253. score_value_text = str(score_value)
    254. score_value_surf = pygame.font.SysFont("Arial", SnakeSize).render(score_value_text, 1, color) # 设置颜色
    255. score_value_rect = score_value_surf.get_rect()
    256. score_value_rect.x = GamePanelWidth - score_value_rect.width
    257. score_value_rect.y = 0
    258. screen.blit(score_value_surf, score_value_rect)
    259. score_text = "Score"
    260. score_surf = pygame.font.SysFont("Arial", SnakeSize).render(score_text, 1, "blue") # 设置颜色
    261. score_rect = score_surf.get_rect()
    262. score_rect.x = score_value_rect.x - score_rect.width - 10
    263. score_rect.y = 0
    264. screen.blit(score_surf, score_rect)
    265. def check_click(position):
    266. global Tick_Snake_Move
    267. if minus_btn_rect == None or plus_btn_rect == None:
    268. return
    269. x, y = position[0], position[1]
    270. minus_btn_x, minus_btn_y = minus_btn_rect.x, minus_btn_rect.y
    271. plus_btn_x, plus_btn_y = plus_btn_rect.x, plus_btn_rect.y
    272. if minus_btn_x < x < minus_btn_x + minus_btn_rect.width and \
    273. minus_btn_y < y < minus_btn_y + minus_btn_rect.height:
    274. Tick_Snake_Move += 1
    275. elif plus_btn_x < x < plus_btn_x + plus_btn_rect.width and \
    276. plus_btn_y < y < plus_btn_y + plus_btn_rect.height:
    277. Tick_Snake_Move -= 1
    278. pygame.init()
    279. pygame.font.init() # 初始化字体
    280. screen = pygame.display.set_mode((GamePanelWidth, ControlPanelHeight + GamePanelHeight))
    281. clock = pygame.time.Clock()
    282. snake = Snake()
    283. screen.fill(ControlPanelColor, ControlPanelRect)
    284. while True:
    285. clock.tick(20)
    286. for event in pygame.event.get():
    287. if event.type == pygame.QUIT:
    288. pygame.quit()
    289. sys.exit()
    290. elif event.type == pygame.KEYDOWN:
    291. if event.key == pygame.K_LEFT or event.key == pygame.K_a:
    292. snake.set_direction(pygame.K_LEFT)
    293. elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
    294. snake.set_direction(pygame.K_RIGHT)
    295. elif event.key == pygame.K_UP or event.key == pygame.K_w:
    296. snake.set_direction(pygame.K_UP)
    297. elif event.key == pygame.K_DOWN or event.key == pygame.K_s:
    298. snake.set_direction(pygame.K_DOWN)
    299. elif event.key == pygame.K_SPACE:
    300. snake.pause()
    301. if pygame.mouse.get_pressed()[0]:
    302. check_click(pygame.mouse.get_pos())
    303. screen.fill(GamePanelColor, (0, ControlPanelHeight, GamePanelWidth, ControlPanelHeight + GamePanelHeight))
    304. snake.move()
    305. for body in snake.bodys:
    306. screen.blit(body.image, body.get_rect())
    307. # collision detection
    308. if snake.is_collided():
    309. snake.stop()
    310. display_result()
    311. else:
    312. # new body
    313. snake.create_body()
    314. if snake.new_body is not None:
    315. screen.blit(snake.new_body.image, snake.new_body.get_rect())
    316. screen.fill(ControlPanelColor, (0, 0, GamePanelWidth, ControlPanelHeight))
    317. display_control_panel()
    318. if snake.is_paused():
    319. display_paused()
    320. pygame.display.flip()

  • 相关阅读:
    新概念英语(第二册)复习——Lesson 16 - Lesson20
    SSM+图书馆电子文件资源管理 毕业设计-附源码091426
    了解区块链技术和智能合约开发
    MySQL超详细安装教程 手把手教你安装MySQL到使用MySQL 最简单的MySQL安装方式,这种方式装,卸载也简单
    ansible自动化运维工具的使用
    JVM内存和垃圾回收-03.运行时数据区概述及线程
    D. Tournament Countdown(交互题)
    用餐高峰期,排队现象严重?食堂多元化升级改造
    python 将数组元素存入.csv文件中;csv内部实现行列转换;
    BUUCTF NewStarCTF 公开赛赛道Week4 Writeup
  • 原文地址:https://blog.csdn.net/kyranhan/article/details/133156623