Skip to content

Commit 2a251dc

Browse files
[投稿] 添加脚本: 66 — by 。自以为
1 parent a258927 commit 2a251dc

1 file changed

Lines changed: 369 additions & 0 deletions

File tree

Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
"""无尽跳塔 — 纵向无尽跳跃,弹簧/碎裂/移动平台,金币与道具"""
4+
import scene
5+
import random
6+
import math
7+
8+
PLAT_NORMAL = 0
9+
PLAT_MOVING = 1
10+
PLAT_FRAGILE = 2
11+
PLAT_SPRING = 3
12+
13+
class Particle:
14+
__slots__ = ('x','y','vx','vy','r','g','b','life','ml','sz')
15+
def __init__(self, x, y, r, g, b, spread=3, life=0.5):
16+
a = random.uniform(0, math.pi*2)
17+
s = random.uniform(0.5, spread)
18+
self.x, self.y = x, y
19+
self.vx, self.vy = math.cos(a)*s, math.sin(a)*s
20+
self.r, self.g, self.b = r, g, b
21+
self.life = random.uniform(life*0.3, life)
22+
self.ml = self.life
23+
self.sz = random.uniform(2, 5)
24+
25+
class Platform:
26+
__slots__ = ('x','y','w','kind','alive','move_dir','move_range','ox')
27+
def __init__(self, x, y, w, kind):
28+
self.x, self.y, self.w = x, y, w
29+
self.kind = kind
30+
self.alive = True
31+
self.move_dir = random.choice([-1, 1])
32+
self.move_range = random.uniform(30, 60)
33+
self.ox = x
34+
35+
class Coin:
36+
__slots__ = ('x','y','alive','pulse')
37+
def __init__(self, x, y):
38+
self.x, self.y = x, y
39+
self.alive = True
40+
self.pulse = random.uniform(0, 6.28)
41+
42+
class JumpTower(scene.Scene):
43+
def setup(self):
44+
W, H = self.size.w, self.size.h
45+
self.player_x = W / 2
46+
self.player_y = H * 0.2
47+
self.player_w = 28
48+
self.player_h = 32
49+
self.vy = 0
50+
self.vx = 0
51+
self.camera_y = 0
52+
self.max_height = 0
53+
self.score = 0
54+
self.coins_collected = 0
55+
self.game_over = False
56+
self.frame = 0
57+
self.particles = []
58+
self.floats = []
59+
self.facing_right = True
60+
self.jump_stretch = 0
61+
self.on_ground = False
62+
63+
self.platforms = []
64+
self.coins = []
65+
self._gen_initial_platforms()
66+
67+
self.bg_stars = [{'x': random.uniform(0, W), 'y': random.uniform(0, H*3),
68+
'sz': random.uniform(1, 2.5), 'br': random.uniform(0.1, 0.4)} for _ in range(80)]
69+
70+
def _gen_initial_platforms(self):
71+
W, H = self.size.w, self.size.h
72+
y = 0
73+
base = Platform(W/2, 30, W, PLAT_NORMAL)
74+
self.platforms.append(base)
75+
y = 80
76+
while y < H * 2:
77+
self._add_platform_at(y)
78+
y += random.uniform(45, 75)
79+
80+
def _add_platform_at(self, y):
81+
W = self.size.w
82+
pw = random.uniform(55, 90)
83+
px = random.uniform(pw/2 + 10, W - pw/2 - 10)
84+
height = max(0, y / 200)
85+
r = random.random()
86+
if height > 5 and r < 0.15:
87+
kind = PLAT_FRAGILE
88+
elif height > 3 and r < 0.3:
89+
kind = PLAT_MOVING
90+
elif r < 0.12:
91+
kind = PLAT_SPRING
92+
else:
93+
kind = PLAT_NORMAL
94+
p = Platform(px, y, pw, kind)
95+
self.platforms.append(p)
96+
if random.random() < 0.3:
97+
self.coins.append(Coin(px + random.uniform(-20, 20), y + 25))
98+
99+
def touch_began(self, touch):
100+
if self.game_over:
101+
self.setup()
102+
return
103+
self._handle_touch(touch)
104+
105+
def touch_moved(self, touch):
106+
if not self.game_over:
107+
self._handle_touch(touch)
108+
109+
def touch_ended(self, touch):
110+
self.vx *= 0.5
111+
112+
def _handle_touch(self, touch):
113+
W = self.size.w
114+
x = touch.location.x
115+
center = W / 2
116+
self.vx = (x - center) / center * 8
117+
if self.vx > 0:
118+
self.facing_right = True
119+
elif self.vx < 0:
120+
self.facing_right = False
121+
122+
def update(self):
123+
self.frame += 1
124+
if self.game_over:
125+
self._upd_particles()
126+
return
127+
128+
W, H = self.size.w, self.size.h
129+
gravity = -0.35
130+
131+
self.vy += gravity
132+
self.player_y += self.vy
133+
self.player_x += self.vx
134+
self.vx *= 0.92
135+
136+
if self.player_x < 0:
137+
self.player_x = W
138+
elif self.player_x > W:
139+
self.player_x = 0
140+
141+
if self.jump_stretch > 0:
142+
self.jump_stretch -= 0.05
143+
144+
self.on_ground = False
145+
if self.vy <= 0:
146+
for p in self.platforms:
147+
if not p.alive:
148+
continue
149+
if (self.player_y > p.y and self.player_y < p.y + 15 and
150+
abs(self.player_x - p.x) < (p.w/2 + self.player_w/2 - 5)):
151+
if p.kind == PLAT_FRAGILE:
152+
p.alive = False
153+
for _ in range(8):
154+
self.particles.append(Particle(p.x, p.y, 0.6, 0.5, 0.4, 3, 0.4))
155+
self.vy = 10
156+
self.jump_stretch = 1
157+
elif p.kind == PLAT_SPRING:
158+
self.vy = 18
159+
self.jump_stretch = 1
160+
for _ in range(5):
161+
self.particles.append(Particle(self.player_x, self.player_y, 0.2, 1, 0.5, 4, 0.3))
162+
else:
163+
self.vy = 10
164+
self.jump_stretch = 1
165+
self.on_ground = True
166+
break
167+
168+
for p in self.platforms:
169+
if p.kind == PLAT_MOVING and p.alive:
170+
p.x += p.move_dir * 1.2
171+
if abs(p.x - p.ox) > p.move_range:
172+
p.move_dir *= -1
173+
174+
for c in self.coins:
175+
if not c.alive:
176+
continue
177+
if abs(self.player_x - c.x) < 20 and abs(self.player_y - c.y) < 20:
178+
c.alive = False
179+
self.coins_collected += 1
180+
self.score += 25
181+
self.floats.append({'x': c.x, 'y': c.y, 'text': '+25', 'life': 1.0})
182+
for _ in range(6):
183+
self.particles.append(Particle(c.x, c.y, 1, 0.85, 0.15, 3, 0.4))
184+
185+
if self.player_y > self.max_height:
186+
diff = int((self.player_y - self.max_height) / 10)
187+
self.score += diff
188+
self.max_height = self.player_y
189+
190+
target_cam = self.player_y - H * 0.4
191+
self.camera_y += (target_cam - self.camera_y) * 0.1
192+
193+
top_y = self.camera_y + H + 100
194+
if self.platforms:
195+
highest = max(p.y for p in self.platforms)
196+
while highest < top_y:
197+
highest += random.uniform(45, 75)
198+
self._add_platform_at(highest)
199+
200+
self.platforms = [p for p in self.platforms if p.y > self.camera_y - 100 and p.alive]
201+
self.coins = [c for c in self.coins if c.y > self.camera_y - 100 and c.alive]
202+
203+
if self.player_y < self.camera_y - 100:
204+
self.game_over = True
205+
for _ in range(20):
206+
self.particles.append(Particle(self.player_x, self.player_y, 1, 0.4, 0.2, 6, 0.8))
207+
208+
self._upd_particles()
209+
for f in self.floats:
210+
f['y'] += 0.8; f['life'] -= 1/60
211+
self.floats = [f for f in self.floats if f['life'] > 0]
212+
213+
def _upd_particles(self):
214+
dt = 1/60
215+
for p in self.particles:
216+
p.x += p.vx; p.y += p.vy; p.life -= dt
217+
self.particles = [p for p in self.particles if p.life > 0]
218+
219+
def _screen_y(self, world_y):
220+
return world_y - self.camera_y
221+
222+
def draw(self):
223+
W, H = self.size.w, self.size.h
224+
insets = self.safe_area_insets
225+
fc = self.frame
226+
cy = self.camera_y
227+
228+
height_ratio = min(1, self.max_height / 5000)
229+
br = 0.05 + height_ratio*0.02
230+
bg = 0.04 + height_ratio*0.01
231+
bb = 0.12 - height_ratio*0.05
232+
scene.background(br, bg, max(0.02, bb))
233+
234+
for s in self.bg_stars:
235+
sy = s['y'] - cy * 0.3
236+
sy = sy % (H * 3)
237+
if 0 < sy < H:
238+
a = s['br'] * (0.5 + 0.5*math.sin(fc*0.005 + s['br']*20))
239+
scene.fill(0.7, 0.8, 1, a)
240+
scene.rect(s['x'], sy, s['sz'], s['sz'])
241+
242+
for p in self.platforms:
243+
if not p.alive:
244+
continue
245+
sy = self._screen_y(p.y)
246+
if sy < -20 or sy > H + 20:
247+
continue
248+
hw = p.w / 2
249+
if p.kind == PLAT_NORMAL:
250+
scene.fill(0.2, 0.55, 0.35)
251+
scene.rect(p.x - hw, sy, p.w, 10, 5)
252+
scene.fill(0.3, 0.75, 0.45, 0.4)
253+
scene.rect(p.x - hw + 3, sy + 7, p.w - 6, 2, 2)
254+
elif p.kind == PLAT_MOVING:
255+
scene.fill(0.2, 0.45, 0.8)
256+
scene.rect(p.x - hw, sy, p.w, 10, 5)
257+
scene.fill(0.4, 0.65, 1.0, 0.4)
258+
scene.rect(p.x - hw + 3, sy + 7, p.w - 6, 2, 2)
259+
scene.fill(0.4, 0.65, 1.0, 0.3)
260+
for i in range(3):
261+
ax = p.x - hw + 10 + i*20
262+
if ax < p.x + hw - 5:
263+
scene.rect(ax, sy + 3, 8, 2, 1)
264+
elif p.kind == PLAT_FRAGILE:
265+
scene.fill(0.6, 0.45, 0.3)
266+
scene.rect(p.x - hw, sy, p.w, 10, 5)
267+
scene.stroke(0.4, 0.3, 0.2, 0.5)
268+
scene.stroke_weight(1)
269+
scene.line(p.x - hw + 10, sy + 2, p.x - hw + 20, sy + 8)
270+
scene.line(p.x + hw - 15, sy + 3, p.x + hw - 8, sy + 7)
271+
scene.no_stroke()
272+
elif p.kind == PLAT_SPRING:
273+
scene.fill(0.2, 0.55, 0.35)
274+
scene.rect(p.x - hw, sy, p.w, 10, 5)
275+
scene.fill(1.0, 0.3, 0.35)
276+
spring_w = 18
277+
scene.rect(p.x - spring_w/2, sy + 10, spring_w, 10, 4)
278+
scene.fill(1.0, 0.5, 0.5, 0.4)
279+
scene.rect(p.x - spring_w/2 + 3, sy + 17, spring_w - 6, 2, 1)
280+
281+
for c in self.coins:
282+
if not c.alive:
283+
continue
284+
sy = self._screen_y(c.y)
285+
if sy < -20 or sy > H + 20:
286+
continue
287+
pulse = 0.7 + 0.3*math.sin(fc*0.06 + c.pulse)
288+
scene.fill(1, 0.85, 0.15, 0.12*pulse)
289+
scene.ellipse(c.x - 12, sy - 12, 24, 24)
290+
scene.fill(1, 0.85, 0.15)
291+
scene.ellipse(c.x - 7, sy - 7, 14, 14)
292+
scene.fill(1, 1, 0.6, 0.4)
293+
scene.ellipse(c.x - 3, sy + 1, 5, 5)
294+
295+
py_screen = self._screen_y(self.player_y)
296+
px = self.player_x
297+
pw, ph = self.player_w, self.player_h
298+
stretch_y = 1 + self.jump_stretch * 0.15
299+
stretch_x = 1 - self.jump_stretch * 0.08
300+
scene.push_matrix()
301+
scene.translate(px, py_screen + ph/2)
302+
scene.scale(stretch_x if self.facing_right else -stretch_x, stretch_y)
303+
scene.fill(0.9, 0.65, 0.4)
304+
scene.ellipse(-pw*0.3, ph*0.05, pw*0.6, ph*0.55, )
305+
scene.fill(0.95, 0.75, 0.55)
306+
scene.ellipse(-pw*0.25, ph*0.35, pw*0.5, pw*0.5)
307+
scene.fill(0.2, 0.2, 0.25)
308+
scene.ellipse(pw*0.02, ph*0.5, 5, 5)
309+
scene.fill(1, 1, 1)
310+
scene.ellipse(pw*0.01, ph*0.52, 3, 3)
311+
if self.vy > 2:
312+
scene.fill(1, 0.6, 0.15, 0.5)
313+
scene.ellipse(-4, -ph*0.2, 8, 10)
314+
scene.fill(1, 0.8, 0.3, 0.3)
315+
scene.ellipse(-3, -ph*0.3, 6, 8)
316+
scene.pop_matrix()
317+
318+
for p in self.particles:
319+
sy = self._screen_y(p.y)
320+
a = p.life / p.ml
321+
scene.fill(p.r, p.g, p.b, a*0.7)
322+
sz = p.sz * a
323+
scene.ellipse(p.x-sz/2, sy-sz/2, sz, sz)
324+
325+
for f in self.floats:
326+
sy = self._screen_y(f['y'])
327+
scene.tint(1, 0.85, 0.15, f['life'])
328+
scene.text(f['text'], 'Arial-BoldMT', 14, f['x'], sy)
329+
scene.no_tint()
330+
331+
hud_top = H - insets.top
332+
hud_h = 48
333+
scene.fill(br, bg, max(0.02, bb), 0.8)
334+
scene.rect(0, hud_top - hud_h, W, hud_h + insets.top)
335+
hcy = hud_top - hud_h / 2
336+
scene.tint(0.5, 0.8, 0.5)
337+
h_m = int(self.max_height / 10)
338+
scene.text(f'↑{h_m}m', 'Arial-BoldMT', 14, 40, hcy + 10)
339+
scene.tint(1, 1, 1)
340+
scene.text(f'{self.score}', 'Arial-BoldMT', 22, W/2, hcy + 2)
341+
scene.tint(0.5, 0.5, 0.6)
342+
scene.text('分数', 'Arial', 10, W/2, hcy + 17)
343+
scene.tint(1, 0.85, 0.15)
344+
scene.text(f'● {self.coins_collected}', 'Arial-BoldMT', 14, W - 45, hcy + 2)
345+
scene.no_tint()
346+
347+
if self.game_over:
348+
scene.fill(0, 0, 0, 0.8)
349+
scene.rect(0, 0, W, H)
350+
p = 1 + 0.03*math.sin(fc*0.06)
351+
scene.push_matrix()
352+
scene.translate(W/2, H/2+70)
353+
scene.scale(p, p)
354+
scene.tint(1, 0.5, 0.3)
355+
scene.text('坠落了!', 'Arial-BoldMT', 40, 0, 0)
356+
scene.pop_matrix()
357+
scene.tint(1, 0.95, 0.7)
358+
scene.text(f'{self.score}', 'Arial-BoldMT', 48, W/2, H/2+15)
359+
scene.tint(0.6, 0.6, 0.7)
360+
scene.text('最终分数', 'Arial', 14, W/2, H/2+40)
361+
scene.tint(0.8, 0.8, 0.9)
362+
scene.text(f'高度 {int(self.max_height/10)}m | 金币 {self.coins_collected}', 'Arial', 16, W/2, H/2-15)
363+
blink = 0.4+0.6*abs(math.sin(fc*0.04))
364+
scene.tint(0.6, 0.7, 0.9, blink)
365+
scene.text('轻触屏幕再来一局', 'Arial', 18, W/2, H/2-50)
366+
scene.no_tint()
367+
368+
if __name__ == '__main__':
369+
scene.run(JumpTower())

0 commit comments

Comments
 (0)