经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Python » 查看文章
Python开发之射击闯关游戏的实现
来源:jb51  时间:2023/1/16 9:16:12  对本文有异议

项目功能

地图编辑器:可以实现玩家自己定义每一关卡的样式和难易程度

运行界面:实现了玩家的移动,跳跃,发射子弹,投掷手雷,以及敌人的AL(移动,发射子弹,扔手雷),同时游戏中有一系列的道具(生命值药箱,子弹补给,手雷补给)以及各种动画和音乐音效,还有各种花草岩石装饰品,以及悬崖和水涡危险地方,更多未知,自己体验就能感受到!

总代码累计1100行左右!

地图编辑器

  1. import pygame
  2. import sys
  3. import csv
  4. import button
  5.  
  6. pygame.init()
  7. # 定义一个时钟
  8. clock = pygame.time.Clock()
  9. FPS = 60
  10.  
  11. # 游戏窗口
  12. SCREEN_WIDTH = 800
  13. SCREEN_HEIGHT = 560
  14. LOWER_MARGIN = 100
  15. SIDE_MAGTIN = 300
  16. screen = pygame.display.set_mode((SCREEN_WIDTH + SIDE_MAGTIN, SCREEN_HEIGHT + LOWER_MARGIN))
  17. pygame.display.set_caption("级别编辑器")
  18.  
  19. # 定义游戏变量
  20. ROWS = 16
  21. MAX_COLS = 150
  22. TILE_SIZE = SCREEN_HEIGHT // ROWS
  23. TILE_TYPES = 21
  24. level = 1
  25. current_tile = 0
  26. scroll_left = False
  27. scroll_right = False
  28. scroll = 0
  29. scroll_speed = 1
  30.  
  31. # 加载背景图片
  32. pine1_img = pygame.image.load("img/Background/pine1.png").convert_alpha()
  33. pine2_img = pygame.image.load("img/Background/pine2.png").convert_alpha()
  34. mountain_img = pygame.image.load("img/Background/mountain.png").convert_alpha()
  35. sky_img = pygame.image.load("img/Background/sky_cloud.png").convert_alpha()
  36. # 瓷砖瓦片列表
  37. img_list = []
  38. for x in range(TILE_TYPES):
  39. img = pygame.image.load(f"img/tile/{x}.png")
  40. img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE))
  41. img_list.append(img)
  42.  
  43. # 创建保存按钮
  44. save_img = pygame.image.load("img/save_btn.png").convert_alpha()
  45. load_img = pygame.image.load("img/load_btn.png").convert_alpha()
  46.  
  47. # 定义颜色
  48. GREEN = (144, 201, 120)
  49. WHITE = (255, 255, 255)
  50. RED = (200, 25, 25)
  51.  
  52. #定义字体
  53. font = pygame.font.SysFont("Futura", 30)
  54.  
  55. # 创建空的瓷砖列表(二维)
  56. world_data = []
  57. for row in range(ROWS):
  58. r = [-1] * MAX_COLS
  59. world_data.append(r)
  60.  
  61. # 创建一个组
  62. for tile in range(0, MAX_COLS):
  63. world_data[ROWS - 1][tile] = 0
  64.  
  65. # 在屏幕上显示下一级定义文本显示函数
  66. def draw_text(text, font, text_color, x, y):
  67. img = font.render(text, True, text_color)
  68. screen.blit(img, (x, y))
  69.  
  70.  
  71. # 创建背景函数
  72. def draw_bg():
  73. screen.fill(GREEN)
  74. width = sky_img.get_width()
  75. for x in range(4):
  76. screen.blit(sky_img, ((x * width) - scroll * 0.5, 0))
  77. screen.blit(mountain_img, ((x * width) - scroll * 0.6, SCREEN_HEIGHT - mountain_img.get_height() - 300))
  78. screen.blit(pine1_img, ((x * width) - scroll * 0.7, SCREEN_HEIGHT - pine1_img.get_height() - 150))
  79. screen.blit(pine2_img, ((x * width) - scroll * 0.8, SCREEN_HEIGHT - pine2_img.get_height()))
  80.  
  81. # 绘制格子
  82. def draw_grid():
  83. # 垂直的线
  84. for c in range(MAX_COLS + 1):
  85. pygame.draw.line(screen, WHITE, (c * TILE_SIZE - scroll, 0), (c * TILE_SIZE - scroll, SCREEN_HEIGHT))
  86. # 水平的线
  87. for c in range(ROWS + 1):
  88. pygame.draw.line(screen, WHITE, (0, c * TILE_SIZE), (SCREEN_WIDTH, c * TILE_SIZE))
  89.  
  90. # 在地图中绘制瓷砖
  91. def draw_world():
  92. for y, row in enumerate(world_data):
  93. for x, tile in enumerate(row):
  94. if tile >= 0:
  95. screen.blit(img_list[tile], (x * TILE_SIZE - scroll, y * TILE_SIZE))
  96. # 创建按钮
  97. # 创建保存和加载数据按钮
  98. save_button = button.Button(SCREEN_WIDTH // 2, SCREEN_HEIGHT + LOWER_MARGIN - 50, save_img, 1)
  99. load_button = button.Button(SCREEN_WIDTH // 2 + 200, SCREEN_HEIGHT + LOWER_MARGIN - 50, load_img, 1)
  100.  
  101. # 制作一个按钮瓷片列表
  102. button_list = []
  103. button_col = 0
  104. button_row = 0
  105. for i in range(len(img_list)):
  106. tile_button = button.Button(SCREEN_WIDTH + (75 * button_col) + 50, 75 * button_row + 50, img_list[i], 1)
  107. button_list.append(tile_button)
  108. button_col += 1
  109. if button_col == 3:
  110. button_row += 1
  111. button_col = 0
  112.  
  113. run = True
  114. while run:
  115. clock.tick(FPS)
  116. draw_bg()
  117. draw_grid()
  118. draw_world()
  119.  
  120. draw_text(f"Level: {level}", font, WHITE, 10, SCREEN_HEIGHT + LOWER_MARGIN - 90)
  121. draw_text("Press up or Down to change level", font, WHITE, 10, SCREEN_HEIGHT + LOWER_MARGIN - 60)
  122.  
  123. # 保存和加载地图数据
  124. if save_button.draw(screen):
  125. # 保存级别数据
  126. with open(f"level{level}_data.csv", "w", newline="") as csvfile:
  127. writer = csv.writer(csvfile, delimiter = ",")
  128. for row in world_data:
  129. writer.writerow(row)
  130. # with open(f"level{level}_data.csv", "wb") as pickle_out:
  131. # pickle.dump(world_data, pickle_out)
  132. if load_button.draw(screen):
  133. # 加载地图级别数据
  134. # 重置滚动scroll为起始位置0
  135. scroll = 0
  136. with open(f"level{level}_data.csv", "r", newline="") as csvfile:
  137. reader = csv.reader(csvfile, delimiter=",")
  138. for y, row in enumerate(reader):
  139. for x, tile in enumerate(row):
  140. world_data[y][x] = int(tile)
  141.  
  142.  
  143. # 画面板和瓷砖
  144. pygame.draw.rect(screen, GREEN, (SCREEN_WIDTH, 0, SIDE_MAGTIN, SCREEN_HEIGHT))
  145. # 选择一种瓷砖,获取右侧瓷砖列表的具体
  146. button_count = 0
  147. for button_count, i in enumerate(button_list):
  148. if i.draw(screen):
  149. current_tile = button_count
  150. # 高亮显示选中的瓷砖
  151. pygame.draw.rect(screen, RED, button_list[current_tile].rect, 3)
  152.  
  153. # 滚动地图
  154. if scroll_left == True and scroll > 0:
  155. scroll -= 5 * scroll_speed
  156. if scroll_right == True and scroll < (MAX_COLS * TILE_SIZE) - SCREEN_WIDTH: # 检测最右边的边缘
  157. scroll += 5 * scroll_speed
  158.  
  159. # 在窗口中增加新的瓷砖
  160. # 获取鼠标的位置
  161. pos = pygame.mouse.get_pos()
  162. x = (pos[0] + scroll) // TILE_SIZE
  163. y = pos[1] // TILE_SIZE
  164.  
  165. # 检测点击的区域,把右侧获取的瓷片放在地图中
  166. if pos[0] < SCREEN_WIDTH and pos[1] < SCREEN_HEIGHT:
  167. # 更新瓷砖的值
  168. if pygame.mouse.get_pressed()[0] == 1:
  169. if world_data[y][x] != current_tile:
  170. world_data[y][x] = current_tile
  171. # 删除选中的
  172. if pygame.mouse.get_pressed()[2] == 1:
  173. world_data[y][x] = -1
  174.  
  175. for event in pygame.event.get():
  176. if event.type == pygame.QUIT:
  177. run = False
  178. pygame.quit()
  179. sys.exit()
  180. # 键盘按键
  181. if event.type == pygame.KEYDOWN:
  182. if event.key == pygame.K_UP:
  183. level += 1
  184. if event.key == pygame.K_DOWN and level > 0:
  185. level -= 1
  186. if event.key == pygame.K_LEFT:
  187. scroll_left = True
  188. if event.key == pygame.K_RIGHT:
  189. scroll_right = True
  190. if event.key == pygame.K_LSHIFT:
  191. scroll_speed = 5
  192. if event.type == pygame.KEYUP:
  193. if event.key == pygame.K_LEFT:
  194. scroll_left = False
  195. if event.key == pygame.K_RIGHT:
  196. scroll_right = False
  197. if event.key == pygame.K_LSHIFT:
  198. scroll_speed = 1
  199. pygame.display.update()

游戏主运行程序

  1. import pygame
  2. from pygame import mixer
  3. import sys
  4. import os
  5. import random
  6. import csv
  7. import button
  8. import math
  9.  
  10. mixer.init()
  11. pygame.init()
  12. # 画布元素
  13. SCREEN_WIDTH = 800
  14. SCREEN_HEIGHT = int(SCREEN_WIDTH * 0.8)
  15. screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
  16. pygame.display.set_caption("射击游戏")
  17.  
  18. # 设置帧
  19. clock = pygame.time.Clock()
  20. FPS = 60
  21.  
  22. # 定义游戏变量
  23. GRAVITY = 0.75
  24. SCROLL_THRESH = 200
  25. ROWS = 16
  26. COLS = 150
  27. TILE_SIZE = SCREEN_HEIGHT // ROWS
  28. TILE_TYPES = 21
  29. MAX_LEVELS = 3
  30. screen_scroll = 0
  31. bg_scroll = 0
  32. level = 1
  33. # 定义游戏状态
  34. start_game = False
  35. # 定义是否淡入进入游戏画面
  36. start_intro = False
  37.  
  38. # 定义玩家状态变量
  39. moving_left = False
  40. moving_right = False
  41. shoot = False
  42. grenade = False
  43. grenade_thrown = False
  44.  
  45. #加载音乐和声音
  46. pygame.mixer.music.load("audio/music2.mp3")
  47. pygame.mixer.music.set_volume(0.3)
  48. pygame.mixer.music.play(-1, 0.0, 3000)
  49. jump_fx = pygame.mixer.Sound("audio/jump.wav")
  50. jump_fx.set_volume(0.5)
  51. shot_fx = pygame.mixer.Sound("audio/shot.wav")
  52. shot_fx.set_volume(0.9)
  53. grenade_fx = pygame.mixer.Sound("audio/grenade.wav")
  54. grenade_fx.set_volume(0.9)
  55.  
  56.  
  57. # 加载背景图片
  58. pine1_img = pygame.image.load("img/Background/pine1.png").convert_alpha()
  59. pine2_img = pygame.image.load("img/Background/pine2.png").convert_alpha()
  60. mountain_img = pygame.image.load("img/Background/mountain.png").convert_alpha()
  61. sky_img = pygame.image.load("img/Background/sky_cloud.png").convert_alpha()
  62. # 加载按钮图像
  63. start_img = pygame.image.load("img/start_btn.png").convert_alpha()
  64. exit_img = pygame.image.load("img/exit_btn.png").convert_alpha()
  65. restart_img = pygame.image.load("img/restart_btn.png").convert_alpha()
  66.  
  67. # 加载21种瓷砖图像放在瓷砖图像列表中
  68. img_list = []
  69. for x in range(TILE_TYPES):
  70. img = pygame.image.load(f"img/Tile/{x}.png")
  71. img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE))
  72. img_list.append(img)
  73. # 加载子弹
  74. bullet_img = pygame.image.load("img/icons/bullet.png").convert_alpha()
  75. grenade_img = pygame.image.load("img/icons/grenade.png").convert_alpha()
  76. # 加载物品
  77. health_box_img = pygame.image.load("img/icons/health_box.png").convert_alpha()
  78. ammo_box_img = pygame.image.load("img/icons/ammo_box.png").convert_alpha()
  79. grenade_box_img = pygame.image.load("img/icons/grenade_box.png").convert_alpha()
  80. item_boxes = {
  81. "Health": health_box_img,
  82. "Ammo": ammo_box_img,
  83. "Grenade": grenade_box_img
  84. }
  85. # 定义颜色
  86. BG = (144, 201, 120)
  87. RED = (255, 0, 0)
  88. WHITE = (255, 255, 255)
  89. GREEN = (0, 255, 0)
  90. BLACK = (0, 0, 0)
  91. PINK = (235, 65, 54)
  92. # 定义字体
  93. font = pygame.font.SysFont("Futura", 30)
  94. # 定义一个显示文本函数,用来显示玩家的相关属性
  95. def draw_text(text, font, text_color, x, y):
  96. img = font.render(text, True, text_color)
  97. screen.blit(img, (x, y))
  98. # 刷新背景函数,for循环重复背景,刷新背景中不同的照片的x坐标,以此达到背景动态效果
  99. def draw_bg():
  100. screen.fill(BG)
  101. width = sky_img.get_width()
  102. for x in range(5):
  103. screen.blit(sky_img, ((x * width) - bg_scroll * 0.5, 0))
  104. screen.blit(mountain_img, ((x * width) - bg_scroll * 0.6, SCREEN_HEIGHT - mountain_img.get_height() - 300))
  105. screen.blit(pine1_img, ((x * width) - bg_scroll * 0.7, SCREEN_HEIGHT - pine1_img.get_height() - 150))
  106. screen.blit(pine2_img, ((x * width) - bg_scroll * 0.8, SCREEN_HEIGHT - pine2_img.get_height()))
  107. # 重置游戏函数定义,碰撞”通关“瓷片时,清空本关的所有显示元素
  108. def reset_level():
  109. enemy_group.empty()
  110. bullet_group.empty()
  111. grenade_group.empty()
  112. explosion_group.empty()
  113. item_box_group.empty()
  114. decoration_group.empty()
  115. water_group.empty()
  116. exit_group.empty()
  117. # 创建空的瓷砖列表。二维列表行列
  118. data = []
  119. for row in range(ROWS):
  120. r = [-1] * COLS
  121. data.append(r)
  122. return data
  123. # 创建士兵类(敌人和玩家)
  124. class Soldier(pygame.sprite.Sprite):
  125. def __init__(self, char_type, x, y, scale, speed, ammo, grenades):
  126. super().__init__()
  127. self.alive = True # 定义或者还是死亡变量
  128. self.char_type = char_type # 获取文件类型样式
  129. self.speed = speed # 速度
  130. self.ammo = ammo # 子弹
  131. self.start_ammo = ammo
  132. self.shoot_cooldown = 0 # 冷却
  133. self.grenades = grenades # 手雷
  134. self.health = 100 # 生命值
  135. self.max_health = self.health
  136. self.direction = 1 # 默认方向右
  137. self.vel_y = 0 # 垂直
  138. self.jump = False # 跳跃
  139. self.in_air = True # 是否在空中
  140. self.flip = False # 默认左为false
  141. self.animation_list = [] # 动画列表
  142. self.frame_index = 0 # 索引
  143. self.action = 0 # 选择动作变量
  144. self.update_time = pygame.time.get_ticks() # 以毫秒为单位获取时间
  145. # 创建AI特定变量
  146. self.move_counter = 0 # 移动计数,对应下文敌人来回徘徊
  147. self.vision = pygame.Rect(0, 0, 150, 20) # 搜索玩家在玩家视线之内
  148. self.idling = False # 闲置状态,对应下文AI开枪和扔手雷的状态
  149. self.idling_counter = 0 # 闲置计数
  150. self.grenade_time = pygame.time.get_ticks() # 对应下文手雷爆炸时间
  151. # 加载玩家是所有的图片类型
  152. animation_types = ["Idle", "Run", "Jump", "Death"]
  153. for animation in animation_types:
  154. # 重置临时列表
  155. temp_list = []
  156. # 统计每种动画帧数量
  157. num_of_frames = len(os.listdir(f"img/{char_type}/{animation}"))
  158. for i in range(num_of_frames):
  159. img = pygame.image.load(f"img/{char_type}/{animation}/{i}.png").convert_alpha()
  160. img = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
  161. temp_list.append(img)
  162. self.animation_list.append(temp_list)
  163. self.image = self.animation_list[self.action][self.frame_index]
  164. self.rect = self.image.get_rect()
  165. self.rect.center = (x, y)# rect=(x,y,w,h)
  166. self.width = self.image.get_width()
  167. self.height = self.image.get_height()
  168.  
  169. def update(self):
  170. self.update_animation()
  171. self.check_alive()
  172. # 更新冷却时间
  173. if self.shoot_cooldown > 0:
  174. self.shoot_cooldown -= 1
  175. def move(self, moving_left, moving_right):
  176. # 重置移动变量
  177. screen_scroll = 0
  178. dx = 0
  179. dy = 0
  180. # 根据移动变量判断向左还是向右移动
  181. if moving_left:
  182. dx = -self.speed
  183. self.flip = True
  184. self.direction = -1
  185. if moving_right:
  186. dx = self.speed
  187. self.flip = False
  188. self.direction = 1
  189. # 跳跃
  190. if self.jump == True and self.in_air == False:
  191. self.vel_y = -11
  192. self.jump = False
  193. self.in_air = True
  194. # 使用重力,让其在y方向跳跃高度进行限制
  195. self.vel_y += GRAVITY
  196. if self.vel_y > 10:
  197. self.vel_y
  198. dy += self.vel_y
  199. # 检测与地面的碰撞
  200. for tile in world.obstacle_list:
  201. # 检测玩家与每个地面瓷砖x方向上的碰撞
  202. if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
  203. dx = 0
  204. # 检测如果是ai机器人碰到墙就返回
  205. if self.char_type == "enemy":
  206. self.direction *= -1
  207. self.move_counter = 0
  208. # 检车玩家与瓷砖y方向上的碰撞
  209. if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
  210. # 检测与地面底部的碰撞
  211. if self.vel_y < 0:
  212. self.vel_y = 0
  213. dy = tile[1].bottom - self.rect.top
  214. # 检测与地面顶部的碰撞
  215. elif self.vel_y >= 0:
  216. self.vel_y = 0
  217. self.in_air = False
  218. dy = tile[1].top - self.rect.bottom
  219. # 检测与水面的碰撞
  220. if pygame.sprite.spritecollide(self, water_group, False):
  221. self.health = 0
  222. # 检车与出口标志碰撞
  223. level_complete = False
  224. if pygame.sprite.spritecollide(self, exit_group, False):
  225. level_complete = True
  226. # 检测从地图上坠落下来
  227. if self.rect.bottom > SCREEN_HEIGHT:
  228. self.health = 0
  229. # 检测是否走到窗口的边缘,如果走到窗口边缘就不让再走了
  230. if self.char_type == "player":
  231. if self.rect.left + dx < 0 or self.rect.right + dx > SCREEN_WIDTH:
  232. dx = 0
  233. # 更新矩形的位置
  234. self.rect.x += dx
  235. self.rect.y += dy
  236. # 在玩家位置的基础上更新滚动平台 rect.right 对应矩形的左,以此类推
  237. if self.char_type == "player":
  238. if (self.rect.right > SCREEN_WIDTH - SCROLL_THRESH and bg_scroll < world.level_length * TILE_SIZE - SCREEN_WIDTH) or (self .rect.left < SCROLL_THRESH and bg_scroll > abs(dx)):
  239. self.rect.x -= dx
  240. screen_scroll = -dx
  241.  
  242. return screen_scroll, level_complete
  243.  
  244. def shoot(self):
  245. if self.shoot_cooldown == 0 and self.ammo > 0:
  246. self.shoot_cooldown = 20
  247. bullet = Bullet(self.rect.centerx + (0.75 * self.rect.size[0] * self.direction), self.rect.centery,
  248. self.direction)
  249. bullet_group.add(bullet)
  250. #减少弹药
  251. self.ammo -= 1
  252. shot_fx.play()
  253. def ai(self):
  254. if self.alive and player.alive:
  255. if self.idling == False and random.randint(1, 100) == 1:
  256. self.update_action(0) # 选择闲置动作
  257. self.idling = True
  258. # ai检测到我方士兵在附近
  259. if self.vision.colliderect(player.rect):
  260. # 停止奔跑并面向玩家的时候
  261. self.update_action(0)
  262. # 并射击
  263. self.shoot()
  264. else:
  265. # 不定时扔手雷
  266. now_time = pygame.time.get_ticks()
  267. if math.sqrt(math.pow(abs(self.rect.centerx - player.rect.centerx), 2) + math.pow(
  268. abs(self.rect.centery - player.rect.centery), 2)) < TILE_SIZE * 5:
  269. if self.grenades > 0:
  270. if now_time - self.grenade_time > random.randint(2000, 3000):
  271. # 停止奔跑并面向玩家的时候
  272. self.update_action(0)
  273. self.grenade_time = pygame.time.get_ticks()
  274. grenade = Grenade(self.rect.centerx, self.rect.centery, self.direction)
  275. grenade_group.add(grenade)
  276. self.grenades -= 1
  277.  
  278. if self.idling == False:
  279. if self.direction == 1:
  280. ai_moving_right = True
  281. self.idling_counter = 50
  282. else:
  283. ai_moving_right = False
  284. ai_moving_left = not ai_moving_right
  285. self.move(ai_moving_left, ai_moving_right)
  286. self.update_action(1) # 选择运动动作
  287. self.move_counter += 1
  288. # 更新ai视觉范围作为移动范围
  289. self.vision.center = (self.rect.centerx + 75 * self.direction, self.rect.centery)
  290. # pygame.draw.rect(screen, RED, self.vision)
  291. if self.move_counter > TILE_SIZE:
  292. self.direction *= -1
  293. self.move_counter *= -1
  294. else:
  295. self.idling_counter -= 1
  296. if self.idling_counter <= 0:
  297. self.idling = False
  298.  
  299. # 滚动
  300. self.rect.x += screen_scroll
  301. def update_animation(self):
  302. # 更新动画
  303. ANIMATION_COOLDOWN= 100
  304. # 更新当前的帧
  305. self.image = self.animation_list[self.action][self.frame_index]
  306. # 检测现在的时间更新时间
  307. if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN:
  308. self.update_time = pygame.time.get_ticks()
  309. self.frame_index += 1
  310. # 检测如果列表索引超出了动画帧数
  311. if self.frame_index >= len(self.animation_list[self.action]):
  312. if self.action == 3:
  313. self.frame_index = len(self.animation_list[self.action]) - 1
  314. else:
  315. self.frame_index = 0
  316. def update_action(self, new_action):
  317. # 判断不同的行动播放不同的动画
  318. if new_action != self.action:
  319. self.action = new_action
  320. # 更新动画设置
  321. self.frame_index = 0
  322. self.update_time = pygame.time.get_ticks()
  323. def check_alive(self):
  324. if self.health <= 0:
  325. self.health = 0
  326. self.speed = 0
  327. self.alive = False
  328. self.update_action(3)
  329. def draw(self):
  330. screen.blit(pygame.transform.flip(self.image, self.flip, False), self.rect)
  331. # 收集物品类
  332. class ItemBox(pygame.sprite.Sprite):
  333. def __init__(self, item_type, x, y):
  334. super().__init__()
  335. self.item_type = item_type
  336. self.image = item_boxes.get(self.item_type)
  337. self.rect = self.image.get_rect()
  338. self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
  339. def update(self):
  340. # 滚动
  341. self.rect.x += screen_scroll
  342. # 检车士兵与物品的碰撞
  343. if pygame.sprite.collide_rect(self, player):
  344. # 检测获取箱子的种类
  345. if self.item_type == "Health":
  346. player.health += 25
  347. if player.health > player.max_health:
  348. player.health = player.max_health
  349. elif self.item_type == "Ammo":
  350. player.ammo += 15
  351. elif self.item_type == "Grenade":
  352. player.grenades += 3
  353. # 删除物品
  354. self.kill()
  355.  
  356.  
  357. # 创建血条类
  358. class HealthBar():
  359. def __init__(self, x, y, health, max_health):
  360. self.x = x
  361. self.y = y
  362. self.health = health
  363. self.max_health = max_health
  364. def draw(self, health):
  365. # 更新最新血条
  366. self.health = health
  367. # 计算血条的比率
  368. ratio = self.health / self.max_health
  369. pygame.draw.rect(screen, BLACK, (self.x - 2, self.y - 2, 154, 24))
  370. pygame.draw.rect(screen, RED, (self.x, self.y, 150, 20))
  371. pygame.draw.rect(screen, GREEN, (self.x, self.y, 150 * ratio, 20))
  372.  
  373. class Bullet(pygame.sprite.Sprite):
  374. def __init__(self, x, y, direction):
  375. super().__init__()
  376. self.speed = 10
  377. self.image = bullet_img
  378. self.rect = self.image.get_rect()
  379. self.rect.center = (x, y)
  380. self.direction = direction
  381. def update(self):
  382. # 移动子弹
  383. self.rect.x += (self.direction * self.speed) + screen_scroll # 子弹射出也要一起移动
  384. # 检测子弹与地面瓷砖的碰撞
  385. for tile in world.obstacle_list:
  386. if tile[1].colliderect(self.rect):
  387. self.kill()
  388. # 检测子弹的碰撞
  389. if pygame.sprite.spritecollide(player, bullet_group, False):
  390. if player.alive:
  391. player.health -= 5
  392. self.kill()
  393. for enemy in enemy_group:
  394. if pygame.sprite.spritecollide(enemy, bullet_group, False):
  395. if enemy.alive:
  396. enemy.health -= 25
  397. self.kill()
  398. # 创建手雷
  399. class Grenade(pygame.sprite.Sprite):
  400. def __init__(self, x, y, direction):
  401. super().__init__()
  402. self.timer = 90
  403. self.vel_y = -11
  404. self.speed = 7
  405. self.image = grenade_img
  406. self.rect = self.image.get_rect()
  407. self.rect.center = (x, y)
  408. self.direction = direction
  409. self.width = self.image.get_width()
  410. self.height = self.image.get_height()
  411. def update(self):
  412. self.vel_y += GRAVITY
  413. dx = self.direction * self.speed
  414. dy = self.vel_y
  415.  
  416. # 检测手雷与每个瓷砖的碰撞
  417. for tile in world.obstacle_list:
  418. # 检测与瓷砖墙壁的碰撞
  419. if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height):
  420. self.direction *= -1
  421. dx = self.direction * self.speed
  422. # 检测与y方向上的碰撞
  423. if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height):
  424. self.speed = 0
  425. # 检测与地面底部的碰撞向下反弹
  426. if self.vel_y < 0:
  427. self.vel_y = 0
  428. dy = tile[1].bottom - self.rect.top
  429. # 检测与地面顶部的碰撞
  430. elif self.vel_y >= 0:
  431. self.vel_y = 0
  432. self.in_air = False
  433. dy = tile[1].top - self.rect.bottom
  434. # 更新受累的位置
  435. self.rect.x += dx + screen_scroll # 手雷扔出也需要加上滚动的量
  436. self.rect.y += dy
  437. # 手雷爆炸冷却时间
  438. self.timer -= 1
  439. if self.timer <= 0:
  440. self.kill()
  441. grenade_fx.play()
  442. explosion = Explosion(self.rect.x, self.rect.y, 0.8)
  443. explosion_group.add(explosion)
  444. # 爆炸后对任何人在一定的范围内都有伤害
  445. if abs(self.rect.centerx - player.rect.centerx) < TILE_SIZE * 2 and abs(self.rect.centery - player.rect.centery) < TILE_SIZE * 2:
  446. player.health -= 10
  447. for enemy in enemy_group:
  448. # if abs(self.rect.centerx - enemy.rect.centerx) < TILE_SIZE and # abs(self.rect.centery - enemy.rect.centery) < TILE_SIZE:
  449. # enemy.health -= 100
  450. if abs(self.rect.centerx - enemy.rect.centerx) < TILE_SIZE * 2 and abs(self.rect.centery - enemy.rect.centery) < TILE_SIZE * 2:
  451. enemy.health -= 50
  452. # 创建地图的类
  453. class World():
  454. def __init__(self):
  455. self.obstacle_list = [] # 障碍列表
  456. def process_data(self, data1):
  457. self.level_length = len(data1[0])
  458. # 迭代加载数据的每个值
  459. for y, row in enumerate(data1):
  460. for x, tile in enumerate(row):
  461. if tile >= 0:
  462. img = img_list[tile]
  463. img_rect = img.get_rect()
  464. img_rect.x = x * TILE_SIZE
  465. img_rect.y = y * TILE_SIZE
  466. tile_data = (img, img_rect)
  467. if tile >= 0 and tile <= 8: # 地面泥块
  468. self.obstacle_list.append(tile_data)
  469. elif tile >= 9 and tile <= 10: # 水
  470. water = Water(img, x * TILE_SIZE, y * TILE_SIZE)
  471. water_group.add(water)
  472. elif tile >= 11 and tile <= 14: # 装饰类型的
  473. decoration = Decoration(img, x * TILE_SIZE, y * TILE_SIZE)
  474. decoration_group.add(decoration)
  475. elif tile == 15: # 创建玩家自己
  476. player = Soldier("player", x * TILE_SIZE, y * TILE_SIZE, 1.65, 5, 30, 10)
  477. health_bar = HealthBar(10, 10, player.health, player.health)
  478. elif tile == 16: # 创建敌人
  479. enemy = Soldier("enemy", x * TILE_SIZE, y * TILE_SIZE, 1.65, 2, 20, 5)
  480. enemy_group.add(enemy)
  481. elif tile == 17:
  482. # 收集弹药
  483. item_box = ItemBox("Ammo", x * TILE_SIZE, y * TILE_SIZE)
  484. item_box_group.add(item_box)
  485. elif tile == 18:
  486. # 收集手雷
  487. item_box = ItemBox("Grenade", x * TILE_SIZE, y * TILE_SIZE)
  488. item_box_group.add(item_box)
  489. elif tile == 19:
  490. # 收集医药
  491. item_box = ItemBox("Health", x * TILE_SIZE, y * TILE_SIZE)
  492. item_box_group.add(item_box)
  493. elif tile == 20: # 出口
  494. exit = Exit(img, x * TILE_SIZE, y * TILE_SIZE)
  495. exit_group.add(exit)
  496. return player, health_bar
  497. def draw(self):
  498. for tile in self.obstacle_list:
  499. tile[1][0] += screen_scroll
  500. screen.blit(tile[0], tile[1])
  501. # 装饰品类
  502. class Decoration(pygame.sprite.Sprite):
  503. def __init__(self, img, x, y):
  504. super().__init__()
  505. self.image = img
  506. self.rect = self.image.get_rect()
  507. self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
  508. def update(self):
  509. self.rect.x += screen_scroll
  510. # 创建水类
  511. class Water(pygame.sprite.Sprite):
  512. def __init__(self, img, x, y):
  513. super().__init__()
  514. self.image = img
  515. self.rect = self.image.get_rect()
  516. self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
  517. def update(self):
  518. self.rect.x += screen_scroll
  519.  
  520. # 创建出口
  521. class Exit(pygame.sprite.Sprite):
  522. def __init__(self, img, x, y):
  523. super().__init__()
  524. self.image = img
  525. self.rect = self.image.get_rect()
  526. self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height()))
  527. def update(self):
  528. self.rect.x += screen_scroll
  529.  
  530. # 创建爆炸类
  531. class Explosion(pygame.sprite.Sprite):
  532. def __init__(self, x, y, scale):
  533. super().__init__()
  534. self.images = []
  535. for num in range(1, 6):
  536. img = pygame.image.load(f"img/explosion/exp{num}.png").convert_alpha()
  537. img = pygame.transform.scale(img, (int(img.get_width() * scale), int(img.get_height() * scale)))
  538. self.images.append(img)
  539. self.frame_index = 0
  540. self.image = self.images[self.frame_index]
  541. self.rect = self.image.get_rect()
  542. self.rect.center = (x, y)
  543. self.counter = 0
  544.  
  545. def update(self):
  546. # 爆炸加滚动
  547. self.rect.x += screen_scroll
  548. EXPLOSION_SPEED = 4
  549. # 更新爆炸动画
  550. self.counter += 1
  551. if self.counter >= EXPLOSION_SPEED:
  552. self.counter = 0
  553. self.frame_index += 1
  554. # 检测爆炸完成后删除爆炸
  555. if self.frame_index >= len(self.images):
  556. self.kill()
  557. else:
  558. self.image = self.images[self.frame_index]
  559.  
  560.  
  561. class ScreenFade():
  562. def __init__(self, direction, color, speed):
  563. self.direction = direction
  564. self.color = color
  565. self.speed = speed
  566. self.fade_counter = 0
  567.  
  568. def fade(self):
  569. fade_complete = False # 定义判断是否完成覆盖
  570. self.fade_counter += self.speed
  571. if self.direction == 1: #所有类型的淡入淡出
  572. pygame.draw.rect(screen, self.color, (0 - self.fade_counter, 0, SCREEN_WIDTH // 2, SCREEN_HEIGHT)) # 向左拉开序幕
  573. pygame.draw.rect(screen, self.color, (SCREEN_WIDTH // 2 + self.fade_counter, 0, SCREEN_WIDTH, SCREEN_HEIGHT)) # 向右拉开序幕
  574. pygame.draw.rect(screen, self.color, (0, 0 - self.fade_counter, SCREEN_WIDTH, SCREEN_HEIGHT // 2))
  575. pygame.draw.rect(screen, self.color, (0, SCREEN_HEIGHT // 2 + self.fade_counter, SCREEN_WIDTH, SCREEN_HEIGHT))
  576. if self.direction == 2: # 垂直向下淡入
  577. pygame.draw.rect(screen, self.color, (0, 0, SCREEN_WIDTH, 0 + self.fade_counter))
  578. if self.fade_counter >= SCREEN_WIDTH:
  579. fade_complete = True
  580. return fade_complete
  581.  
  582. # 创建淡入淡出
  583. intro_fade = ScreenFade(1, BLACK, 4)
  584. death_fade = ScreenFade(2, PINK, 4)
  585.  
  586. #创建开始、退出、重置菜单按钮
  587. start_button = button.Button(SCREEN_WIDTH // 2 - 130, SCREEN_HEIGHT // 2 - 150, start_img, 1)
  588. exit_button = button.Button(SCREEN_WIDTH // 2 - 110, SCREEN_HEIGHT // 2 + 50, exit_img, 1)
  589. restart_button = button.Button(SCREEN_WIDTH // 2 - 70, SCREEN_HEIGHT // 2 - 50, restart_img, 1)
  590.  
  591. #创建群组
  592. enemy_group = pygame.sprite.Group()
  593. bullet_group = pygame.sprite.Group()
  594. grenade_group = pygame.sprite.Group()
  595. explosion_group = pygame.sprite.Group()
  596. item_box_group = pygame.sprite.Group()
  597. decoration_group = pygame.sprite.Group()
  598. water_group = pygame.sprite.Group()
  599. exit_group = pygame.sprite.Group()
  600. # 创建空的瓷砖列表
  601. world_data = []
  602. for row in range(ROWS):
  603. r = [-1] * COLS
  604. world_data.append(r)
  605. # 加载级别数据创建地图
  606. with open(f"level{level}_data.csv", newline="") as csvfile:
  607. reader = csv.reader(csvfile, delimiter=",")
  608. for y, row in enumerate(reader):
  609. for x, tile in enumerate(row):
  610. world_data[y][x] = int(tile)
  611. world = World()
  612. player, health_bar = world.process_data(world_data)
  613. run = True
  614. while run:
  615. clock.tick(FPS)
  616. if start_game == False:
  617. # 显示主菜单
  618. # 画菜单
  619. screen.fill(BG)
  620. # 增加按钮
  621. if start_button.draw(screen):
  622. start_game = True
  623. start_intro = True
  624. if exit_button.draw(screen):
  625. run = False
  626. else:
  627. draw_bg()
  628. # 显示地图
  629. world.draw()
  630. # 显示血条
  631. health_bar.draw(player.health)
  632. # 显示弹药量
  633. draw_text("AMMO: ", font, WHITE, 10, 35)
  634. for x in range(player.ammo):
  635. screen.blit(bullet_img, (90 + (x * 10), 40))
  636. # 显示手雷量
  637. draw_text("GRENADES: ", font, WHITE, 10, 60)
  638. for x in range(player.grenades):
  639. screen.blit(grenade_img, (135 + (x * 15), 60))
  640. # 显示血条量
  641. # draw_text(f"AMMO: {player.ammo}", font, WHITE, 10, 35)
  642.  
  643. player.update()
  644. player.draw()
  645.  
  646. for enemy in enemy_group:
  647. enemy.ai()
  648. enemy.update()
  649. enemy.draw()
  650.  
  651. # 更新和画组
  652. bullet_group.update()
  653. grenade_group.update()
  654. explosion_group.update()
  655. item_box_group.update()
  656. decoration_group.update()
  657. water_group.update()
  658. exit_group.update()
  659. bullet_group.draw(screen)
  660. grenade_group.draw(screen)
  661. explosion_group.draw(screen)
  662. item_box_group.draw(screen)
  663. decoration_group.draw(screen)
  664. water_group.draw(screen)
  665. exit_group.draw(screen)
  666. # 显示淡入
  667. if start_intro:
  668. if intro_fade.fade():
  669. start_intro = False
  670. intro_fade.fade_counter = 0
  671.  
  672. # 更新玩家的动作
  673. if player.alive:
  674. # 发射子弹
  675. if shoot:
  676. player.shoot()
  677. # 扔手雷
  678. elif grenade and grenade_thrown == False and player.grenades > 0:
  679. grenade = Grenade(player.rect.centerx + (0.5 * player.rect.size[0] * player.direction),
  680. player.rect.top, player.direction)
  681. grenade_group.add(grenade)
  682. player.grenades -= 1
  683. grenade_thrown = True
  684.  
  685. if player.in_air:
  686. player.update_action(2)
  687. elif moving_left or moving_right:
  688. player.update_action(1)
  689. else:
  690. player.update_action(0)
  691.  
  692. screen_scroll, level_complete = player.move(moving_left, moving_right)
  693. bg_scroll -= screen_scroll
  694. # 检测玩家是否通过该级别,把二位列表的具体位置(某一行的某一列)赋值
  695. if level_complete:
  696. start_intro = True
  697. level += 1
  698. bg_scroll = 0
  699. world_data = reset_level()
  700. if level <= MAX_LEVELS:
  701. # 加载级别数据创建地图
  702. with open(f"level{level}_data.csv", newline="") as csvfile:
  703. reader = csv.reader(csvfile, delimiter=",")
  704. for y, row in enumerate(reader):
  705. for x, tile in enumerate(row):
  706. world_data[y][x] = int(tile)
  707.  
  708. world = World()
  709. player, health_bar = world.process_data(world_data)
  710.  
  711. else:
  712. screen_scroll = 0
  713. if death_fade.fade(): # 完成覆盖后才出现按钮中间时间过渡
  714. if restart_button.draw(screen):
  715. death_fade.fade_counter = 0 # 计数清零
  716. start_intro = True
  717. bg_scroll = 0
  718. world_data = reset_level()
  719. # 加载级别数据创建地图
  720. with open(f"level{level}_data.csv", newline="") as csvfile:
  721. reader = csv.reader(csvfile, delimiter=",")
  722. for y, row in enumerate(reader):
  723. for x, tile in enumerate(row):
  724. world_data[y][x] = int(tile)
  725.  
  726. world = World()
  727. player, health_bar = world.process_data(world_data)
  728.  
  729.  
  730. for event in pygame.event.get():
  731. # 退出游戏
  732. if event.type == pygame.QUIT:
  733. run = False
  734. pygame.quit()
  735. sys.exit()
  736. # 键盘按键
  737. if event.type == pygame.KEYDOWN:
  738. if event.key == pygame.K_a:
  739. moving_left = True
  740. if event.key == pygame.K_d:
  741. moving_right = True
  742. if event.key == pygame.K_SPACE:
  743. shoot = True
  744. if event.key == pygame.K_q:
  745. grenade = True
  746. if event.key == pygame.K_w and player.alive:
  747. player.jump = True
  748. jump_fx.play()
  749. if event.key == pygame.K_ESCAPE:
  750. run = False
  751. # 按键释放
  752. if event.type == pygame.KEYUP:
  753. if event.key == pygame.K_a:
  754. moving_left = False
  755. if event.key == pygame.K_d:
  756. moving_right = False
  757. if event.key == pygame.K_SPACE:
  758. shoot = False
  759. if event.key == pygame.K_q:
  760. grenade = False
  761. grenade_thrown = False
  762.  
  763.  
  764. pygame.display.update()

部分游戏截图

到此这篇关于Python开发之射击闯关游戏的实现的文章就介绍到这了,更多相关Python射击闯关游戏内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号