lingdev
↓ 0 下载LingDev: a Godot / Unity-style game editor written in Ling. 2-D and 3-D scenes, Flash-style timeline and cutscenes, HUDs over 3-D, Ling scripting, audio / music / MIDI, builds for Windows, Linux, macOS, web, Raspberry Pi and LingOS. Ships with Ling Agent (an N64-style spy FPS), Ling Breaker (a 3-D breakout) and Moko's Picture Book (a kawaii storybook platformer).
关键词 game-engine, editor, 3d, 2d, cutscene, timeline, ling, lingos, raspberry-pi
lingfu install lingdev代码 / 文件 (136)
ling/assets.ling
# assets.ling - the asset browser: every file under the project, filtered by kind, searchable,
# draggable into the viewport. Double-click opens scenes / scripts or adds the asset to the scene.
fn assets_mark_stale() { st_set("as_stale", 1.0) }
fn assets_skip_dir(name) {
return name == "rt" || name == "dist" || name == ".autosave" || name == ".ling-build" || name == "target" || name == "cache" || name == ".git" || name == "node_modules" || name == "lingos" || name == "AST"
}
fn assets_scan() {
st_lclear("as_path")
st_lclear("as_rel")
st_lclear("as_ext")
bind root = proj_root()
if root == "" { bind root = p_join(st_get("app_root", "."), "projects") }
assets_walk(root, root, 0.0)
# an imported game keeps its big asset folders outside the project
bind src = st_get("proj_source", "")
if src != "" && fs_is_dir(src) { assets_walk(src, src, 0.0) }
st_set("as_stale", 0.0)
st_set("as_scan_t", st_get("now", 0.0))
}
fn assets_walk(root, dir, depth) {
if depth > 5.0 || st_llen("as_path") > 4000.0 { return 0.0 }
bind items = plat_list(dir)
bind n = len(items) / 3.0
bind i = 0.0
while i < n {
bind nm = list_get(items, i * 3.0)
bind isd = is_true(list_get(items, i * 3.0 + 1.0))
bind full = p_join(dir, nm)
if isd {
if !assets_skip_dir(nm) && !(depth == 0.0 && nm == "scripts" && false) { assets_walk(root, full, depth + 1.0) }
} else {
bind e = p_ext(nm)
if assets_kind(e) != "" {
st_lpush("as_path", full)
st_lpush("as_rel", p_rel(root, full))
st_lpush("as_ext", e)
}
}
bind i = i + 1.0
}
return 0.0
}
fn assets_kind(e) {
if e == "lscene" || e == "lcut" { return "scene" }
if e == "glb" || e == "gltf" || e == "lmesh" || e == "blend" { return "model" }
if e == "png" || e == "jpg" || e == "jpeg" || e == "bmp" || e == "svg" { return "image" }
if e == "wav" || e == "ogg" || e == "mp3" || e == "flac" || e == "aac" { return "audio" }
if e == "mid" || e == "midi" { return "midi" }
if e == "ling" { return "script" }
if e == "ttf" || e == "otf" { return "font" }
if e == "txt" || e == "md" || e == "toml" || e == "cfg" || e == "lrc" { return "text" }
return ""
}
fn assets_panel(x, y, w, h) {
g_rect(x, y, w, h, C_BG_SIDE)
u_vsep(x + w - 1.0, y, h)
u_hsep(x, y, w)
ds_word(x + 10.0, y + 8.0, 10.0, "ASSETS", ds_col("lit"), true)
if u_hot(x + w - 54.0, y + 5.0, 22.0, 20.0) { u_tip("Rescan the project folder") }
if u_icon_button("as_refresh", x + w - 54.0, y + 5.0, 22.0, 20.0, "refresh", 11.0, C_TEXT_STRONG, false) { assets_mark_stale() }
if u_hot(x + w - 30.0, y + 5.0, 22.0, 20.0) { u_tip("Import a file into assets/ (Ctrl+I)") }
if u_icon_button("as_import", x + w - 30.0, y + 5.0, 22.0, 20.0, "plus", 11.0, C_TEXT_STRONG, false) { do_action("asset.import") }
if st_get("as_stale", 1.0) > 0.5 || st_get("now", 0.0) - st_get("as_scan_t", 0.0) > 8.0 { assets_scan() }
# filters
bind fy = y + 30.0
bind filters = ["ALL", "SCN", "MDL", "IMG", "SND", "MID", "SCR"]
bind fsel = ds_selector("as_filter", x + 8.0, fy, 10.0, filters, st_get("as_fi", 0.0), ds_col("lit"), "Show")
if fsel >= 0.0 { st_set("as_fi", fsel) st_set("as_scroll", 0.0) }
bind qy = fy + 16.0
bind q = u_textfield("as_q", x + 8.0, qy, w - 18.0, 22.0, st_get("as_q", ""))
st_set("as_q", q)
if q == "" && st_get("focus", "") != "as_q" { g_text(x + 16.0, qy + 5.0, 11.0, C_TEXT_DIM, "search assets") }
bind top = qy + 28.0
bind vh = h - (top - y) - 4.0
# rows
bind fi = st_get("as_fi", 0.0)
bind kinds = ["", "scene", "model", "image", "audio", "midi", "script"]
bind want = list_get(kinds, fi)
bind ql = str_lower(q)
bind rows = []
bind n = st_llen("as_path")
bind i = 0.0
while i < n {
bind e = st_lget("as_ext", i)
bind k = assets_kind(e)
if (want == "" || k == want) && (ql == "" || str_find(str_lower(st_lget("as_rel", i)), ql) >= 0.0) { bind rows = list_push(rows, i) }
bind i = i + 1.0
}
bind rh = 20.0
bind total = len(rows) * rh + 8.0
bind off = u_scroll_y("as_sb", x, top, w - 1.0, vh, total, st_get("as_scroll", 0.0))
st_set("as_scroll", off)
u_clip(x, top, w - 1.0, vh)
bind r = 0.0
while r < len(rows) {
bind i = list_get(rows, r)
bind ry = top + 4.0 + r * rh - off
if ry + rh >= top && ry <= top + vh {
bind path = st_lget("as_path", i)
bind rel = st_lget("as_rel", i)
bind e = st_lget("as_ext", i)
bind selected = st_get("asset_sel", "") == path
bind hot = u_hot(x, ry, w - 12.0, rh)
if selected { g_rect(x, ry, w - 1.0, rh, C_SEL_ROW) } else if hot { g_rect(x, ry, w - 1.0, rh, C_HOVER_ROW) }
icon_draw(ext_icon(e), x + 16.0, ry + rh * 0.5, 12.0, ext_col(e))
bind dir = p_dir(rel)
bind name = p_base(rel)
if dir != "" {
bind dw = g_text_w(10.0, dir + "/")
if dw < (w - 60.0) * 0.5 { g_text(x + 30.0, ry + 5.0, 10.0, C_TEXT_DIM, dir + "/") g_text_fit(x + 30.0 + dw, ry + 4.0, w - 44.0 - dw, 11.0, sel(selected, C_TEXT_STRONG, C_TEXT), name) }
else { g_text_fit(x + 30.0, ry + 4.0, w - 44.0, 11.0, sel(selected, C_TEXT_STRONG, C_TEXT), name) }
} else { g_text_fit(x + 30.0, ry + 4.0, w - 44.0, 11.0, sel(selected, C_TEXT_STRONG, C_TEXT), name) }
if hot {
u_tip(rel)
if u_pressed() {
st_set("asset_sel", path)
st_set("drag_path", path)
st_set("drag_x", u_mx())
st_set("drag_y", u_my())
st_set("drag_live", 0.0)
if u_double() { asset_activate(path) }
}
if u_rpressed() { st_set("asset_sel", path) ctx_open("asset", path, u_mx(), u_my()) }
}
}
bind r = r + 1.0
}
if len(rows) < 0.5 { g_text_wrap(x + 12.0, top + 8.0, w - 24.0, 11.0, C_TEXT_DIM, sel(proj_root() == "", "Open a project to browse its files.", "No files match. Import with the + button, or drop files into assets/.")) }
u_unclip()
}
fn asset_activate(path) {
bind e = p_ext(path)
if e == "lscene" || e == "lcut" { st_set("ctx_arg", path) guard_unsaved("scene.open_path") }
else if e == "ling" { script_open(path) st_set("dock_tab", 2.0) st_set("show_dock", 1.0) }
else { asset_add_to_scene(path, false) }
}
# make a node for a file: models, sounds, MIDI, images (2-D scenes)
fn asset_add_to_scene(path, at_cursor) {
bind e = p_ext(path)
bind k = assets_kind(e)
bind rel = proj_rel(path)
bind name = p_stem(path)
if k == "model" {
bind i = add_node_quiet("model", name, at_cursor)
nd_set(i, "src", rel)
} else if k == "audio" {
bind i = add_node_quiet("audio", name, at_cursor)
nd_set(i, "src", rel)
} else if k == "midi" {
bind i = add_node_quiet("midi", name, at_cursor)
nd_set(i, "src", rel)
} else if k == "image" {
if sc_get("kind", "3d") != "2d" { status("Images go into 2D scenes (menus / HUDs). Open or create one first.") return 0.0 }
bind i = add_node_quiet("image", name, at_cursor)
nd_set(i, "src", rel)
if at_cursor { nd_set(i, "px", st_get("drop_cx", 40.0)) nd_set(i, "py", st_get("drop_cy", 40.0)) }
} else if k == "script" {
bind sidx = sel_index()
if sidx >= 0.0 { undo_push() nd_set(sidx, "scr", p_stem(path)) status("Attached script " + p_stem(path) + " to " + nd_get(sidx, "name")) }
else { status("Select a node, then drop the script on it (or double-click to edit)") }
return 0.0
} else if k == "scene" { st_set("ctx_arg", path) guard_unsaved("scene.open_path") return 0.0 }
else { status("Nothing to add for ." + e) return 0.0 }
status("Added " + name)
return 0.0
}
# add_node without a file picker, placed at the last viewport hit when asked
fn add_node_quiet(kind, name, at_cursor) {
undo_push()
bind i = nd_add(kind, name, 0.0)
if !nd_is_2d(kind) {
if at_cursor && st_get("hit_ok", 0.0) > 0.5 {
nd_set(i, "px", st_get("hit_x", 0.0))
nd_set(i, "py", st_get("hit_y", 0.0) - sel(kind == "model", 0.0, nd_get(i, "sy")))
nd_set(i, "pz", st_get("hit_z", 0.0))
} else {
nd_set(i, "px", st_get("ed_tx", 0.0))
nd_set(i, "py", 0.0 - nd_get(i, "sy"))
nd_set(i, "pz", st_get("ed_tz", 0.0))
}
if kind == "audio" || kind == "midi" { nd_set(i, "py", nd_get(i, "py") - 1.0) }
}
sel_set(nd_id(i))
return i
}
# copy a file into the project's assets/<kind>/
fn asset_import(path) {
if proj_root() == "" { status("Open a project first") return 0.0 }
bind e = p_ext(path)
bind k = assets_kind(e)
bind sub = "assets/other"
if k == "model" { bind sub = "assets/models" }
else if k == "image" { bind sub = "assets/images" }
else if k == "audio" { bind sub = "assets/audio" }
else if k == "midi" { bind sub = "assets/midi" }
else if k == "font" { bind sub = "assets/fonts" }
else if k == "script" { bind sub = "scripts" }
else if k == "scene" { bind sub = "scenes" }
plat_mkdir(p_join(proj_root(), sub))
bind dst = p_join(p_join(proj_root(), sub), p_base(path))
if plat_copy(path, dst) { status("Imported " + p_base(path) + " into " + sub + sel(e == "blend", " (run `ling convert` or export glb from Blender to draw it)", "")) }
else { status("Could not copy " + path) }
assets_mark_stale()
return 0.0
}