同步主站 Cocos 当前工程:关卡资源、运行脚本与打包工具。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-12 14:40:34 +08:00
parent ae75fce80b
commit fac515a669
5609 changed files with 27744634 additions and 5653 deletions

View File

@@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""生成关卡左下角等距 X/Y 罗盘(灰箭头 + 青色标注,抗锯齿)。"""
from __future__ import annotations
import math
from pathlib import Path
from PIL import Image, ImageDraw, ImageFilter, ImageFont
# HUD 显示约 1.55× Unity2x 出图保证清晰
OUT_W, OUT_H = 802, 430
SS = 4
ARROW = (92, 98, 108, 255)
ARROW_EDGE = (58, 62, 70, 255)
TEAL = (64, 220, 188, 255)
LABEL_STROKE = (32, 36, 40, 230)
SHADOW = (0, 0, 0, 48)
def _font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
for fp in (
"/System/Library/Fonts/Supplemental/Arial Bold.ttf",
"/System/Library/Fonts/Helvetica.ttc",
"/Library/Fonts/Arial Bold.ttf",
):
try:
return ImageFont.truetype(fp, size)
except OSError:
continue
return ImageFont.load_default()
def _arrow(draw: ImageDraw.ImageDraw, ox: float, oy: float, angle: float, length: float) -> tuple[float, float]:
"""沿 angle屏幕 Y 向上)画一支圆润箭头,返回箭头顶点。"""
dx, dy = math.cos(angle), -math.sin(angle)
px, py = -dy, dx
shaft = 12.5 * SS
head_len = 26 * SS
head_w = 20 * SS
tip_x = ox + dx * length
tip_y = oy + dy * length
base_x = ox + dx * (length - head_len)
base_y = oy + dy * (length - head_len)
# 箭身:圆头线段
draw.line([(ox, oy), (base_x, base_y)], fill=ARROW_EDGE, width=int(shaft + 2.4 * SS), joint="curve")
draw.line([(ox, oy), (base_x, base_y)], fill=ARROW, width=int(shaft), joint="curve")
hx, hy = px * head_w, py * head_w
head = [
(tip_x, tip_y),
(base_x + hx, base_y + hy),
(base_x - hx, base_y - hy),
]
draw.polygon(head, fill=ARROW_EDGE)
inset = 1.15 * SS
inner = [
(tip_x - dx * inset * 0.4, tip_y - dy * inset * 0.4),
(base_x + hx * 0.78 + dx * inset, base_y + hy * 0.78 + dy * inset),
(base_x - hx * 0.78 + dx * inset, base_y - hy * 0.78 + dy * inset),
]
draw.polygon(inner, fill=ARROW)
return tip_x, tip_y
def render() -> Image.Image:
w, h = OUT_W * SS, OUT_H * SS
layer = Image.new("RGBA", (w, h), (0, 0, 0, 0))
draw = ImageDraw.Draw(layer)
ox = w * 0.50
oy = h * 0.84
length = min(w, h) * 0.46
ang_x = math.atan2(0.5, 1.0) # 2:1 等距 +X → 右上
ang_y = math.atan2(0.5, -1.0) # +Y → 左上
tx, ty = _arrow(draw, ox, oy, ang_x, length)
uy, vy = _arrow(draw, ox, oy, ang_y, length)
r = 11 * SS
draw.ellipse((ox - r - SS, oy - r - SS, ox + r + SS, oy + r + SS), fill=ARROW_EDGE)
draw.ellipse((ox - r, oy - r, ox + r, oy + r), fill=ARROW)
ir = 5.2 * SS
draw.ellipse((ox - ir, oy - ir, ox + ir, oy + ir), fill=TEAL)
font = _font(int(60 * SS))
labels = (("Y", uy, vy, ang_y), ("X", tx, ty, ang_x))
stroke = max(2, int(3.6 * SS))
for text, px, py, ang in labels:
dx, dy = math.cos(ang), -math.sin(ang)
lx = px + dx * 30 * SS
ly = py + dy * 30 * SS
bbox = draw.textbbox((0, 0), text, font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
pos = (lx - tw / 2, ly - th / 2 - 6 * SS)
for oxs in range(-stroke, stroke + 1):
for oys in range(-stroke, stroke + 1):
if oxs * oxs + oys * oys > stroke * stroke:
continue
draw.text((pos[0] + oxs, pos[1] + oys), text, font=font, fill=LABEL_STROKE)
draw.text(pos, text, font=font, fill=TEAL)
alpha = layer.split()[3]
sh = Image.new("RGBA", (w, h), (0, 0, 0, 0))
sh_draw = ImageDraw.Draw(sh)
# 用同一 alpha 做淡阴影
sh.putalpha(alpha.point(lambda a: min(255, int(a * 0.28)) if a else 0))
rgb = Image.new("RGB", (w, h), (0, 0, 0))
sh = Image.merge("RGBA", (*rgb.split(), sh.split()[-1]))
sh = sh.filter(ImageFilter.GaussianBlur(radius=1.6 * SS))
composed = Image.new("RGBA", (w, h), (0, 0, 0, 0))
composed.alpha_composite(sh, (int(1.2 * SS), int(1.4 * SS)))
composed.alpha_composite(layer)
out = composed.resize((OUT_W, OUT_H), Image.Resampling.LANCZOS)
alpha = out.split()[3].point(lambda p: 255 if p >= 16 else 0)
box = alpha.getbbox()
if box:
pad = 16
x0 = max(0, box[0] - pad)
y0 = max(0, box[1] - pad)
x1 = min(out.width, box[2] + pad)
y1 = min(out.height, box[3] + pad)
out = out.crop((x0, y0, x1, y1))
return out
def write(path: Path) -> Path:
path.parent.mkdir(parents=True, exist_ok=True)
render().save(path, format="PNG", optimize=True)
return path
if __name__ == "__main__":
out = Path(__file__).resolve().parents[1] / "assets/resources/textures/ui/ui-direction.png"
write(out)
print(f"wrote {out}")