# 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))
```

### 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)
```