-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
473 lines (390 loc) · 30.5 KB
/
main.py
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
"""
MIT License
Copyright (c) 2021 Lakhya Jyoti Nath
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
PyBluesky - A simple python game to navigate your jet and fight though a massive missiles attack based on pygame framework.
Version: 1.0.0 (based on desktop release 1.0.5 ; changed version number for android release)
Author: Lakhya Jyoti Nath (ljnath)
Email: [email protected]
Website: https://ljnath.com
"""
import asyncio
import math
import random
import webbrowser
import pygame
from android import loadingscreen
from plyer import accelerometer, orientation, vibrator
from game.data.enums import Screen, StartChoice
from game.environment import GameEnvironment
from game.handlers.leaderboard import LeaderBoardHandler
from game.handlers.network import NetworkHandler
from game.sprites.cloud import Cloud
from game.sprites.jet import Jet
from game.sprites.missile import Missile
from game.sprites.samlauncher import SamLauncher
from game.sprites.star import Star
from game.sprites.text.input.name import NameInputText
from game.sprites.text import Text
from game.sprites.text.exitmenu import ExitMenuText
from game.sprites.text.gamemenu import GameMenuText
from game.sprites.text.help import HelpText
from game.sprites.text.leaderboard import LeaderBoardText
from game.sprites.text.replaymenu import ReplayMenuText
from game.sprites.text.score import ScoreText
from game.sprites.vegetation import Vegetation
API_KEY = ''
def check_update() -> None:
"""
Method to check for game update
"""
network_handler = NetworkHandler(API_KEY)
asyncio.get_event_loop().run_until_complete(network_handler.check_game_update())
asyncio.get_event_loop().run_until_complete(LeaderBoardHandler().update(API_KEY))
def submit_result() -> None:
"""
Method to submit game score to remote server
"""
game_env = GameEnvironment()
if game_env.dynamic.game_score > 0:
network_handler = NetworkHandler(API_KEY)
asyncio.get_event_loop().run_until_complete(network_handler.submit_result())
asyncio.get_event_loop().run_until_complete(LeaderBoardHandler().update(API_KEY))
def create_vegetation(vegetations) -> None:
"""
Method to create vegetation
"""
game_env = GameEnvironment()
vegetations.empty()
for i in range(math.ceil(game_env.static.screen_width / game_env.vegetation_size[0])): # drawing the 1st vegetations required to fill the 1st sceen (max is the screen width)
vegetation = Vegetation(x_pos=i * game_env.vegetation_size[0] + game_env.vegetation_size[0] / 2) # creating a new vegetation
vegetations.add(vegetation) # just adding sprite to vegetations group, to updating on screen for now
def notify_user_of_update() -> None:
"""
Method to open the webbrowser when an new update is available
"""
game_env = GameEnvironment()
if game_env.dynamic.update_url:
try:
webbrowser.open(game_env.dynamic.update_url)
except Exception:
pass
def get_hint_sprite(hint_message: str) -> None:
"""
Method to create hint text
"""
game_env = GameEnvironment()
return Text(f'HINT: {hint_message}', 26, pos_x=game_env.static.screen_width / 2, pos_y=game_env.static.screen_height - 30) # creating game hint message
def play():
pygame.mixer.init() # initializing same audio mixer with default settings
pygame.init() # initializing pygame
game_env = GameEnvironment() # initializing game environment
game_env.dynamic.collision_sound.set_volume(1.5)
game_env.dynamic.levelup_sound.set_volume(1.5)
game_env.dynamic.shoot_sound.set_volume(1.5)
game_env.dynamic.hit_sound.set_volume(3)
game_env.dynamic.powerup_sound.set_volume(10)
game_env.dynamic.samfire_sound.set_volume(5)
# setting main game background musicm
# lopping the main game music and setting game volume
pygame.mixer.music.load(game_env.static.game_sound.get('music'))
pygame.mixer.music.play(loops=-1)
pygame.mixer.music.set_volume(.2)
# settings flags to create screen in fullscreen, use HW-accleration and DoubleBuffer
flags = pygame.FULLSCREEN | pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.SCALED | pygame.RESIZABLE
# creating game screen with custom width and height
screen = pygame.display.set_mode((game_env.static.screen_width, game_env.static.screen_height), flags)
pygame.display.set_caption('{} version. {}'.format(game_env.static.name, game_env.static.version)) # setting name of game window
pygame.mouse.set_visible(False) # hiding the mouse pointer from the game screen
gameclock = pygame.time.Clock() # setting up game clock to maintain constant fps
check_update()
ADD_CLOUD = pygame.USEREVENT + 1 # creating custom event to automatically add cloud in the screen
pygame.time.set_timer(ADD_CLOUD, int(1000 / game_env.static.cloud_per_sec)) # setting event to auto-trigger every 1s; 1 cloud will be created every second
ADD_MISSILE = pygame.USEREVENT + 2 # creating custom event to automatically add missiles in the screen
pygame.time.set_timer(ADD_MISSILE, int(1000 / game_env.static.missile_per_sec)) # setting event to auto-trigger every 500ms; 2 missiles will be created every second
ADD_SAM_LAUNCHER = pygame.USEREVENT + 3 # creating custom event to automatically add SAM-LAUNCHER in the screen
pygame.time.set_timer(ADD_SAM_LAUNCHER, 5000) # setting event to auto-trigger every 5s; 1 level can have 4 sam launcher
running = True # game running variable
gameover = False # no gameover by default
game_started = False # game is not started by default
game_pause = False
star_shown = False
user_has_swipped = False
screen_color = game_env.static.background_default if game_started else game_env.static.background_special
# blocking all the undesired events
pygame.event.set_blocked(pygame.FINGERMOTION)
pygame.event.set_blocked(pygame.FINGERUP)
pygame.event.set_blocked(pygame.FINGERDOWN)
pygame.event.set_blocked(pygame.MOUSEBUTTONDOWN)
pygame.event.set_blocked(pygame.MOUSEMOTION)
pygame.event.set_blocked(pygame.KEYUP)
pygame.event.set_blocked(ADD_MISSILE)
pygame.event.set_blocked(ADD_SAM_LAUNCHER)
backgrounds = pygame.sprite.Group() # creating seperate group for background sprites
stars = pygame.sprite.GroupSingle() # group of stars with max 1 sprite
vegetations = pygame.sprite.Group() # creating cloud group for storing all the clouds in the game
clouds = pygame.sprite.Group() # creating cloud group for storing all the clouds in the game
missiles = pygame.sprite.Group() # creating missile group for storing all the missiles in the game
deactivated_missile = pygame.sprite.Group() # creating missile group for storing all the deactivated missiles in the game
samlaunchers = pygame.sprite.GroupSingle() # creating missile group for storing all the samlaunchers in the game
title_sprites = pygame.sprite.Group()
hint_sprite = get_hint_sprite("Swipe your finger to know more") # creating game hint message
title_banner_sprite = Text("{} {}".format(game_env.static.name, game_env.static.version), 100, pos_x=game_env.static.screen_width / 2, pos_y=100) # creating title_banner_sprite text sprite with game name
title_author_sprite = Text("By Lakhya Jyoti Nath aka ljnath", 28, pos_x=game_env.static.screen_width / 2, pos_y=150) # creating game author
swipe_navigated_menus = {
Screen.GAME_MENU: GameMenuText(),
Screen.HELP: HelpText(),
Screen.LEADERBOARD: LeaderBoardText()
}
selected_menu_index = 0
# showing regular game menus if user has entered the player name
if game_env.dynamic.player_name:
game_env.dynamic.all_sprites.add(hint_sprite)
active_sprite = swipe_navigated_menus[Screen.GAME_MENU]
pygame.event.set_allowed(pygame.MOUSEMOTION)
else:
# else showing the screen for user to enter the player name
active_sprite = NameInputText()
game_env.dynamic.active_screen = Screen.NAME_INPUT
[title_sprites.add(sprite) for sprite in (active_sprite, title_banner_sprite, title_author_sprite)] # adding all the necessary sprites to title_sprites
[game_env.dynamic.all_sprites.add(sprite) for sprite in title_sprites] # adding all title_sprites sprite to all_sprites
jet = Jet() # creating jet sprite
scoretext_sprite = ScoreText() # creating scoreboard sprite
game_env.dynamic.noammo_sprite = Text("NO AMMO !!!", 30) # creating noammo-sprite
create_vegetation(vegetations)
menu_screens = {Screen.REPLAY_MENU, Screen.GAME_MENU, Screen.EXIT_MENU}
last_active_sprite = (game_env.dynamic.active_screen, active_sprite)
def start_gameplay():
nonlocal gameover, jet, star_shown, screen_color, game_started, ADD_MISSILE, ADD_SAM_LAUNCHER
pygame.event.set_blocked(game_env.MOUSEMOTION)
pygame.event.set_allowed(ADD_MISSILE)
pygame.event.set_allowed(ADD_SAM_LAUNCHER)
screen_color = game_env.static.background_default # restoring screen color
[sprite.kill() for sprite in title_sprites] # kill all the title_sprites sprite sprite
jet = Jet() # re-creating the jet
missiles.empty() # empting the missle group
game_env.dynamic.all_sprites = pygame.sprite.Group() # re-creating group of sprites
[game_env.dynamic.all_sprites.remove(sprite) for sprite in (active_sprite, hint_sprite)] # removing active sprite and hint sprite
[game_env.dynamic.all_sprites.add(sprite) for sprite in (jet, scoretext_sprite)] # adding the jet and scoreboard to all_sprites
game_env.reset() # reseting game data
pygame.time.set_timer(ADD_MISSILE, int(1000 / game_env.static.missile_per_sec)) # resetting missile creation event timer
create_vegetation(vegetations) # creating vegetation
[backgrounds.add(sprite) for sprite in vegetations.sprites()] # adding vegetation to background
game_env.dynamic.active_screen = Screen.GAME_SCREEN # setting gamescreen as the active sprite
game_started = True # game has started
gameover = False # game is not over yet
star_shown = False # no star is displayed
# enabling acclerometer sensor to get accleration sensor data
accelerometer.enable()
# Main game loop
while running:
# getting the accleration sensor data from accelerometer
# acceleration_sensor_values is a tuple of (x, y, z) sensor data
acceleration_sensor_values = accelerometer.acceleration
# this variable is updated in case of a MOUSEMOTION; in subsequent MOUSEBUTTONUP event,
# it is checked if the position of both these events are the same.
# if yes, this indicates that these are part of same motion and the MOUSEBUTTONUP event can be discarded
last_touch_position = (0, 0)
# Look at every event in the queue
for event in pygame.event.get():
# checking for VIDEORESIZE event, this event is used to prevent auto-rotate in android device
# if any change in the screensize is detected, then the orienatation is forcefully re-applied
if event.type == game_env.VIDEORESIZE:
orientation.set_landscape(reverse=False)
# handling keydown event to show the pause menu
elif event.type == game_env.KEYDOWN:
if game_env.dynamic.active_screen != Screen.EXIT_MENU and pygame.key.name(event.key) == 'AC Back':
pygame.mixer.music.pause()
last_active_screen = game_env.dynamic.active_screen
last_active_sprite = active_sprite
game_started, game_pause = game_pause, game_started
[game_env.dynamic.all_sprites.remove(sprite) for sprite in (active_sprite, hint_sprite)]
active_sprite = ExitMenuText()
game_env.dynamic.all_sprites.add(active_sprite)
game_env.dynamic.active_screen = Screen.EXIT_MENU
# handling the textinput event to allow user to type
elif event.type == game_env.TEXTINPUT and game_env.dynamic.active_screen == Screen.NAME_INPUT:
active_sprite.update(event.text)
# handling menu navigation via finger swipe; menu navigation is not allowed during NAME_INPUT screen
elif event.type == game_env.MOUSEMOTION and not game_pause and not game_started and not gameover:
# saving current interaction position; this will be later used for discarding MOUSEBUTTONUP event if the position is same
last_touch_position = event.pos
if user_has_swipped:
continue
is_valid_swipe = False
if event.rel[0] < -40:
user_has_swipped = True
is_valid_swipe = True
selected_menu_index += 1
if selected_menu_index == len(swipe_navigated_menus):
selected_menu_index = 0
elif event.rel[0] > 40:
user_has_swipped = True
is_valid_swipe = True
selected_menu_index -= 1
if selected_menu_index < 0:
selected_menu_index = len(swipe_navigated_menus) - 1
if not is_valid_swipe:
continue
pygame.event.clear()
# settings the current swipe_navigated_menus as the active one for it to be rendered
# and refreshing the active_sprite in game_env.dynamic.all_sprites for re-rendering
game_env.dynamic.active_screen = list(swipe_navigated_menus.keys())[selected_menu_index]
game_env.dynamic.all_sprites.remove(active_sprite)
active_sprite = swipe_navigated_menus[game_env.dynamic.active_screen]
game_env.dynamic.all_sprites.add(active_sprite)
# mouse based interaction to simulate finger based interaction
elif event.type == game_env.MOUSEBUTTONUP:
# handling single finger only for now
if event.button == 1 and event.pos != last_touch_position:
# resume or exit game based on user interaction with the EXIT-MENU
if game_env.dynamic.active_screen == Screen.EXIT_MENU:
if game_env.dynamic.exit:
running = False
elif not game_env.dynamic.exit:
pygame.mixer.music.unpause()
game_started, game_pause = game_pause, game_started
game_env.dynamic.all_sprites.remove(active_sprite)
game_env.dynamic.active_screen = last_active_screen
active_sprite = last_active_sprite
if game_env.dynamic.active_screen != Screen.GAME_SCREEN:
[game_env.dynamic.all_sprites.add(sprite) for sprite in (active_sprite, hint_sprite)]
# handling interaction oin the NAME-INPUT menu like button click and show/hide of keyboard
elif game_env.dynamic.active_screen == Screen.NAME_INPUT:
# if playername is not defined; this screen is shown to the user for getting the username
# once the username is entered, user can touch either of CLEAR or OK surface.
# we are check this touch activity here
if game_env.dynamic.player_name.strip() == '':
active_sprite.check_for_touch(event.pos)
# jet can shoot at use touch and when the game is running
elif game_started and not gameover:
jet.shoot()
# start the game when user has selected 'Start Game' in GAME_MENU or 'Yes' in REPLAY_MENT
elif (game_env.dynamic.active_screen == Screen.GAME_MENU and game_env.dynamic.game_start_choice == StartChoice.START) or (game_env.dynamic.active_screen == Screen.REPLAY_MENU and game_env.dynamic.replay):
start_gameplay()
# exit the game when user has selected 'Exit' in GAME_MENU or 'No' in REPLAY_MENT
elif game_env.dynamic.active_screen == Screen.GAME_MENU and game_env.dynamic.game_start_choice == StartChoice.EXIT or (game_env.dynamic.active_screen == Screen.REPLAY_MENU and not game_env.dynamic.replay):
running = False
# adding of clouds, backgroud, vegetation and power-up star is handled inside this
# the reset of user swip is also handled in this; this a user is allowed to make 1 swipe every second
elif event.type == ADD_CLOUD:
user_has_swipped = False
if game_pause:
continue
last_sprite = vegetations.sprites()[-1] # storing the last available vegetation for computation
if last_sprite.rect.x + last_sprite.rect.width / 2 - game_env.static.screen_width < 0: # checking if the last vegetation has appeared in the screen, if yes a new vegetation will be created and appended
vegetation = Vegetation(x_pos=last_sprite.rect.x + last_sprite.rect.width + last_sprite.rect.width / 2) # position of the new sprite is after the last sprite
vegetations.add(vegetation) # adding sprite to groups for update and display
backgrounds.add(vegetation)
new_cloud = Cloud() # is event to add cloud is triggered
clouds.add(new_cloud) # create a new cloud
backgrounds.add(new_cloud) # adding the cloud to all_sprites group
if not gameover and game_started:
game_env.dynamic.game_playtime += 1 # increasing playtime by 1s as this event is triggered every second; just reusing existing event instead of recreating a new event
if not star_shown and random.randint(0, 30) % 3 == 0: # probabity of getting a star is 30%
star = Star()
stars.add(star)
game_env.dynamic.all_sprites.add(star)
star_shown = True
if game_env.dynamic.game_playtime % 20 == 0: # changing game level very 20s
star_shown = False
game_env.dynamic.levelup_sound.play() # playing level up sound
game_env.dynamic.game_level += 1 # increasing the game level
pygame.time.set_timer(ADD_MISSILE, int(1000 / (game_env.static.missile_per_sec + int(game_env.dynamic.game_level / 2)))) # updating timer of ADD_MISSLE for more missiles to be added
game_env.dynamic.ammo += 50 # adding 50 ammo on each level up
game_env.dynamic.game_score += 10 # increasing game score by 10 after each level
game_env.dynamic.all_sprites.remove(game_env.dynamic.noammo_sprite) # removing no ammo sprite when ammo is refilled
# # add missile and sam-launcher if the game has started and not gameover
elif event.type == ADD_MISSILE: # is event to add missile is triggered; missles are not added during gameover
new_missile = Missile() # create a new missile
missiles.add(new_missile) # adding the missile to missle group
game_env.dynamic.all_sprites.add(new_missile) # adding the missile to all_sprites group as well
elif event.type == ADD_SAM_LAUNCHER and not samlaunchers.sprites() and game_env.dynamic.game_level > 5:
samlauncher = SamLauncher()
samlaunchers.add(samlauncher)
game_env.dynamic.all_sprites.add(samlauncher)
# if the active screen is NAME-INPUT and if the playername is available
# this means that user has entered the playername in the NAME-INPNUT screen; removing the screen now
if game_env.dynamic.active_screen == Screen.NAME_INPUT and game_env.dynamic.player_name.strip() != '':
pygame.key.stop_text_input()
game_env.dynamic.all_sprites.remove(active_sprite)
active_sprite = swipe_navigated_menus[Screen.GAME_MENU]
[game_env.dynamic.all_sprites.add(sprite) for sprite in (active_sprite, hint_sprite)]
game_env.dynamic.active_screen = Screen.GAME_MENU
pygame.event.set_allowed(pygame.MOUSEMOTION)
screen.fill(screen_color) # Filling screen with sky blue color
[screen.blit(sprite.surf, sprite.rect) for sprite in backgrounds] # drawing all backgrounds sprites
[screen.blit(sprite.surf, sprite.rect) for sprite in game_env.dynamic.all_sprites] # drawing all sprites in the screen
if game_started and not gameover:
# missile hit
if pygame.sprite.spritecollideany(jet, missiles) or pygame.sprite.spritecollideany(jet, game_env.dynamic.sam_missiles): # Check if any missiles have collided with the player; if so
pygame.event.clear() # clearing all existing events in queue once game is over
vibrator.vibrate(1) # vibrating device for 1s on game-over
pygame.event.set_blocked(ADD_MISSILE) # blocking event to add missile due to gameover
pygame.event.set_blocked(ADD_SAM_LAUNCHER) # blocking event to add new sam launcher due to gameover
gameover = True # setting gameover to true to prevent new missiles from spawning
active_sprite = ReplayMenuText()
game_env.dynamic.active_screen = Screen.REPLAY_MENU
jet.kill() # killing the jet
[sam_missile.kill() for sam_missile in game_env.dynamic.sam_missiles] # killing the SAM missile
game_env.dynamic.collision_sound.play()
hint_sprite = get_hint_sprite("Move your device to change selection and tap to confirm") # updating game hint message
[game_env.dynamic.all_sprites.add(sprite) for sprite in (active_sprite, hint_sprite)] # adding the gameover and the hint sprite
game_env.dynamic.all_sprites.remove(game_env.dynamic.noammo_sprite)
submit_result()
# bullet hit
collision = pygame.sprite.groupcollide(missiles, game_env.dynamic.bullets, True, True) # checking for collision between bullets and missiles, killing each one of them on collision
if len(collision) > 0:
game_env.dynamic.hit_sound.play() # play missile destroyed sound
game_env.dynamic.game_score += len(collision) * 10 # 1 missle destroyed = 10 pts.
game_env.dynamic.missiles_destroyed += len(collision) # to calulate player accuracy
# powerup hit
if pygame.sprite.spritecollideany(jet, stars): # collition between jet and star (powerup)
game_env.dynamic.powerup_sound.play()
[game_env.dynamic.all_sprites.remove(s) for s in stars.sprites()] # removing the star from all_sprites to hide from screen
game_env.dynamic.game_score += 100 * game_env.dynamic.game_level # increasing game score by 100
stars.empty() # removing star from stars group
for missile in missiles.sprites():
missile.deactivate() # making missile as deactivated
deactivated_missile.add(missile) # adding missile to deactivated_missile group
missiles.remove(missile) # remove missiles from missles group to avoid collision with jet
if not game_pause and game_started and not gameover:
jet.update(acceleration_sensor_values)
elif game_env.dynamic.active_screen in menu_screens:
active_sprite.update(acceleration_sensor_values) # handling menu interactions for all the possible interactive screens
pygame.display.flip() # updating display to the screen
gameclock.tick(game_env.static.fps) # ticking game clock at 30 to maintain 30fps
if game_pause:
continue
if game_started:
vegetations.update() # vegetations will move only after the game starts
game_env.dynamic.bullets.update()
game_env.dynamic.sam_missiles.update()
missiles.update() # update the position of the missiles
deactivated_missile.update()
clouds.update() # update the postition of the clouds
stars.update()
samlaunchers.update((jet.rect.x + jet.rect.width / 2, jet.rect.y + jet.rect.height))
scoretext_sprite.update() # update the game score
pygame.mixer.music.stop() # stopping game music
pygame.mixer.quit() # stopping game sound mixer
notify_user_of_update()
if __name__ == '__main__':
# hide loading screen as the game has been loaded
loadingscreen.hide_loading_screen()
# start the game
play()