@@ -2912,75 +2912,80 @@ Pygame
2912
2912
2913
2913
### Example
2914
2914
2915
- #### Runs a simple Mario game:
2916
- ``` python
2917
- import collections, dataclasses, enum, io, itertools, math, pygame, random, urllib
2918
- from urllib.request import urlopen
2919
-
2920
- D = enum.Enum(' D' , ' n e s w' )
2921
- P = collections.namedtuple(' P' , ' x y' )
2922
- Mario = dataclasses.make_dataclass(' Mario' , [' rect' , ' spd' , ' facing_left' , ' running_cycle' ])
2923
- RECT_SIDE , SCR_SIDE , MAX_SPEED = 16 , 25 , P(x = 5 , y = 10 )
2924
- COORDS = [p for p in itertools.product(range (SCR_SIDE ), repeat = 2 ) if {* p} & {0 , SCR_SIDE - 1 }] + \
2925
- [(random.randint(1 , SCR_SIDE - 2 ), random.randint(2 , SCR_SIDE - 2 )) for _ in range (62 )]
2926
- FLOORS = [pygame.Rect(x* RECT_SIDE , y* RECT_SIDE , RECT_SIDE , RECT_SIDE ) for x, y in COORDS ]
2927
- URL = ' https://raw.githubusercontent.com/gto76/python-cheatsheet/master/web/mario_bros.png'
2928
- IMAGE = pygame.image.load(io.BytesIO(urlopen(URL ).read()))
2929
- FRAMES = [IMAGE .subsurface(pygame.Rect(x* 16 , 0 , 16 , 16 )) for x in range (7 )]
2930
- FRAMES += [pygame.transform.flip(f, True , False ) for f in FRAMES ]
2931
- TILES = [IMAGE .subsurface(pygame.Rect(x* 16 , 0 , 16 , 16 )) for x in range (9 , 14 )]
2915
+ #### Runs a simple Super Mario game:
2916
+ ``` python
2917
+ import collections, dataclasses, enum, io, math, pygame, urllib.request, itertools as it
2918
+ from random import randint
2919
+
2920
+ P = collections.namedtuple(' P' , ' x y' )
2921
+ D = enum.Enum(' D' , ' n e s w' )
2922
+ SIZE , MAX_SPEED = 25 , P(5 , 10 )
2932
2923
2933
2924
def main ():
2934
- pygame.init()
2935
- screen = pygame.display.set_mode(2 * [SCR_SIDE * RECT_SIDE ])
2936
- mario = Mario(pygame.Rect(16 , 16 , 16 , 16 ), P(0 , 0 ), False , itertools.cycle(range (3 )))
2937
- while not any (event.type == pygame.QUIT for event in pygame.event.get()):
2925
+ def get_rect (x , y ):
2926
+ return pygame.Rect(x* 16 , y* 16 , 16 , 16 )
2927
+ def get_images ():
2928
+ url = ' https://gto76.github.io/python-cheatsheet/web/mario_bros.png'
2929
+ img = pygame.image.load(io.BytesIO(urllib.request.urlopen(url).read()))
2930
+ return [img.subsurface(get_rect(x, 0 )) for x in range (img.get_width() // 16 )]
2931
+ def get_mario ():
2932
+ Mario = dataclasses.make_dataclass(' Mario' , ' rect spd facing_left frame_cycle' .split())
2933
+ return Mario(get_rect(1 , 1 ), P(0 , 0 ), False , it.cycle(range (3 )))
2934
+ def get_tiles ():
2935
+ positions = [p for p in it.product(range (SIZE ), repeat = 2 ) if {* p} & {0 , SIZE - 1 }] + \
2936
+ [(randint(1 , SIZE - 2 ), randint(2 , SIZE - 2 )) for _ in range (SIZE ** 2 // 10 )]
2937
+ return [get_rect(* p) for p in positions]
2938
+ def get_screen ():
2939
+ pygame.init()
2940
+ return pygame.display.set_mode(2 * [SIZE * 16 ])
2941
+ run(get_images(), get_mario(), get_tiles(), get_screen())
2942
+
2943
+ def run (images , mario , tiles , screen ):
2944
+ while all (event.type != pygame.QUIT for event in pygame.event.get()):
2938
2945
keys = {pygame.K_UP : D.n, pygame.K_RIGHT : D.e, pygame.K_DOWN : D.s, pygame.K_LEFT : D.w}
2939
2946
pressed = {keys.get(i, None ) for i, on in enumerate (pygame.key.get_pressed()) if on}
2940
- update_speed(mario, pressed)
2941
- update_position(mario)
2942
- draw(screen, mario, pressed)
2947
+ update_speed(mario, tiles, pressed)
2948
+ update_position(mario, tiles )
2949
+ draw(mario, tiles, screen, pressed, images )
2943
2950
pygame.time.wait(28 )
2944
2951
2945
- def update_speed (mario , pressed ):
2946
- bounds = get_boundaries(mario.rect)
2952
+ def update_speed (mario , tiles , pressed ):
2953
+ bounds = get_boundaries(mario.rect, tiles )
2947
2954
x, y = mario.spd
2948
2955
x += 2 * ((D.e in pressed) - (D.w in pressed))
2949
2956
x = math.copysign(abs (x) - 1 , x) if x else 0
2950
2957
y += 1 if D.s not in bounds else (- 10 if D.n in pressed else 0 )
2951
2958
speed = stop_on_collision(P(x, y), bounds)
2952
2959
mario.spd = P(* [max (- thresh, min (thresh, s)) for thresh, s in zip (MAX_SPEED , speed)])
2953
2960
2954
- def update_position (mario ):
2955
- delta, old_p = P(0 , 0 ), mario.rect.topleft
2961
+ def update_position (mario , tiles ):
2962
+ old_p, delta = mario.rect.topleft, P(0 , 0 )
2956
2963
larger_speed = max (abs (s) for s in mario.spd)
2957
2964
for _ in range (int (larger_speed)):
2958
- mario.spd = stop_on_collision(mario.spd, get_boundaries(mario.rect))
2959
- delta = P(* [s/ larger_speed + dlt for s, dlt in zip (mario.spd, delta )])
2960
- mario.rect.topleft = [sum (a ) for a in zip (old_p, delta)]
2965
+ mario.spd = stop_on_collision(mario.spd, get_boundaries(mario.rect, tiles ))
2966
+ delta = P(* [a + s/ larger_speed for a, s in zip (delta, mario.spd)])
2967
+ mario.rect.topleft = [sum (pair ) for pair in zip (old_p, delta)]
2961
2968
2962
- def get_boundaries (rect ):
2969
+ def get_boundaries (rect , tiles ):
2963
2970
deltas = {D.n: P(0 , - 1 ), D.e: P(1 , 0 ), D.s: P(0 , 1 ), D.w: P(- 1 , 0 )}
2964
- return {d for d, delta in deltas.items() if rect.move(delta).collidelist(FLOORS ) != - 1 }
2971
+ return {d for d, delta in deltas.items() if rect.move(delta).collidelist(tiles ) != - 1 }
2965
2972
2966
2973
def stop_on_collision (spd , bounds ):
2967
2974
return P(x = 0 if (D.w in bounds and spd.x < 0 ) or (D.e in bounds and spd.x > 0 ) else spd.x,
2968
2975
y = 0 if (D.n in bounds and spd.y < 0 ) or (D.s in bounds and spd.y > 0 ) else spd.y)
2969
2976
2970
- def draw (screen , mario , pressed ):
2977
+ def draw (mario , tiles , screen , pressed , images ):
2978
+ def get_frame_index ():
2979
+ if D.s not in get_boundaries(mario.rect, tiles):
2980
+ return 4
2981
+ return next (mario.frame_cycle) if {D.w, D.e} & pressed else 6
2971
2982
screen.fill((85 , 168 , 255 ))
2972
- mario.facing_left = mario.spd.x < 0 if mario.spd.x else mario.facing_left
2973
- screen.blit(FRAMES [get_frame_index(mario, pressed) + mario.facing_left* 7 ], mario.rect)
2974
- for rect in FLOORS :
2975
- tile_index = 1 if {* rect.topleft} & {0 , (SCR_SIDE - 1 )* RECT_SIDE } else 0
2976
- screen.blit(TILES [tile_index], rect)
2983
+ mario.facing_left = (D.w in pressed) if {D.e, D.w} & pressed else mario.facing_left
2984
+ screen.blit(images[get_frame_index() + mario.facing_left* 9 ], mario.rect)
2985
+ for rect in tiles:
2986
+ screen.blit(images[19 if {* rect.topleft} & {0 , (SIZE - 1 )* 16 } else 18 ], rect)
2977
2987
pygame.display.flip()
2978
2988
2979
- def get_frame_index (mario , pressed ):
2980
- if D.s not in get_boundaries(mario.rect):
2981
- return 4
2982
- return next (mario.running_cycle) if {D.w, D.e} & pressed else 6
2983
-
2984
2989
if __name__ == ' __main__' :
2985
2990
main()
2986
2991
```
0 commit comments