# pygame cheatsheet ## terms - display surface: the window - (regular) surface: object put on display surface like plain color, image or text ## cordinate - right, down is positive x, y ## rectangles + precise postioning of surface + basic collisions ## colors - rgb tuple: (R, G, B) where R, G and B are integers between 0 and 255 - hexidecimal value: `#rrggbb`, how it works is same as rgb tuple. But `rr`, `gg` and `bb` are hexidecimal values between `00` to `ff`. ## 一些重要觀念 - 先判按鍵,再判 collision ``` if game_active: actual_game else: game_over ``` ## 基本語法補充 `global` 可以宣告全域變數 ```python= counter = 0 def func_a(): global counter counter += 2 print('func_a counter: ' + str(counter)) def func_b(): global counter counter += 3 print('func_b counter: ' + str(counter)) func_a() print('counter: ' + str(counter)) func_b() print('counter: ' + str(counter)) ``` ## code ### basic setting ```python= import pygame from sys import exit pygame.init() # important screen = pygame.display.set_mode((900, 600)) # display surface: (width, height) pygame.display.set_caption('Runner') clock = pygame.time.Clock() test_surface = pygame.Surface((100, 200)) # create surface test_surface.fill('Cyan') while True: # add elements here for event in pygame.event.get(): if event.type == pygame.QUIT: # close button pygame.quit() exit() screen.blit(test_surface, (200, 100)) # block image transfer (put something in pos (top-left)) pygame.display.update() clock.tick(60) # maximum framerate ``` ### insert image The `convert` and `convert_alpha` function are for optimizing surface rendering (make it faster). The `convert_alpha` is used when image has transparency, and we want to preserve the property. ```python! test_surface = pygame.image.load('images/yanami.jpg') snail_surface = pygame.image.load('graphics/snail/snail1.png').convert_alpha() ``` ### insert text ```python= test_font = pygame.font.Font('fonts/angel.ttf', 50) # none in font field for being default text_surface = test_font.render('UwU', False, 'Gold') # The second field is whether enabling anti-aliasing screen.blit(text_surface, (450, 50)) ``` ### Create rectangles https://www.pygame.org/docs/ref/rect.html ```python= # test_rect = pygame.Rect(left, top, width, height) player_surf = pygame.image.load('graphics/Player/player_walk_1.png').convert_alpha() player_rect = player_surf.get_rect(midbottom = (80, 300)) ``` ![image](https://hackmd.io/_uploads/rJECsizVge.png) ### Check collisions (of rectangle) Using pygame.mouse ```python= if player_rect.colliderect(snail_rect): # check if two rectangle collides print('collision uwu') mouse_pos = pygame.mouse.get_pos() # return a position tuple if player_rect.collidepoint(mouse_pos): # check if one rectangle collides with one point print(pygame.mouse.get_pressed()) # (bool, bool, bool), stands for whether left, middle, right button being pressed print('collision owo') ``` Using (something) inside event loop - `MOUSEMOTION`: if mouse moves? - `MOUSEBUTTONDOWN`: the moment mouse being pressed - `MOUSEBUTTONUP`: the moment mouse button being released ```python= if event.type == pygame.MOUSEMOTION and player_rect.collidepoint(event.pos): print(f'collision at {event.pos}') if event.type == pygame.MOUSEBUTTONDOWN: print('down') if event.type == pygame.MOUSEBUTTONUP: print('up') ``` ### `pygame.draw` ```python= pygame.draw.rect(screen, '#c0e8ec', score_rect) pygame.draw.rect(screen, '#c0e8ec', score_rect, 20) pygame.draw.line(screen, 'Gold', (0, 0), pygame.mouse.get_pos(), 10) # line from one point to another point pygame.draw.ellipse(screen, 'Green', pygame.Rect(500, 100, 70, 30)) # use rectangle to limit the spanning of ellipse ``` ### keyboard manipulation The keyboard constant can be found [here](https://www.pygame.org/docs/ref/key.html). ```python= # using submodule pygame.key keys = pygame.key.get_pressed() if keys[pygame.K_SPACE]: print('jump') ``` ```python= if event.type == pygame.KEYDOWN: # there's also one KEYUP if event.key == pygame.K_SPACE: print('jump') ``` ### Time measurement `pygame.time.get_ticks()` return time (ms) since `pygame.init()`. ```python= def display_score(): current_time = (pygame.time.get_ticks() - start)//1000 score_surf = test_font.render(f'Score uwu: {current_time}', False, (64,64,64)) score_rect = score_surf.get_rect(center = (400, 50)) screen.blit(score_surf, score_rect) ``` tips: update variable `start` when restarting the game ### Transforming the surface ```python= player_stand = pygame.image.load('graphics/Player/player_stand.png').convert_alpha() # player_stand = pygame.transform.scale(player_stand, (200, 400)) # (surface, (width, height)) # player_stand = pygame.transform.scale2x(player_stand) # 放大兩倍 player_stand = pygame.transform.rotozoom(player_stand, 30, 2) # (surfacem, angle, scale) ``` ### timer example: ```python= obstacle_timer = pygame.USEREVENT + 1 pygame.time.set_timer(obstacle_timer, 1500) if event.type == obstacle_timer: if randint(0, 2): obstacle_rect_list.append(snail_surf.get_rect(bottomright = (randint(900, 1100), 300))) else: obstacle_rect_list.append(fly_surf.get_rect(bottomright = (randint(900, 1100), 210))) ``` ## Example game (The runner) with the application of **spirite class** and **playing imported music/sound** ```python= import pygame from sys import exit from random import randint, choice class Player(pygame.sprite.Sprite): def __init__(self): super().__init__() self.gravity = 0 player_walk_1 = pygame.image.load('graphics/Player/player_walk_1.png').convert_alpha() player_walk_2 = pygame.image.load('graphics/Player/player_walk_2.png').convert_alpha() self.player_walk = [player_walk_1, player_walk_2] self.player_jump = pygame.image.load('graphics/Player/jump.png').convert_alpha() self.player_index = 0 self.image = self.player_walk[self.player_index] self.jump_sound = pygame.mixer.Sound('audio/jump.mp3') self.jump_sound.set_volume(0.3) self.rect = self.image.get_rect(midbottom = (80, 300)) def player_input(self): keys = pygame.key.get_pressed() if keys[pygame.K_SPACE] and self.rect.bottom >= 300: self.gravity = -20 self.jump_sound.play() def apply_gravity(self): self.gravity += 1 self.rect.y += self.gravity if self.rect.bottom >= 300: self.rect.bottom = 300 def animation_state(self): if self.rect.bottom < 300: self.image = self.player_jump else: self.player_index += 0.1 self.image = self.player_walk[int(self.player_index) % 2] def update(self): self.player_input() self.apply_gravity() self.animation_state() class Obstacle(pygame.sprite.Sprite): def __init__(self, type): super().__init__() if type == 'fly': fly_frame_1 = pygame.image.load('graphics/Fly/Fly1.png').convert_alpha() fly_frame_2 = pygame.image.load('graphics/Fly/Fly2.png').convert_alpha() self.frames = [fly_frame_1, fly_frame_2] y = 210 else: snail_frame_1 = pygame.image.load('graphics/snail/snail1.png').convert_alpha() snail_frame_2 = pygame.image.load('graphics/snail/snail2.png').convert_alpha() self.frames = [snail_frame_1, snail_frame_2] y = 300 self.animation_index = 0 self.image = self.frames[self.animation_index] self.rect = self.image.get_rect(midbottom = (randint(900, 1100), y)) def animation_state(self): self.animation_index += 0.1 self.image = self.frames[int(self.animation_index) % 2] def update(self): self.animation_state() self.rect.x -= 6 self.destroy() def destroy(self): if self.rect.x <= -100: self.kill() def collision_sprite(): if pygame.sprite.spritecollide(player.sprite, obstacle_group, False): obstacle_group.empty() return False return True pygame.init() screen = pygame.display.set_mode((800, 400)) pygame.display.set_caption('Runner') clock = pygame.time.Clock() test_font = pygame.font.Font('font/Pixeltype.ttf', 50) start = 0 bg_music = pygame.mixer.Sound('audio/music.wav') bg_music.set_volume(0.3) # the volume should be [0, 1] (like percentage) bg_music.play(loops = -1) # the music plays [loops] times, and -1 means infinite times def display_score(): current_time = (pygame.time.get_ticks() - start)//1000 score_surf = test_font.render(f'Score uwu: {current_time}', False, (64,64,64)) score_rect = score_surf.get_rect(center = (400, 50)) screen.blit(score_surf, score_rect) return current_time sky_surface = pygame.image.load('graphics/Sky.png').convert() ground_surface = pygame.image.load('graphics/ground.png').convert() player = pygame.sprite.GroupSingle() player.add(Player()) obstacle_group = pygame.sprite.Group() player_stand = pygame.image.load('graphics/Player/player_stand.png').convert_alpha() player_stand = pygame.transform.rotozoom(player_stand, 30, 2) player_stand_rect = player_stand.get_rect(center = (400, 200)) game_name = test_font.render('uwu runner', False, (111, 196, 169)) game_name_rect = game_name.get_rect(center = (400, 80)) game_msg = test_font.render('Press space to start', False, (111, 196, 169)) game_msg_rect = game_msg.get_rect(center = (400, 320)) # timer obstacle_timer = pygame.USEREVENT + 1 pygame.time.set_timer(obstacle_timer, 1500) game_active = False score = 0 snail_animation_timer = pygame.USEREVENT + 2 pygame.time.set_timer(snail_animation_timer, 500) fly_animation_timer = pygame.USEREVENT + 3 pygame.time.set_timer(fly_animation_timer, 200) while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() if game_active: if event.type == obstacle_timer: obstacle_group.add(Obstacle(choice(['fly', 'snail', 'snail']))) else: if event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: game_active = True start = pygame.time.get_ticks() if game_active: screen.blit(sky_surface, (0, 0)) screen.blit(ground_surface, (0, 300)) score = display_score() player.draw(screen) player.update() obstacle_group.draw(screen) obstacle_group.update() # collision game_active = collision_sprite() else: screen.fill((94, 129, 162)) screen.blit(player_stand, player_stand_rect) screen.blit(game_name, game_name_rect) if score == 0: screen.blit(game_msg, game_msg_rect) else: score_msg = test_font.render(f'Your Score: {score}', False, (111, 196, 169)) score_msg_rect = score_msg.get_rect(center = (400, 330)) screen.blit(score_msg, score_msg_rect) pygame.display.update() clock.tick(60) ```