Skip to content

Commit a127d9b

Browse files
Merge pull request #36 from jinwandalaohu66/submission/script_mn0g9yri
2 parents fd83e4b + 05788f0 commit a127d9b

2 files changed

Lines changed: 350 additions & 2 deletions

File tree

script_library/index.json

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"format_version": 1,
3-
"data_version": 14,
4-
"updated": "2026-03-20T14:18:52.520Z",
3+
"data_version": 15,
4+
"updated": "2026-03-21T14:55:45.667Z",
55
"announcement": null,
66
"categories": [
77
{
@@ -518,6 +518,29 @@
518518
"updated": null,
519519
"status": "active",
520520
"lines": 85
521+
},
522+
{
523+
"id": "script_mn0g9yri",
524+
"name": "火柴人搭桥",
525+
"name_en": "火柴人搭桥",
526+
"desc": "经典搭桥小游戏,用手指按在屏幕上,桥就会开始增加长度.你要在两根柱子之间正确的把桥搭好才能正确过关,过长过短都会掉下悬崖!考验你的距离敏感度!",
527+
"desc_en": "经典搭桥小游戏,用手指按在屏幕上,桥就会开始增加长度.你要在两根柱子之间正确的把桥搭好才能正确过关,过长过短都会掉下悬崖!考验你的距离敏感度!",
528+
"category": "games",
529+
"file": "scripts/games/script_mn0g9yri.py",
530+
"thumbnail": null,
531+
"version": 1,
532+
"file_type": "py",
533+
"author": "XiaoYuan",
534+
"author_en": "XiaoYuan",
535+
"tags": [
536+
"community"
537+
],
538+
"requires": [],
539+
"min_app_version": "1.5.0",
540+
"added": "2026-03-21",
541+
"updated": null,
542+
"status": "active",
543+
"lines": 325
521544
}
522545
]
523546
}
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
from scene import *
2+
import random
3+
import math
4+
5+
6+
class StickBridgeCN(Scene):
7+
def setup(self):
8+
self._init_game()
9+
self._start_new_run()
10+
11+
def _init_game(self):
12+
self.sky_color = (0.55, 0.82, 0.98)
13+
self.ocean_color_top = (0.20, 0.62, 0.86)
14+
self.ocean_color_mid = (0.12, 0.45, 0.74)
15+
self.ocean_color_deep = (0.07, 0.28, 0.50)
16+
self.pillar_color = (0.08, 0.08, 0.12)
17+
self.stick_color = (0.03, 0.03, 0.03)
18+
19+
self.hero_skins = [
20+
(0.02, 0.02, 0.02),
21+
(0.12, 0.18, 0.40),
22+
(0.08, 0.34, 0.20),
23+
(0.36, 0.14, 0.12),
24+
]
25+
self.skin_index = 0
26+
self.hero_color = self.hero_skins[self.skin_index]
27+
28+
self.base_y = self.size.h * 0.2
29+
self.pillar_h = self.size.h * 0.3
30+
self.top_y = self.base_y + self.pillar_h
31+
32+
self.start_left_x = self.size.w * 0.12
33+
self.start_left_w = self.size.w * 0.14
34+
35+
self.left_x = self.start_left_x
36+
self.left_w = self.start_left_w
37+
self.right_x = 0.0
38+
self.right_w = 0.0
39+
40+
self.state = 'ready'
41+
self.score = 0
42+
self.best_score = 0
43+
self.last_gain = 0
44+
45+
self.success = False
46+
self.perfect = False
47+
48+
self.grow_speed = self.size.h * 0.95
49+
self.rotate_speed = math.pi * 1.8
50+
self.walk_speed = self.size.w * 0.52
51+
self.fall_g = self.size.h * 2.4
52+
53+
self.stick_len = self.size.h * 0.002
54+
self.stick_angle = math.pi / 2
55+
56+
self.hero_h = self.size.h * 0.085
57+
self.hero_foot_offset = self.hero_h * 0.18
58+
59+
self.hero_x = 0.0
60+
self.hero_y = 0.0
61+
self.hero_tilt = 0.0
62+
self.hero_vy = 0.0
63+
64+
self.bridge_end_x = 0.0
65+
self.walk_target_x = 0.0
66+
67+
self.camera_x = 0.0
68+
self.camera_target_x = 0.0
69+
70+
self.dead_timer = 0.0
71+
self.gain_show_timer = 0.0
72+
self.hint_text = '按住蓄力,松手放桥'
73+
74+
self.ui_top_inset = max(26, self.size.h * 0.035)
75+
76+
def _generate_right_pillar(self):
77+
min_gap = self.size.w * 0.18
78+
max_gap = self.size.w * 0.45
79+
min_w = self.size.w * 0.08
80+
max_w = self.size.w * 0.18
81+
82+
gap = random.uniform(min_gap, max_gap)
83+
w = random.uniform(min_w, max_w)
84+
x = self.left_x + self.left_w + gap
85+
return x, w
86+
87+
def _prepare_round(self, first_round=False):
88+
if not first_round:
89+
self.left_x = self.right_x
90+
self.left_w = self.right_w
91+
92+
self.right_x, self.right_w = self._generate_right_pillar()
93+
94+
self.stick_len = self.size.h * 0.002
95+
self.stick_angle = math.pi / 2
96+
97+
self.hero_x = self.left_x + self.left_w
98+
self.hero_y = self.top_y + self.hero_foot_offset
99+
self.hero_tilt = 0.0
100+
self.hero_vy = 0.0
101+
102+
self.success = False
103+
self.perfect = False
104+
self.state = 'ready'
105+
self.hint_text = '按住蓄力,松手放桥'
106+
107+
def _start_new_run(self):
108+
self.left_x = self.start_left_x
109+
self.left_w = self.start_left_w
110+
self.camera_x = 0.0
111+
self.camera_target_x = 0.0
112+
self.score = 0
113+
self.last_gain = 0
114+
self.gain_show_timer = 0.0
115+
self._prepare_round(first_round=True)
116+
117+
def _on_dead_reset(self):
118+
if self.score > self.best_score:
119+
self.best_score = self.score
120+
121+
self.skin_index = (self.skin_index + 1) % len(self.hero_skins)
122+
self.hero_color = self.hero_skins[self.skin_index]
123+
124+
self._start_new_run()
125+
126+
def touch_began(self, touch):
127+
if self.state == 'ready':
128+
self.state = 'growing'
129+
self.stick_len = self.size.h * 0.002
130+
self.hint_text = '正在蓄力...'
131+
132+
def touch_ended(self, touch):
133+
if self.state == 'growing':
134+
self.state = 'rotating'
135+
self.hint_text = '放桥中...'
136+
137+
def update(self):
138+
dt = min(self.dt, 1 / 30)
139+
140+
self.camera_x += (self.camera_target_x - self.camera_x) * min(1.0, dt * 6.0)
141+
142+
if self.gain_show_timer > 0:
143+
self.gain_show_timer -= dt
144+
if self.gain_show_timer < 0:
145+
self.gain_show_timer = 0
146+
147+
if self.state == 'growing':
148+
self.stick_len += self.grow_speed * dt
149+
150+
elif self.state == 'rotating':
151+
self.stick_angle -= self.rotate_speed * dt
152+
if self.stick_angle <= 0:
153+
self.stick_angle = 0
154+
self.bridge_end_x = self.left_x + self.left_w + self.stick_len
155+
156+
self.success = self.right_x <= self.bridge_end_x <= (self.right_x + self.right_w)
157+
158+
if self.success:
159+
center = self.right_x + self.right_w * 0.5
160+
perfect_range = max(self.size.w * 0.015, self.right_w * 0.1)
161+
self.perfect = abs(self.bridge_end_x - center) <= perfect_range
162+
else:
163+
self.perfect = False
164+
165+
self.walk_target_x = self.bridge_end_x
166+
self.state = 'walking'
167+
self.hint_text = '前进中...'
168+
169+
elif self.state == 'walking':
170+
self.hero_x += self.walk_speed * dt
171+
self.camera_target_x = max(0.0, self.hero_x - self.size.w * 0.35)
172+
173+
if self.hero_x >= self.walk_target_x:
174+
self.hero_x = self.walk_target_x
175+
if self.success:
176+
self.walk_target_x = self.right_x + self.right_w * 0.5
177+
self.state = 'to_center'
178+
else:
179+
self.state = 'falling'
180+
self.hero_vy = 0.0
181+
self.hint_text = '桥长不合适,掉落中...'
182+
183+
elif self.state == 'to_center':
184+
self.hero_x += self.walk_speed * dt
185+
self.camera_target_x = max(0.0, self.hero_x - self.size.w * 0.35)
186+
187+
if self.hero_x >= self.walk_target_x:
188+
self.hero_x = self.walk_target_x
189+
190+
gain = 1
191+
if self.perfect:
192+
gain += 2
193+
194+
self.last_gain = gain
195+
self.score += gain
196+
self.gain_show_timer = 0.8
197+
198+
if self.perfect:
199+
self.hint_text = '完美落点!+3'
200+
else:
201+
self.hint_text = '过桥成功!+1'
202+
203+
self._prepare_round(first_round=False)
204+
205+
elif self.state == 'falling':
206+
self.hero_vy -= self.fall_g * dt
207+
self.hero_y += self.hero_vy * dt
208+
self.hero_tilt -= 2.8 * dt
209+
self.camera_target_x = max(0.0, self.hero_x - self.size.w * 0.35)
210+
211+
if self.hero_y < -self.size.h * 0.2:
212+
self.state = 'dead_pause'
213+
self.dead_timer = 0.55
214+
self.hint_text = '坠落失败,正在重置...'
215+
216+
elif self.state == 'dead_pause':
217+
self.dead_timer -= dt
218+
if self.dead_timer <= 0:
219+
self._on_dead_reset()
220+
221+
def _draw_hero(self):
222+
h = self.hero_h
223+
head_r = h * 0.12
224+
225+
push_matrix()
226+
translate(self.hero_x, self.hero_y)
227+
rotate(self.hero_tilt)
228+
229+
stroke(self.hero_color[0], self.hero_color[1], self.hero_color[2], 1)
230+
stroke_weight(self.size.w * 0.007)
231+
232+
line(0, 0, 0, h * 0.45)
233+
line(0, h * 0.35, -h * 0.16, h * 0.22)
234+
line(0, h * 0.35, h * 0.16, h * 0.22)
235+
line(0, 0, -h * 0.14, -h * 0.18)
236+
line(0, 0, h * 0.14, -h * 0.18)
237+
238+
no_stroke()
239+
fill(self.hero_color[0], self.hero_color[1], self.hero_color[2], 1)
240+
ellipse(-head_r, h * 0.45, head_r * 2, head_r * 2)
241+
242+
pop_matrix()
243+
244+
def _draw_world(self):
245+
background(self.sky_color[0], self.sky_color[1], self.sky_color[2])
246+
247+
push_matrix()
248+
translate(-self.camera_x, 0)
249+
250+
no_stroke()
251+
fill(self.ocean_color_deep[0], self.ocean_color_deep[1], self.ocean_color_deep[2], 1)
252+
rect(self.camera_x - self.size.w, 0, self.size.w * 4, self.base_y)
253+
254+
fill(self.ocean_color_mid[0], self.ocean_color_mid[1], self.ocean_color_mid[2], 0.95)
255+
rect(self.camera_x - self.size.w, self.base_y * 0.35, self.size.w * 4, self.base_y * 0.65)
256+
257+
fill(self.ocean_color_top[0], self.ocean_color_top[1], self.ocean_color_top[2], 0.9)
258+
rect(self.camera_x - self.size.w, self.base_y * 0.72, self.size.w * 4, self.base_y * 0.28)
259+
260+
stroke(1, 1, 1, 0.32)
261+
stroke_weight(self.size.h * 0.0035)
262+
wave_step = self.size.w * 0.09
263+
start_x = self.camera_x - self.size.w
264+
end_x = self.camera_x + self.size.w * 3
265+
phase = self.t * 1.8
266+
x = start_x
267+
while x < end_x:
268+
y1 = self.base_y - self.size.h * 0.008 + math.sin(x * 0.03 + phase) * self.size.h * 0.003
269+
y2 = y1 + math.sin(x * 0.045 + phase * 1.2) * self.size.h * 0.0015
270+
line(x, y1, x + wave_step * 0.55, y2)
271+
x += wave_step
272+
273+
no_stroke()
274+
fill(self.pillar_color[0], self.pillar_color[1], self.pillar_color[2], 1)
275+
rect(self.left_x, self.base_y, self.left_w, self.pillar_h)
276+
rect(self.right_x, self.base_y, self.right_w, self.pillar_h)
277+
278+
base_x = self.left_x + self.left_w
279+
end_x = base_x + self.stick_len * math.cos(self.stick_angle)
280+
end_y = self.top_y + self.stick_len * math.sin(self.stick_angle)
281+
282+
stroke(self.stick_color[0], self.stick_color[1], self.stick_color[2], 1)
283+
stroke_weight(self.size.w * 0.01)
284+
line(base_x, self.top_y, end_x, end_y)
285+
286+
if self.state in ('ready', 'growing', 'rotating', 'walking', 'to_center'):
287+
center = self.right_x + self.right_w * 0.5
288+
mark_w = max(self.size.w * 0.01, self.right_w * 0.08)
289+
no_stroke()
290+
fill(1, 0.25, 0.25, 0.8)
291+
rect(center - mark_w * 0.5, self.top_y - self.size.h * 0.01, mark_w, self.size.h * 0.01)
292+
293+
self._draw_hero()
294+
pop_matrix()
295+
296+
def _draw_panel(self):
297+
panel_h = self.size.h * 0.12
298+
panel_y = self.size.h - self.ui_top_inset - panel_h - self.size.h * 0.09
299+
300+
no_stroke()
301+
fill(0, 0, 0, 0.28)
302+
rect(0, panel_y, self.size.w, panel_h)
303+
304+
fill(1, 1, 1, 1)
305+
text('当前分', x=self.size.w * 0.17, y=panel_y + panel_h * 0.65, font_size=self.size.h * 0.028, alignment=5)
306+
text(str(self.score), x=self.size.w * 0.17, y=panel_y + panel_h * 0.28, font_size=self.size.h * 0.048, alignment=5)
307+
308+
text('最高分', x=self.size.w * 0.83, y=panel_y + panel_h * 0.65, font_size=self.size.h * 0.028, alignment=5)
309+
text(str(self.best_score), x=self.size.w * 0.83, y=panel_y + panel_h * 0.28, font_size=self.size.h * 0.048, alignment=5)
310+
311+
fill(1, 1, 1, 0.95)
312+
hint_y = panel_y - self.size.h * 0.035
313+
text(self.hint_text, x=self.size.w * 0.5, y=hint_y, font_size=self.size.h * 0.03, alignment=5)
314+
315+
if self.gain_show_timer > 0:
316+
alpha = min(1.0, self.gain_show_timer / 0.8)
317+
fill(1, 0.93, 0.2, alpha)
318+
text(f'+{self.last_gain}', x=self.size.w * 0.5, y=hint_y - self.size.h * 0.028, font_size=self.size.h * 0.024, alignment=5)
319+
320+
def draw(self):
321+
self._draw_world()
322+
self._draw_panel()
323+
324+
325+
run(StickBridgeCN(), PORTRAIT)

0 commit comments

Comments
 (0)