前言
大家好,我是小明呀,今天给大家分享一款我以前特爱玩的游戏,水果大战,今天我就教大家使用python把这款游戏制作出来。我们先来看效果
当我运行代码这个就是第一效果图,还会有熟悉的音乐的声音。然后拖动鼠标就会有水果跳上来。

接下来我们就可以通过鼠标随便切,可以横着切,也可以竖着切,是不是很解压呢?好了,我们直接进入主题,接下来辣条哥把这个水果大战游戏效果跟大家详细介绍。
游戏代码展示
1 import time 2 import math 3 import random 4 import pygame 5 from pygame.constants import * 6 7 pygame.init() 8 class OptionMode(pygame.sprite.Sprite): 9 """ 模式选项类 """ 10 11 def __init__(self, window, x, y, image_path, turn_angel, flag): 12 pygame.sprite.Sprite.__init__(self) 13 self.window = window 14 self.image = pygame.image.load(image_path) 15 self.rect = self.image.get_rect() 16 self.rect.x = x 17 self.rect.y = y 18 self.turn_angel = turn_angel 19 self.v_angel = 0 20 self.flag = flag 21 22 def update(self): 23 new_image = pygame.transform.rotate(self.image, -self.v_angel) 24 self.window.blit(new_image, (self.rect.x + self.rect.width / 2 - new_image.get_width() / 2, 25 self.rect.y + self.rect.height / 2 - new_image.get_height() / 2)) 26 self.v_angel += self.turn_angel 27 28 29 class Background(pygame.sprite.Sprite): 30 """ 背景图片 """ 31 32 def __init__(self, window, x, y, image_path): 33 pygame.sprite.Sprite.__init__(self) 34 self.window = window 35 self.image = pygame.image.load(image_path) 36 self.rect = self.image.get_rect() 37 self.rect.x = x 38 self.rect.y = y 39 40 def update(self): 41 self.window.blit(self.image, self.rect) 42 43 44 class ThrowFruit(pygame.sprite.Sprite): 45 """ 被抛出的水果类 """ 46 47 def __init__(self, window, image_path, speed, turn_angel, flag): 48 pygame.sprite.Sprite.__init__(self) 49 50 # 游戏窗口 51 self.window = window 52 53 # 导入水果图像并获取其矩形区域 54 self.image = pygame.image.load(image_path) 55 self.rect = self.image.get_rect() 56 57 # 水果抛出时x坐标取随机数 58 self.rect.x = random.randint(0, Manager.WIDTH) 59 60 # 水果初始y坐标 61 self.rect.y = Manager.HEIGHT 62 63 # 抛出时速度 64 self.speed = speed 65 66 # 旋转速度 67 self.turn_angel = turn_angel 68 69 # 水果抛出时与窗口下水平线的夹角弧度,因为要用到随机函数, 所以取整数, 使用时除以100 70 self.throw_angel = 157 71 72 # 水果抛出后所经历的时间, 初始化为0 73 self.fruit_t = 0 74 75 # 旋转的总角度 76 self.v_angel = 0 77 78 # 水果抛出时的初速度 79 self.v0 = 6 80 81 # 水果标记 82 self.flag = flag 83 84 def update(self): 85 """ 水果运动状态更新 """ 86 87 # 如果水果的初始X坐标位于窗口左边区域, 取抛出时弧度在1.4 ~ 1.57 之间(70度至90度之间) 88 if self.rect.x <= Manager.WIDTH / 2: 89 self.throw_angel = random.randint(140, 157) 90 91 # 如果水果的初始X坐标位于窗口右侧区域, 取抛出时弧度在1.57 * 100 ~ 1.75 * 100之间(90度至110度之间) 92 elif self.rect.x >= Manager.WIDTH / 2: 93 self.throw_angel = random.randint(157, 175) 94 95 # 水果旋转后的新图像 96 new_fruit = pygame.transform.rotate(self.image, self.v_angel) 97 98 # 将旋转后的新图像贴入游戏窗口, 注意, 旋转后的图像尺寸以及像素都不一样了(尺寸变大了), 所以坐标需要进行适当处理 99 self.window.blit(new_fruit, (self.rect.x + self.rect.width / 2 - new_fruit.get_width() / 2, 100 self.rect.y + self.rect.height / 2 - new_fruit.get_height() / 2)) 101 102 # 水果抛出后的运动时水平匀速运动以及竖直向上的变速运动到达最高点时下落, 所以可以判断水果做的是斜上抛运动 103 # 可以利用重力加速度来求出每隔一段时间水果运动后的y坐标 104 # 公式: v0 * t * sin(α) - g * t^2 / 2 105 if self.rect.y >= Manager.HEIGHT + self.rect.height: 106 if self.flag != 5: 107 Manager.classic_miss += 1 108 self.kill() 109 self.rect.y -= self.v0 * self.fruit_t * math.sin(self.throw_angel / 100) - (Manager.G * 110 self.fruit_t ** 2 / 10) / 2 111 112 # 计算水果在水平方向的位移之后的X坐标, 匀速运动,没啥好说的 113 # 公式: v0 * t * cos(α) 114 self.rect.x += self.v0 * self.fruit_t * math.cos(self.throw_angel / 100) 115 116 # 累加经过的时间 117 self.fruit_t += 0.1 118 119 # 累加旋转总角度 120 self.v_angel += self.turn_angel 121 122 123 class HalfFruit(pygame.sprite.Sprite): 124 """ 水果切片类 """ 125 126 def __init__(self, window, image_path, x, y, turn_angel, v_angel, v0): 127 pygame.sprite.Sprite.__init__(self) 128 # 游戏窗口 129 self.window = window 130 131 # 导入水果图像并获取其矩形区域 132 self.image = pygame.image.load(image_path) 133 self.rect = self.image.get_rect() 134 135 # 水果被切开后的 136 self.rect.x = x 137 138 # 水果初始y坐标 139 self.rect.y = y 140 141 # 旋转速度 142 self.turn_angel = turn_angel 143 144 # 水果被切开时开始计时 145 self.fruit_t = 0 146 147 # 旋转的总角度 148 self.v_angel = v_angel 149 150 # 水果抛出时的水平初速度 151 self.v0 = v0 152 153 def update(self): 154 """ 水果运动状态更新 """ 155 156 # 如果水果的初始X坐标位于窗口左边区域, 取抛出时弧度在1.4 ~ 1.57 之间(70度至90度之间) 157 # if self.rect.x <= v1版本.Manager.WIDTH / 2 - self.rect.width: 158 # self.throw_angel = random.randint(140, 157) 159 # 160 # 如果水果的初始X坐标位于窗口右侧区域, 取抛出时弧度在1.57 * 100 ~ 1.75 * 100之间(90度至110度之间) 161 # elif self.rect.x >= v1版本.Manager.WIDTH / 2 + self.rect.width: 162 # self.throw_angel = random.randint(157, 175) 163 164 # 水果旋转后的新图像 165 new_fruit = pygame.transform.rotate(self.image, self.v_angel) 166 167 # 将旋转后的新图像贴入游戏窗口, 注意, 旋转后的图像尺寸以及像素都不一样了(尺寸变大了), 所以坐标需要进行适当处理 168 self.window.blit(new_fruit, (self.rect.x + self.rect.width / 2 - new_fruit.get_width() / 2, 169 self.rect.y + self.rect.height / 2 - new_fruit.get_height() / 2)) 170 171 # 水果被切开之后的切片做的是平抛运动 172 # 可以利用重力加速度来求出每隔一段时间水果运动后的y坐标 173 # 公式: h += v0 * t * sin(α) - g * t^2 / 2 174 if self.rect.y >= Manager.HEIGHT: 175 self.kill() 176 self.rect.y += Manager.G * self.fruit_t ** 2 / 2 177 178 # 计算水果在水平方向的位移之后的X坐标, 匀速运动,没啥好说的 179 # 公式: v0 * t * cos(α) 180 self.rect.x += self.v0 * self.fruit_t 181 182 # 累加经过的时间 183 self.fruit_t += 0.01 184 185 # 累加旋转总角度 186 self.v_angel += self.turn_angel 187 188 189 class Bgm(object): 190 """ 游戏音乐类 """ 191 192 def __init__(self): 193 pygame.mixer.init() 194 195 def play_menu(self): 196 pygame.mixer.music.load("./sound/menu.ogg") 197 pygame.mixer.music.play(-1, 0) 198 199 def play_classic(self): 200 pygame.mixer.music.load("./sound/start.mp3") 201 pygame.mixer.music.play(1, 0) 202 203 def play_throw(self): 204 pygame.mixer.music.load("./sound/throw.mp3") 205 pygame.mixer.music.play(1, 0) 206 207 def play_splatter(self): 208 pygame.mixer.music.load("./sound/splatter.mp3") 209 pygame.mixer.music.play(1, 0) 210 211 def play_over(self): 212 pygame.mixer.music.load("./sound/over.mp3") 213 pygame.mixer.music.play(1, 0) 214 215 216 class Knife(object): 217 def __init__(self, window): 218 self.window = window 219 self.apple_flash = pygame.image.load("./images/apple_flash.png") 220 self.banana_flash = pygame.image.load("./images/banana_flash.png") 221 self.peach_flash = pygame.image.load("./images/peach_flash.png") 222 self.sandia_flash = pygame.image.load("./images/sandia_flash.png") 223 224 def show_apple_flash(self, x, y): 225 self.window.blit(self.apple_flash, (x, y)) 226 227 def show_banana_flash(self, x, y): 228 self.window.blit(self.banana_flash, (x, y)) 229 230 def show_peach_flash(self, x, y): 231 self.window.blit(self.peach_flash, (x, y)) 232 233 def show_sandia_flash(self, x, y): 234 self.window.blit(self.sandia_flash, (x, y)) 235 236 237 class Manager(object): 238 # 窗口尺寸 239 WIDTH = 640 240 HEIGHT = 480 241 242 # 游戏中的定时器常量 243 THROWFRUITTIME = pygame.USEREVENT 244 pygame.time.set_timer(THROWFRUITTIME, 3000) 245 246 # 重力加速度, 取整数,使用时除以10 247 G = random.randint(21, 23) 248 249 # 经典模式miss掉的水果数 250 classic_miss = 0 251 252 def __init__(self): 253 # 生成游戏窗口 254 self.window = pygame.display.set_mode((Manager.WIDTH, Manager.HEIGHT)) 255 self.window_icon = pygame.image.load("./images/score.png") 256 pygame.display.set_icon(self.window_icon) 257 pygame.display.set_caption("FruitNinja") 258 259 # 游戏分数 260 self.classic_score = 0 261 self.zen_score = 0 262 263 # 创建游戏中用到的的精灵组 264 self.background_list = pygame.sprite.Group() 265 self.circle_option = pygame.sprite.Group() 266 self.option_fruit_list = pygame.sprite.Group() 267 self.fruit_half_list = pygame.sprite.Group() 268 self.throw_fruit_list = pygame.sprite.Group() 269 270 # 导入背景图像并添加入背景精灵组 271 self.background = Background(self.window, 0, 0, "./images/background.jpg") 272 self.home_mask = Background(self.window, 0, 0, "./images/home-mask.png") 273 self.logo = Background(self.window, 20, 10, "./images/logo.png") 274 self.ninja = Background(self.window, Manager.WIDTH - 320, 45, "./images/ninja.png") 275 self.home_desc = Background(self.window, 20, 135, "./images/home-desc.png") 276 277 self.background_list.add(self.background) 278 self.background_list.add(self.home_mask) 279 self.background_list.add(self.logo) 280 self.background_list.add(self.ninja) 281 self.background_list.add(self.home_desc) 282 283 # 创建旋转的圈并添加进精灵组 284 self.dojo = OptionMode(self.window, Manager.WIDTH - 600, Manager.HEIGHT - 250, "./images/dojo.png", 3, None) 285 self.new_game = OptionMode(self.window, Manager.WIDTH - 405, Manager.HEIGHT - 250, "./images/new-game.png", 3, 286 None) 287 self.game_quit = OptionMode(self.window, Manager.WIDTH - 160, Manager.HEIGHT - 150, "./images/quit.png", -3, 288 None) 289 290 self.circle_option.add(self.dojo) 291 self.circle_option.add(self.new_game) 292 self.circle_option.add(self.game_quit) 293 294 # 创建主菜单界面旋转的水果并添加进精灵组 295 self.home_sandia = OptionMode(self.window, Manager.WIDTH - 405 + self.new_game.rect.width / 2 - 49, 296 Manager.HEIGHT - 250 + self.new_game.rect.height / 2 - 85 / 2, 297 "./images/sandia.png", -3, "option_sandia") 298 self.home_peach = OptionMode(self.window, Manager.WIDTH - 600 + self.dojo.rect.width / 2 - 31, 299 Manager.HEIGHT - 250 + self.dojo.rect.height / 2 - 59 / 2, 300 "./images/peach.png", -3, "option_peach") 301 self.home_boom = OptionMode(self.window, Manager.WIDTH - 160 + self.game_quit.rect.width / 2 - 66 / 2, 302 Manager.HEIGHT - 150 + self.game_quit.rect.height / 2 - 68 / 2, 303 "./images/boom.png", 3, "option_boom") 304 self.option_fruit_list.add(self.home_sandia) 305 self.option_fruit_list.add(self.home_peach) 306 self.option_fruit_list.add(self.home_boom) 307 308 # 设置定时器 309 self.clock = pygame.time.Clock() 310 311 # 模式标记 312 self.mode_flag = 0 313 314 # 音效 315 self.bgm = Bgm() 316 317 # 刀光 318 self.knife = Knife(self.window) 319 320 def create_fruit(self): 321 """ 创建水果 """ 322 if self.mode_flag == 1: 323 boom_prob = random.randint(0, 10) 324 if boom_prob == 8: 325 self.bgm.play_throw() 326 boom = ThrowFruit(self.window, "./images/boom.png", None, 5, 5) 327 self.throw_fruit_list.add(boom) 328 329 fruit_image_path = ["./images/sandia.png", "./images/peach.png", 330 "./images/banana.png", "./images/apple.png", 331 "./images/basaha.png"] 332 fruit_number = random.randint(1, 4) 333 for n in range(fruit_number): 334 rand_fruit_index = random.randint(0, len(fruit_image_path) - 1) 335 self.bgm.play_throw() 336 fruit = ThrowFruit(self.window, fruit_image_path[rand_fruit_index], None, 5, rand_fruit_index) 337 self.throw_fruit_list.add(fruit) 338 339 def create_fruit_half(self, fruit_flag, fruit_x, fruit_y, turn_angel, v_angel): 340 if fruit_flag == "option_sandia": 341 """ 经典模式西瓜被切开 """ 342 fruit_left = HalfFruit(self.window, "./images/sandia-1.png", fruit_x - 50, fruit_y, turn_angel, v_angel, 343 -5) 344 fruit_right = HalfFruit(self.window, "./images/sandia-2.png", fruit_x + 50, fruit_y, -turn_angel, v_angel, 345 5) 346 self.fruit_half_list.add(fruit_left) 347 self.fruit_half_list.add(fruit_right) 348 if fruit_flag == "option_peach": 349 """ 禅宗模式的梨被切开 """ 350 fruit_left = HalfFruit(self.window, "./images/peach-1.png", fruit_x - 50, fruit_y, turn_angel, v_angel, -5) 351 fruit_right = HalfFruit(self.window, "./images/peach-2.png", fruit_x + 50, fruit_y, -turn_angel, v_angel, 352 5) 353 self.fruit_half_list.add(fruit_left) 354 self.fruit_half_list.add(fruit_right) 355 356 if fruit_flag == 0: 357 """ 西瓜被切开 """ 358 fruit_left = HalfFruit(self.window, "./images/sandia-1.png", fruit_x - 50, fruit_y, turn_angel, v_angel, -5) 359 fruit_right = HalfFruit(self.window, "./images/sandia-2.png", fruit_x + 50, fruit_y, -turn_angel, v_angel, 360 5) 361 self.fruit_half_list.add(fruit_left) 362 self.fruit_half_list.add(fruit_right) 363 if fruit_flag == 1: 364 """ 梨被切开 """ 365 fruit_left = HalfFruit(self.window, "./images/peach-1.png", fruit_x - 50, fruit_y, turn_angel, v_angel, -5) 366 fruit_right = HalfFruit(self.window, "./images/peach-2.png", fruit_x + 50, fruit_y, -turn_angel, v_angel, 367 5) 368 self.fruit_half_list.add(fruit_left) 369 self.fruit_half_list.add(fruit_right) 370 if fruit_flag == 2: 371 """ 香蕉被切开 """ 372 fruit_left = HalfFruit(self.window, "./images/banana-1.png", fruit_x - 50, fruit_y, turn_angel, v_angel, -5) 373 fruit_right = HalfFruit(self.window, "./images/banana-2.png", fruit_x + 50, fruit_y, -turn_angel, v_angel, 374 5) 375 self.fruit_half_list.add(fruit_left) 376 self.fruit_half_list.add(fruit_right) 377 if fruit_flag == 3: 378 """ 苹果被切开 """ 379 fruit_left = HalfFruit(self.window, "./images/apple-1.png", fruit_x - 50, fruit_y, turn_angel, v_angel, -5) 380 fruit_right = HalfFruit(self.window, "./images/apple-2.png", fruit_x + 50, fruit_y, -turn_angel, v_angel, 381 5) 382 self.fruit_half_list.add(fruit_left) 383 self.fruit_half_list.add(fruit_right) 384 if fruit_flag == 4: 385 """ 草莓被切开 """ 386 fruit_left = HalfFruit(self.window, "./images/basaha-1.png", fruit_x - 50, fruit_y, turn_angel, v_angel, -5) 387 fruit_right = HalfFruit(self.window, "./images/basaha-2.png", fruit_x + 50, fruit_y, -turn_angel, v_angel, 388 5) 389 self.fruit_half_list.add(fruit_left) 390 self.fruit_half_list.add(fruit_right) 391 392 def impact_check(self): 393 """ 碰撞检测 """ 394 for item in self.option_fruit_list: 395 """ 主页的模式选择 """ 396 mouse_pos = pygame.mouse.get_pos() 397 if mouse_pos[0] > item.rect.left and mouse_pos[0] < item.rect.right \ 398 and mouse_pos[1] > item.rect.top and mouse_pos[1] < item.rect.bottom: 399 self.bgm.play_splatter() 400 self.create_fruit_half(item.flag, item.rect.x, item.rect.y, item.turn_angel, item.v_angel) 401 self.option_fruit_list.remove_internal(item) 402 if item.flag == "option_sandia": 403 self.mode_flag = 1 404 return 1 405 elif item.flag == "option_peach": 406 self.mode_flag = 2 407 return 2 408 elif item.flag == "option_boom": 409 return 0 410 411 for item in self.throw_fruit_list: 412 """ 游戏开始后判断水果是否被切到 """ 413 mouse_pos = pygame.mouse.get_pos() 414 if mouse_pos[0] > item.rect.left and mouse_pos[0] < item.rect.right \ 415 and mouse_pos[1] > item.rect.top and mouse_pos[1] < item.rect.bottom: 416 if item.flag == 0: 417 self.knife.show_sandia_flash(item.rect.x, item.rect.y) 418 if item.flag == 1: 419 self.knife.show_peach_flash(item.rect.x, item.rect.y) 420 if item.flag == 2: 421 self.knife.show_banana_flash(item.rect.x, item.rect.y) 422 if item.flag == 3: 423 self.knife.show_apple_flash(item.rect.x, item.rect.y) 424 if item.flag == 4: 425 self.knife.show_apple_flash(item.rect.x, item.rect.y) 426 427 if self.mode_flag == 1: 428 self.classic_score += 2 429 if self.mode_flag == 2: 430 self.zen_score += 2 431 print(self.zen_score) 432 self.bgm.play_splatter() 433 self.create_fruit_half(item.flag, item.rect.x, item.rect.y, item.turn_angel, item.v_angel) 434 self.throw_fruit_list.remove_internal(item) 435 if item.flag == 5: 436 return 3 437 438 def check_key(self): 439 """ 监听事件 """ 440 for event in pygame.event.get(): 441 if event.type == QUIT: 442 pygame.quit() 443 exit() 444 elif event.type == Manager.THROWFRUITTIME and self.mode_flag == 1: 445 self.create_fruit() 446 elif event.type == Manager.THROWFRUITTIME and self.mode_flag == 2: 447 self.create_fruit() 448 449 def classic_mode(self): 450 """ 经典模式 """ 451 pygame.font.init() 452 self.bgm.play_classic() 453 score_image = Background(self.window, 10, 5, "./images/score.png") 454 text = pygame.font.Font("./images/simhei.ttf", 20) # 设置分数显示的字体 455 x_times = pygame.sprite.Group() 456 miss_times = pygame.sprite.Group() 457 xxx = Background(self.window, Manager.WIDTH - 30, 5, "./images/xxx.png") 458 xx = Background(self.window, Manager.WIDTH - 60, 5, "./images/xx.png") 459 x = Background(self.window, Manager.WIDTH - 90, 5, "./images/x.png") 460 x_times.add(xxx) 461 x_times.add(xx) 462 x_times.add(x) 463 464 while True: 465 # 设置游戏帧率 466 self.clock.tick(60) 467 pygame.display.update() 468 self.check_key() 469 self.background_list.sprites()[0].update() 470 score_image.update() 471 text_score = text.render("%d" % self.classic_score, 1, (0, 255, 0)) 472 self.window.blit(text_score, (50, 10)) 473 x_times.update() 474 miss_times.update() 475 temp_flag = self.impact_check() 476 if temp_flag == 3: 477 return 478 self.throw_fruit_list.update() 479 self.fruit_half_list.update() 480 if Manager.classic_miss == 1: 481 xf = Background(self.window, Manager.WIDTH - 90, 5, "./images/xf.png") 482 miss_times.add(xf) 483 elif Manager.classic_miss == 2: 484 xf = Background(self.window, Manager.WIDTH - 90, 5, "./images/xf.png") 485 miss_times.add(xf) 486 xxf = Background(self.window, Manager.WIDTH - 60, 5, "./images/xxf.png") 487 miss_times.add(xxf) 488 elif Manager.classic_miss >= 3: 489 self.bgm.play_over() 490 Manager.classic_miss = 0 491 return 492 493 def zen_mode(self): 494 """ 禅宗模式 """ 495 self.bgm.play_classic() 496 497 # 记录分数 498 record_time = 0 499 while True: 500 # 设置游戏帧率 501 self.clock.tick(60) 502 self.check_key() 503 self.background_list.sprites()[0].update() 504 self.impact_check() 505 self.throw_fruit_list.update() 506 self.fruit_half_list.update() 507 pygame.display.update() 508 509 if record_time == 3000: 510 return 511 record_time += 1 512 513 def main(self): 514 """ 主页 """ 515 self.bgm.play_menu() 516 while True: 517 # 设置游戏帧率 518 self.clock.tick(60) 519 self.background_list.update() 520 self.circle_option.update() 521 self.option_fruit_list.update() 522 self.fruit_half_list.update() 523 524 temp_flag = self.impact_check() 525 pygame.display.update() 526 if temp_flag == 1: 527 self.classic_mode() 528 self.__init__() 529 self.bgm.play_over() 530 self.bgm.play_menu() 531 532 if temp_flag == 2: 533 self.zen_mode() 534 self.__init__() 535 self.bgm.play_over() 536 self.bgm.play_menu() 537 538 elif temp_flag == 0: 539 pygame.quit() 540 exit() 541 elif temp_flag == 3: 542 self.__init__() 543 self.bgm.play_over() 544 self.bgm.play_menu() 545 self.check_key() 546 547 548 if __name__ == '__main__': 549 manager = Manager() 550 manager.main()
总结:
这个是全部的python代码,里面使用了python的math库,random库,还有pygame,就只用了这三个库,我们就能制作一个水果大战游戏是不是python还是很牛X的。里面还有多代码,那么在这个里面的我也每一行代码都帮大家写好注释了,大家可以看注释也是能看明白的。
除了代码之外,当然还有我们很多的图片文件,还有配置资源。

里面包括有图片文件,有音乐文件,还有代码文件。有了这些文件,在加上我们前面的代码,水果大战游戏就制作完成啦。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现