多年前写过一篇 Flash/Flex学习笔记(25):摩擦力与屏幕环绕,可惜的当时上传的flash,服务器后来无人维护,现在flash链接都失效了。本篇用pygame重新实现了一个:
原理是类似,但要注意的是:pygame中旋转的角度采用逆时针系统 ,即:逆时针方向旋转,角度为正,反之为负。所以在外理角度时,y轴方向的速度要取反。
素材图(2张):
飞船熄火
飞船点火
需求:按向上键点火,飞船启动,一直加速;无按键时,飞船熄火,速度慢慢降下来(设置摩擦系数);左右键控制转向;飞出屏幕时,从另一侧切回来。
代码:
代码语言:javascript复制 1 import os
2 import pygame
3 import sys
4 import math
5
6 pygame.init()
7
8 clock = pygame.time.Clock()
9
10 SIZE = WIDTH, HEIGHT = 400, 400
11 GRAY = (200, 200, 200)
12 RED = (255, 0, 0)
13 screen = pygame.display.set_mode(SIZE)
14 pygame.display.set_caption("ship")
15 img_base_path = os.getcwd() '/img/'
16
17
18 class Ship(object):
19 def __init__(self, img_base_path, screen):
20 self.vx = 0
21 self.vy = 0
22 # 旋转角速度
23 self.vr = 0
24 # 推进力
25 self.thrust = 0
26 self.angle = 0
27 self.show_flame = False
28 self.scale = 1.0
29 # 是否显示辅助边框
30 self.show_rect = False
31
32 self.img_src = pygame.image.load(img_base_path 'ship.png')
33 self.img_flame_src = pygame.image.load(img_base_path 'ship_flame.png')
34
35 self.img = self.img_src
36 self.rect = self.img_src.get_rect()
37
38 self.img_new = self.img
39 self.rect_new = self.img_new.get_rect()
40
41 self.rect = self.rect.move((WIDTH - self.rect.width) * 0.5, (HEIGHT - self.rect.height) * 0.5)
42
43 def draw(self, screen):
44 screen.blit(self.img_new, self.rect_new)
45 if self.show_rect:
46 pygame.draw.rect(screen, GRAY, ship.rect, 1)
47 pygame.draw.rect(screen, RED, ship.rect_new, 1)
48
49 def move(self):
50 self.rect = self.rect.move(self.vx, self.vy)
51 self.rect_new = self.rect_new.move(self.vx, self.vy)
52 # 向左飞出边界
53 if self.rect_new.right < 0 and ship.vx < 0:
54 self.rect_new.left = WIDTH
55 self.rect.left = WIDTH
56 # 向右飞出边界
57 if self.rect_new.left > WIDTH and ship.vx > 0:
58 self.rect_new.right = 0
59 self.rect.right = 0
60 # 向下飞出边界
61 if self.rect_new.top > HEIGHT and ship.vy > 0:
62 self.rect_new.bottom = 0
63 self.rect.bottom = 0
64 # 向上飞出边界
65 if self.rect_new.bottom < 0 and ship.vy < 0:
66 self.rect_new.top = HEIGHT
67 self.rect.top = HEIGHT
68
69 def rotate_zoom(self):
70 # rotozoom=旋转 缩放
71 self.img_new = pygame.transform.rotozoom(self.img, self.angle, self.scale)
72 self.rect_new = self.img_new.get_rect(center=self.rect.center)
73 if math.fabs(self.angle) == 360:
74 self.angle = 0
75
76 def set_flame(self, show_flame=False):
77 self.show_flame = show_flame
78 if self.show_flame:
79 self.img = self.img_flame_src
80 else:
81 self.img = self.img_src
82
83
84 def get_speed(speed):
85 if speed > 0:
86 return math.ceil(speed)
87 if speed < 0:
88 return math.floor(speed)
89 return speed
90
91
92 ship = Ship(img_base_path, screen)
93 ship.scale = 0.5
94 ship.show_rect = True
95 # 摩擦系数
96 friction = 0.995
97 while True:
98 clock.tick(60)
99
100 for event in pygame.event.get():
101 if event.type == pygame.QUIT:
102 sys.exit()
103 elif event.type == pygame.KEYUP:
104 # KEYUP时,熄火,动力归0
105 ship.vr = 0
106 ship.thrust = 0
107 ship.set_flame(False)
108 elif event.type == pygame.KEYDOWN:
109 keys = pygame.key.get_pressed()
110 if keys[pygame.K_LEFT]:
111 ship.vr = 5
112 elif keys[pygame.K_RIGHT]:
113 ship.vr = -5
114 if keys[pygame.K_UP]:
115 # 按向上键时,点火,动力为0.3
116 ship.set_flame(True)
117 ship.thrust = 0.3
118 else:
119 ship.set_flame(False)
120
121 # 将每一帧的底色先填充成白色
122 screen.fill((255, 255, 255))
123
124 pygame.draw.line(screen, GRAY, (0, HEIGHT / 2), (WIDTH, HEIGHT / 2), 1)
125 pygame.draw.line(screen, GRAY, (WIDTH / 2, 0), (WIDTH / 2, HEIGHT), 1)
126
127 ship.angle = ship.vr
128 ax = math.cos(ship.angle * math.pi / 180) * ship.thrust
129 # 注:pygame中,角度是逆时针转的,所以垂直加速度要取反
130 ay = -1 * math.sin(ship.angle * math.pi / 180) * ship.thrust
131 ship.vx = ax
132 ship.vy = ay
133
134 # 摩擦系数
135 if math.fabs(ship.vx) > 0.001:
136 ship.vx = ship.vx * friction
137 if math.fabs(ship.vy) > 0.001:
138 ship.vy = ship.vy * friction
139
140 print("vx:", ship.vx)
141
142 ship.rotate_zoom()
143 ship.move()
144 ship.draw(screen)
145
146 # 更新画布
147 pygame.display.update()
如果把背景变成黑色,辅助边框去掉,看上去更有漆黑宇宙的感觉:)
源代码地址: https://github.com/yjmyzz/pygame_tutorial/blob/master/move_02.py