Python实践-保护小雪乃

写在前面

Python的基础学习已经告一段落了,为了加强效果,实行第二阶段计划,编写一个名为「保护小雪乃」的游戏,来增强自己的代码能力。(其实是学习外星人入侵之后改编的)

Github开源项目地址

保护小雪乃

背景

大老师军团来欺负雪乃了!怎么能看到世界第一可爱的雪之下雪乃受到欺负呢,快操控小雪乃发射消灭子弹,把大老师军团赶走,保护小雪乃!

玩法

  • 点击Play开始游戏

  • 你可以使用左右键来操控小雪乃左右移动,使用空格键发射消灭子弹。

  • 大老师军团会左右移动,到达边界会改变移动方向并向下移动一定距离。

  • 如果任何一个大老师碰到小雪乃或者到达屏幕底端,吓到了小雪乃,那么就视为被欺负成功,减去一次机会。

  • 每消灭一个大老师就会得到一定的小雪乃好感值,好感值无上限,并且会显示在屏幕右上角。

  • 你的最高好感值会被保存下来,显示在屏幕正上方。

  • 每消灭一波大老师军团,大老师愤怒等级+1,会派遣更加强大的大老师军团进攻(移动速度增加,消灭得到的好感值增加)

  • 你的小雪乃拥有三次机会被大老师欺负,如果超过三次,就说明你并没有保护好小雪乃,游戏结束。

  • 祝你好运!

素材

  • 雪之下雪乃 Yukinoshita-Yukino

  • 比企谷八潘 Hikigaya-Hachiman

start

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
import pygame
import functions
from settings import Settings
from yukino import Yukino
from pygame.sprite import Group
from teacher import Teacher
from stats import GameStats
from button import Button
from scoreboard import Scoreboard
def run_game():
pygame.init()
number=Settings()
screen=pygame.display.set_mode((number.screen_width,number.screen_height))
play_button=Button(number,screen,"Play")
pygame.display.set_caption("Yukinoshita-Yukino's attack")
stats=GameStats(number)
sb=Scoreboard(number,screen,stats)
yukino=Yukino(number,screen)
bullets=Group()
teachers=Group()
functions.create_fleet(number,screen,yukino,teachers)
while True:
functions.check_events(number,screen,stats,sb,play_button,yukino,teachers,bullets)
if stats.game_active:
yukino.update()
functions.update_bullets(number,screen,stats,sb,yukino,teachers,bullets)
functions.update_teachers(number,screen,stats,sb,yukino,teachers,bullets)
functions.update_screen(number,screen,stats,sb,yukino,teachers,bullets,play_button)
run_game()
a=input()

stats

1
2
3
4
5
6
7
8
9
10
11
class GameStats():
def __init__(self,number):
self.number=number
self.reset_stats()
self.game_active=False
self.high_score=False
def reset_stats(self):
self.yukino_left=self.number.yukino_limit
self.score=False
self.level=1

