- Все 189 скелетов Spine сконвертированы в нативные Godot Skeleton2D (.tscn), без Spine в рантайме - Меню + сцены: подземелье (карта комнат), просмотр спрайтов, нативные анимации - Кастомизация цвета (кожа/волосы/глаза) через шейдер - Вытащенные текстуры/UI/шрифты + нарезка спрайтов из атласа - Датасет комнат для LoRA, офлайн-инструменты конвертации в tools/ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
69 lines
2.2 KiB
GDScript
69 lines
2.2 KiB
GDScript
extends SceneTree
|
|
|
|
# Нарезает отдельные спрайты из упакованных Unity SpriteAtlas по разметке
|
|
# tools/sprite_layout.json (сгенерирована из AssetRipper Sprite/*.json).
|
|
# Unity-координаты снизу-вверх => при вырезании нужен flip по Y.
|
|
# Запуск:
|
|
# godot-4.2-4.6.1-stable-mono.exe --headless --path <проект> --script res://tools/slice_atlas.gd
|
|
|
|
const LAYOUT := "res://tools/sprite_layout.json"
|
|
const ATLAS_DIR := "res://assets/atlas/"
|
|
const OUT_DIR := "res://assets/"
|
|
|
|
func _initialize():
|
|
var f := FileAccess.open(LAYOUT, FileAccess.READ)
|
|
if f == null:
|
|
push_error("нет %s" % LAYOUT); quit(); return
|
|
var data: Dictionary = JSON.parse_string(f.get_as_text())
|
|
f.close()
|
|
var sprites: Array = data.get("sprites", [])
|
|
|
|
var atlas_cache := {}
|
|
var ok := 0
|
|
var fail := 0
|
|
var groups := {}
|
|
|
|
for s in sprites:
|
|
var atlas_file: String = s["atlas"]
|
|
if not atlas_cache.has(atlas_file):
|
|
var img := Image.new()
|
|
var err := img.load(ATLAS_DIR + atlas_file)
|
|
atlas_cache[atlas_file] = img if err == OK else null
|
|
if err != OK:
|
|
push_error("не открылся атлас: " + atlas_file)
|
|
var atlas: Image = atlas_cache[atlas_file]
|
|
if atlas == null:
|
|
fail += 1; continue
|
|
|
|
var w: int = s["w"]
|
|
var h: int = s["h"]
|
|
if w <= 0 or h <= 0:
|
|
fail += 1; continue
|
|
var x: int = s["x"]
|
|
var top: int = atlas.get_height() - (int(s["y"]) + h) # flip Y
|
|
x = clampi(x, 0, atlas.get_width() - w)
|
|
top = clampi(top, 0, atlas.get_height() - h)
|
|
|
|
var sub := atlas.get_region(Rect2i(x, top, w, h))
|
|
|
|
var group: String = s["group"]
|
|
var out_path := "%s%s/%s.png" % [OUT_DIR, group, _safe(s["name"])]
|
|
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path("%s%s" % [OUT_DIR, group]))
|
|
if sub.save_png(out_path) == OK:
|
|
ok += 1
|
|
groups[group] = groups.get(group, 0) + 1
|
|
else:
|
|
fail += 1
|
|
|
|
print("НАРЕЗКА: сохранено %d, ошибок %d, по группам %s" % [ok, fail, groups])
|
|
quit()
|
|
|
|
func _safe(name: String) -> String:
|
|
var out := ""
|
|
for c in name:
|
|
if (c >= "a" and c <= "z") or (c >= "A" and c <= "Z") or (c >= "0" and c <= "9") or c in ["_", "-", "(", ")", "."]:
|
|
out += c
|
|
else:
|
|
out += "_"
|
|
return out
|