前言
大家好,今天给大家分享一款我以前特爱玩的游戏,水果大战,今天我就教大家使用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的。里面还有多代码,那么在这个里面的我也每一行代码都帮大家写好注释了,大家可以看注释也是能看明白的。
除了代码之外,当然还有我们很多的图片文件,还有配置资源。
里面包括有图片文件,有音乐文件,还有代码文件。有了这些文件,在加上我们前面的代码,水果大战游戏就制作完成啦。