functions

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
import sys
import pygame
from bullet import Bullet
from teacher import Teacher
from time import sleep
def check_keydown_events(event,number,screen,yukino,bullets):
if event.key==pygame.K_RIGHT:
yukino.moving_right=True
elif event.key==pygame.K_LEFT:
yukino.moving_left=True
elif event.key==pygame.K_SPACE:
if len(bullets)<number.bullets_allowed:
new_bullet=Bullet(number,screen,yukino)
bullets.add(new_bullet)
elif event.key==pygame.K_q:
sys.exit()
def check_keyup_events(event,yukino):
if event.key==pygame.K_RIGHT:
yukino.moving_right=False
elif event.key==pygame.K_LEFT:
yukino.moving_left=False
def check_events(number,screen,stats,sb,play_button,yukino,teachers,bullets):
for event in pygame.event.get():
if event.type==pygame.QUIT:
sys.exit()
elif event.type==pygame.KEYDOWN:
check_keydown_events(event,number,screen,yukino,bullets)
elif event.type==pygame.KEYUP:
check_keyup_events(event,yukino)
elif event.type==pygame.MOUSEBUTTONDOWN:
mouse_x,mouse_y=pygame.mouse.get_pos()
check_play_button(number,screen,stats,sb,play_button,
yukino,teachers,bullets,mouse_x,mouse_y)
def check_play_button(number,screen,stats,sb,play_button,yukino,teachers,bullets,mouse_x,mouse_y):
button_clicked=play_button.rect.collidepoint(mouse_x,mouse_y)
if button_clicked and not stats.game_active:
number.initialize_dynamic_settings()
pygame.mouse.set_visible(False)
if play_button.rect.collidepoint(mouse_x,mouse_y):
stats.reset_stats()
stats.game_active=True
sb.prep_score()
sb.prep_high_score()
sb.prep_level()
sb.prep_yukino()
teachers.empty()
bullets.empty()
create_fleet(number,screen,yukino,teachers)
yukino.center_yukino()
def check_fleet_edges(number,teachers):
for teacher in teachers.sprites():
if teacher.check_edges():
change_fleet_direction(number,teachers)
break
def check_bullet_teacher_collisions(number,screen,stats,sb,yukino,teachers,bullets):
collisions=pygame.sprite.groupcollide(bullets,teachers,True,True)
if collisions:
for teachers in collisions.values():
stats.score+=number.teacher_points*len(teachers)
sb.prep_score()
check_high_score(stats,sb)
if len(teachers)==False:
bullets.empty()
number.increase_speed()
stats.level+=1
sb.prep_level()
create_fleet(number,screen,yukino,teachers)
def check_teachers_bottom(number,screen,stats,sb,yukino,teachers,bullets):
screen_rect=screen.get_rect()
for teacher in teachers.sprites():
if teacher.rect.bottom>=screen_rect.bottom:
yukino_hit(number,stats,screen,sb,yukino,teacher,bullets)
break
def check_high_score(stats,sb):
if stats.score>stats.high_score:
stats.high_score=stats.score
sb.prep_high_score()
def change_fleet_direction(number,teachers):
for teacher in teachers.sprites():
teacher.rect.y+=number.fleet_drop_speed
number.fleet_direction*=-1
def update_screen(number,screen,stats,sb,yukino,teachers,bullets,play_button):
screen.fill(number.bg_color)
for bullet in bullets.sprites():
bullet.draw_bullet()
yukino.blitme()
teachers.draw(screen)
sb.show_score()
if not stats.game_active:
play_button.draw_button()
pygame.display.flip()
def update_bullets(number,screen,stats,sb,yukino,teachers,bullets):
bullets.update()
for bullet in bullets.copy():
if bullet.rect.bottom<=0:
bullets.remove(bullet)
check_bullet_teacher_collisions(number,screen,stats,sb,yukino,teachers,bullets)
def update_teachers(number,screen,stats,sb,yukino,teachers,bullets):
check_fleet_edges(number,teachers)
teachers.update()
if pygame.sprite.spritecollideany(yukino,teachers):
yukino_hit(number,screen,stats,sb,yukino,teachers,bullets)
check_teachers_bottom(number,screen,stats,sb,yukino,teachers,bullets)
def get_number_teachers_x(number,teacher_width):
available_space_x=number.screen_width-2*teacher_width
number_teachers_x=int(available_space_x/(2*teacher_width))
return number_teachers_x
def get_number_rows(number,yukino_height,teacher_height):
available_space_y=(number.screen_height-(3*teacher_height)-yukino_height)
number_rows=int(available_space_y/(2*teacher_height))
return number_rows
def create_teacher(number,screen,teachers,teacher_number,row_number):
teacher=Teacher(number,screen)
teacher_width=teacher.rect.width
teacher.x=teacher_width+2*teacher_width*teacher_number
teacher.rect.x=teacher.x
teacher.rect.y=teacher.rect.height+2*teacher.rect.height*row_number
teachers.add(teacher)
def create_fleet(number,screen,yukino,teachers):
teacher=Teacher(number,screen)
number_teachers_x=get_number_teachers_x(number,teacher.rect.width)
number_rows=get_number_rows(number,yukino.rect.height,teacher.rect.height)
for row_number in range(number_rows):
for teacher_number in range(number_teachers_x):
create_teacher(number,screen,teachers,teacher_number,row_number)
def yukino_hit(number,screen,stats,sb,yukino,teachers,bullets):
if stats.yukino_left>0:
stats.yukino_left-=1
sb.prep_yukino()
teachers.empty()
bullets.empty()
create_fleet(number,screen,yukino,teachers)
yukino.center_yukino()
sleep(number.stop_time)
else:
stats.game_active=False
pygame.mouse.set_visible(True)

yukino

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
import pygame
from pygame.sprite import Sprite
class Yukino(Sprite):
def __init__(self,number,screen):
super(Yukino,self).__init__()
self.screen=screen
self.number=number
self.image=pygame.image.load('images/Yukino.bmp')
self.rect=self.image.get_rect()
self.screen_rect=screen.get_rect()
self.rect.centerx=self.screen_rect.centerx
self.rect.bottom=self.screen_rect.bottom
self.center=float(self.rect.centerx)
self.moving_right=False
self.moving_left=False
def update(self):
if self.moving_right and self.rect.right<self.screen_rect.right:
self.center+=self.number.yukino_speed_factor
if self.moving_left and self.rect.left>False:
self.center-=self.number.yukino_speed_factor
self.rect.centerx=self.center
def blitme(self):
self.screen.blit(self.image,self.rect)
def center_yukino(self):
self.center=self.screen_rect.centerx

button

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
import pygame.font
class Button():
def __init__(self,number,screen,msg):
self.screen=screen
self.screen_rect=screen.get_rect()
self.width,self.height=200,50
self.button_color=(241,158,194)
self.text_color=(255,255,255)
self.font=pygame.font.SysFont(None,48)
self.rect=pygame.Rect(False,False,self.width,self.height)
self.rect.center=self.screen_rect.center
self.prep_msg(msg)
def prep_msg(self,msg):
self.msg_image=self.font.render(msg,True,self.text_color,self.button_color)
self.msg_image_rect=self.msg_image.get_rect()
self.msg_image_rect.center=self.rect.center
def draw_button(self):
self.screen.fill(self.button_color,self.rect)
self.screen.blit(self.msg_image,self.msg_image_rect)

scoreboard

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
import pygame.font
from pygame.sprite import Group
from yukino import Yukino
class Scoreboard():
def __init__(self,number,screen,stats):
self.screen=screen
self.screen_rect=screen.get_rect()
self.number=number
self.stats=stats
self.text_color=(30,30,30)
self.font=pygame.font.SysFont(None,48)
self.prep_score()
self.prep_high_score()
self.prep_level()
self.prep_yukino()
def prep_score(self):
score_str="{:,}".format(self.stats.score)
self.score_image=self.font.render(score_str,
True,self.text_color,self.number.bg_color)
self.score_rect=self.score_image.get_rect()
self.score_rect.right=self.screen_rect.right-20
self.score_rect.top=20
def show_score(self):
self.screen.blit(self.score_image,self.score_rect)
self.screen.blit(self.high_score_image,self.high_score_rect)
self.screen.blit(self.level_image,self.level_rect)
self.yukino.draw(self.screen)
def prep_high_score(self):
high_score_str="{:,}".format(self.stats.high_score)
self.high_score_image=self.font.render(high_score_str,
True,self.text_color,self.number.bg_color)
self.high_score_rect=self.high_score_image.get_rect()
self.high_score_rect.centerx=self.screen_rect.centerx
self.high_score_rect.top=self.score_rect.top
def prep_level(self):
self.level_image=self.font.render(str(self.stats.level),
True,self.text_color,self.number.bg_color)
self.level_rect=self.level_image.get_rect()
self.level_rect.right=self.score_rect.right
self.level_rect.top=self.score_rect.bottom+10
def prep_yukino(self):
self.yukino=Group()
for yukino_number in range(self.stats.yukino_left):
yukino=Yukino(self.number,self.screen)
yukino.rect.x=10+yukino_number*yukino.rect.width
yukino.rect.y=10
self.yukino.add(yukino)

teacher

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
import pygame
from pygame.sprite import Sprite
class Teacher(Sprite):
def __init__(self,number,screen):
super(Teacher,self).__init__()
self.screen=screen
self.number=number
self.image=pygame.image.load('images/teacher.bmp')
self.rect=self.image.get_rect()
self.rect.x=self.rect.width
self.rect.y=self.rect.height
self.x=float(self.rect.x)
def blitme(self):
self.screen.blit(self.image,self.rect)
def check_edges(self):
screen_rect=self.screen.get_rect()
if self.rect.right>=screen_rect.right:
return True
elif self.rect.left<=False:
return True
def update(self):
self.x+=(self.number.teacher_speed_factor*self.number.fleet_direction)
self.rect.x=self.x

bullet

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
class Settings():
def __init__(self):
self.screen_width=1200
self.screen_height=750
self.bg_color=(230,230,230)
self.yukino_limit=3
self.bullet_width=3
self.bullet_height=15
self.bullet_color=60,60,60
self.bullets_allowed=3
self.speedup_scale=1.1
self.score_scale=10
self.initialize_dynamic_settings()
self.stop_time=1
def initialize_dynamic_settings(self):
self.yukino_speed_factor=1
self.bullet_speed_factor=3
self.teacher_speed_factor=0.5
self.fleet_direction=1
self.teacher_points=50
self.fleet_drop_speed=10
def increase_speed(self):
self.yukino_speed_factor*=self.speedup_scale
self.bullet_speed_factor*=self.speedup_scale
self.teacher_speed_factor*=self.speedup_scale
self.fleet_drop_speed*=self.speedup_scale
self.teacher_points=self.teacher_points+self.score_scale

settings

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
class Settings():
def __init__(self):
self.screen_width=1200
self.screen_height=750
self.bg_color=(230,230,230)
self.yukino_limit=3
self.bullet_width=3
self.bullet_height=15
self.bullet_color=60,60,60
self.bullets_allowed=3
self.speedup_scale=1.1
self.score_scale=10
self.initialize_dynamic_settings()
self.stop_time=1
def initialize_dynamic_settings(self):
self.yukino_speed_factor=1
self.bullet_speed_factor=3
self.teacher_speed_factor=0.5
self.fleet_direction=1
self.teacher_points=50
self.fleet_drop_speed=10
def increase_speed(self):
self.yukino_speed_factor*=self.speedup_scale
self.bullet_speed_factor*=self.speedup_scale
self.teacher_speed_factor*=self.speedup_scale
self.fleet_drop_speed*=self.speedup_scale
self.teacher_points=self.teacher_points+self.score_scale