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/gen.ling
# gen.ling - writes a project's runnable game: main.ling (player + scenes + scripts), Ling.toml,
# run / build scripts, a LingOS kernel manifest. `ling run main.ling` plays it; `ling build .` ships it.
fn gen_scripts() {
bind out = []
bind dir = p_join(proj_root(), "scripts")
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))
if !isd && p_ext(nm) == "ling" { bind out = list_push(out, p_stem(nm)) }
bind i = i + 1.0
}
return out
}
fn gen_hooks() { return ["ready", "update", "draw", "input", "trigger", "hit", "net"] }
fn gen_hook_args(h) {
if h == "ready" { return "(i)" }
if h == "update" { return "(i, dt)" }
if h == "draw" { return "(i)" }
if h == "input" { return "(i, arg)" }
if h == "trigger" { return "(i, arg)" }
if h == "hit" { return "(i, arg)" }
return "(i, arg)"
}
# main.ling for a platform layer ("desktop" or "os")
fn gen_main_text(plat) {
bind s = "# Generated by LingDev for project \"" + st_get("proj_name", "") + "\". Edit scenes/ and scripts/, then rebuild.\n"
bind s = s + "# Run: ling run main.ling Build: ling build . --platform win|lin|mac|web|rpi|lingos\n\n"
bind s = s + "use \"rt/plat_" + plat + ".ling\"\n"
bind files = runtime_files()
bind i = 0.0
while i < len(files) {
bind s = s + "use \"rt/" + p_base(list_get(files, i)) + "\"\n"
bind i = i + 1.0
}
bind scripts = gen_scripts()
bind i = 0.0
while i < len(scripts) {
bind s = s + "use \"scripts/" + list_get(scripts, i) + ".ling\"\n"
bind i = i + 1.0
}
bind s = s + "\nfn user_scripts() { return ["
bind i = 0.0
while i < len(scripts) {
if i > 0.0 { bind s = s + ", " }
bind s = s + "\"" + list_get(scripts, i) + "\""
bind i = i + 1.0
}
bind s = s + "] }\n\n"
# dispatch: only hooks the script file defines
bind s = s + "fn user_script(name, hook, i, dt, arg) {\n"
bind hooks = gen_hooks()
bind i = 0.0
while i < len(scripts) {
bind sn = list_get(scripts, i)
bind src = plat_read(p_join(proj_root(), "scripts/" + sn + ".ling"))
bind s = s + " if name == \"" + sn + "\" {\n"
bind h = 0.0
while h < len(hooks) {
bind hk = list_get(hooks, h)
if str_find(src, "fn " + sn + "_" + hk + "(") >= 0.0 {
bind call = sn + "_" + hk + sel(hk == "update", "(i, dt)", sel(hk == "ready" || hk == "draw", "(i)", "(i, arg)"))
bind s = s + " if hook == \"" + hk + "\" { " + call + " return 1.0 }\n"
}
bind h = h + 1.0
}
bind s = s + " return 0.0\n }\n"
bind i = i + 1.0
}
bind s = s + " return 0.0\n}\n\n"
bind start = st_get("proj_start", "")
if start == "" && st_llen("proj_scenes") > 0.5 { bind start = st_lget("proj_scenes", 0.0) }
bind s = s + "bind start = do {\n"
bind s = s + " game_boot(\"" + start + "\", \"" + str_replace(st_get("proj_title", st_get("proj_name", "Game")), "\"", "'") + "\", " + num_str(st_get("proj_w", 1280.0)) + ".0, " + num_str(st_get("proj_h", 720.0)) + ".0)\n"
bind s = s + " while window_is_open() && game_quit() < 0.5 {\n game_frame()\n }\n game_shutdown()\n}\n"
return s
}
fn gen_ling_toml() {
bind name = str_lower(str_replace(st_get("proj_name", "game"), " ", "-"))
bind s = "# Ling.toml - read by `ling run .` / `ling build .` (line-parsed) and by lingfu (TOML).\n"
bind s = s + "name = \"" + name + "\"\nversion = \"" + st_get("proj_version", "0.1.0") + "\"\nentry = \"main.ling\"\nkind = \"game\"\n"
if st_get("proj_icon", "") != "" { bind s = s + "icon = \"" + st_get("proj_icon", "") + "\"\n" }
bind s = s + "includes = [\"scenes/*.lscene\", \"scenes/*.lcut\", \"assets/**\", \"rt/*.ling\", \"scripts/*.ling\"]\n\n"
bind s = s + "[package]\nname = \"" + name + "\"\nversion = \"" + st_get("proj_version", "0.1.0") + "\"\ndescription = \"" + str_replace(st_get("proj_title", ""), "\"", "'") + " - made with LingDev\"\nauthors = [\"" + st_get("proj_author", "") + "\"]\nkeywords = [\"game\", \"lingdev\"]\n"
return s
}
fn gen_kernel_toml() {
bind name = str_lower(str_replace(st_get("proj_name", "game"), " ", "-"))
bind s = "# LingOS kernel target: this game as the Pi desktop's built-in app.\n"
bind s = s + "# Copy this folder to ../LingOS/kernel/rpi-" + name + "/ and run:\n"
bind s = s + "# ling build kernel/rpi-" + name + " --platform rpi --out dist\n"
bind s = s + "[project]\nname = \"lingos-" + name + "-rpi\"\nversion = \"" + st_get("proj_version", "0.1.0") + "\"\nentry = \"../../apps/lingdesk/main.ling\"\nkind = \"kernel\"\n"
bind s = s + "bundle_app = \"" + name + ":<path-to-project>/main_os.ling\"\nembed_root = \"<path-to-project>\"\n"
bind s = s + "includes = [\"assets/fonts/ui.ttf\", \"scenes/*.lscene\", \"assets/images/*.png\"]\n"
return s
}
fn gen_write_all() {
if proj_root() == "" { return 0.0 }
proj_install_runtime(proj_root())
plat_write(p_join(proj_root(), "main.ling"), gen_main_text("desktop"))
plat_write(p_join(proj_root(), "main_os.ling"), gen_main_text("os"))
plat_write(p_join(proj_root(), "Ling.toml"), gen_ling_toml())
plat_mkdir(p_join(proj_root(), "lingos"))
plat_write(p_join(proj_root(), "lingos/Ling.toml"), gen_kernel_toml())
plat_write(p_join(proj_root(), "run.bat"), gen_run_bat())
plat_write(p_join(proj_root(), "run.sh"), gen_run_sh())
plat_write(p_join(proj_root(), "BUILD.md"), gen_build_md())
return 0.0
}
fn ling_exe_guess() {
bind s = st_get("ling_exe", "")
if s != "" { return s }
bind app = st_get("app_root", ".")
bind c1 = p_join(p_dir(app), "ling/target/release/ling.exe")
if file_exists(c1) { return c1 }
return "ling"
}
fn gen_run_bat() {
bind s = "@echo off\r\nrem Runs the game with the ling runtime (interpreter mode, like LingStationX).\r\nsetlocal\r\ncd /d \"%~dp0\"\r\n"
bind s = s + "set LING=" + str_replace(ling_exe_guess(), "/", "\\") + "\r\nif not exist \"%LING%\" set LING=ling\r\n"
bind s = s + "\"%LING%\" run --interp main.ling %*\r\nif errorlevel 1 pause\r\n"
return s
}
fn gen_run_sh() {
return "#!/usr/bin/env sh\n# Runs the game with the ling runtime.\ncd \"$(dirname \"$0\")\"\nLING=${LING:-ling}\n\"$LING\" run --interp main.ling \"$@\"\n"
}
fn build_command(t) {
if t == "rpi" { return "ling build . --platform rpi --out dist" }
if t == "lingos" { return "ling build . --platform lingos --out dist" }
if t == "web" { return "ling build . --platform web --out dist" }
return "ling build . --platform " + t + " --pack --out dist"
}
# writes build_<target>.bat / .sh next to main.ling and returns its path
fn gen_build_script(t) {
bind exe = ling_exe_guess()
bind cmd = build_command(t)
bind bat = p_join(proj_root(), "build_" + t + ".bat")
bind s = "@echo off\r\nrem " + cmd + "\r\nsetlocal\r\ncd /d \"%~dp0\"\r\nset LING=" + str_replace(exe, "/", "\\") + "\r\nif not exist \"%LING%\" set LING=ling\r\n"
if t == "rpi" || t == "lingos" {
bind s = s + "rem The Pi / LingOS targets compile main_os.ling (software renderer, no audio) with the in-kernel runtime.\r\n"
bind s = s + "copy /y main_os.ling main.ling.os.tmp >nul\r\n"
}
bind s = s + "\"%LING%\" " + str_replace(cmd, "ling ", "") + "\r\nif errorlevel 1 (echo Build failed & pause & exit /b 1)\r\necho Build finished: dist\\\r\nexplorer dist\r\n"
plat_write(bat, s)
bind sh = p_join(proj_root(), "build_" + t + ".sh")
plat_write(sh, "#!/usr/bin/env sh\n# " + cmd + "\ncd \"$(dirname \"$0\")\"\nLING=${LING:-ling}\n\"$LING\" " + str_replace(cmd, "ling ", "") + "\n")
return bat
}
fn gen_build_md() {
bind s = "# Building " + st_get("proj_name", "") + "\n\nGenerated by LingDev. All commands run from this folder.\n\n"
bind s = s + "| Target | Command | Result |\n|---|---|---|\n"
bind s = s + "| Run from source | `ling run --interp main.ling` | plays with the installed ling runtime |\n"
bind s = s + "| Windows x86-64 | `ling build . --platform win --pack --out dist` | `dist/windows/<name>.exe`, assets embedded |\n"
bind s = s + "| Linux x86-64 | `ling build . --platform lin --pack --out dist` | `dist/linux/<name>` |\n"
bind s = s + "| macOS | `ling build . --platform mac --pack --out dist` | `dist/macos/<name>` |\n"
bind s = s + "| Web | `ling build . --platform web --out dist` | `dist/web/` (WASM + WebGL2) |\n"
bind s = s + "| Raspberry Pi (bare metal kernel) | `ling build . --platform rpi --out dist` | `dist/rpi/kernel8.img`, boots from an SD card (see ../LingOS/live/build-sdcard-rpi.sh) |\n"
bind s = s + "| LingOS app | `ling build . --platform lingos --out dist` | `dist/apps/<name>.elf` |\n"
bind s = s + "| LingOS desktop (Pi) built-in app | see `lingos/Ling.toml` | the game in the LingOS start menu next to LingStationX |\n\n"
bind s = s + "The Pi / LingOS targets have no native 3-D or audio yet: they use `main_os.ling` (the same scenes and scripts, drawn by the software renderer in rt/rt_sw3d.ling). Pi 5 note: LingOS's aarch64 kernel targets the BCM2837/BCM2711 memory map today; BCM2712 (Pi 5) needs the HAL follow-up described in LingOS/live/build-sdcard-rpi.sh.\n"
return s
}
# a starter script
fn script_template(name) {
if name == "player" {
return "# player.ling - the player actor. The built-in controller already moves an actor tagged `player`\n# (WASD / arrows / gamepad, Space jumps, collides with `solid` boxes). These hooks add behaviour.\n\nfn player_ready(i) {\n game_set(\"score\", 0.0)\n}\n\nfn player_update(i, dt) {\n # fall off the world: respawn\n if nd_get(i, \"py\") > 40.0 { game_respawn(i) }\n}\n\nfn player_trigger(i, other) {\n bind tag = nd_get(other, \"tag\")\n if tag == \"goal\" { game_set(\"score\", game_var(\"score\") + 100.0) }\n}\n\nfn player_input(i, key) {\n if key == \"e\" { game_say(\"Player\", \"Hello from a script hook.\", 2.0) }\n}\n"
}
if name == "rhythm" {
return "# rhythm.ling - a MIDI-driven node: flashes on every note onset (desktop runtime loads the .mid).\n\nfn rhythm_update(i, dt) {\n if game_on_beat(i, game_time()) { nd_set(i, \"col\", 16242758.0) } else { nd_set(i, \"col\", 10114006.0) }\n}\n"
}
return "# " + name + ".ling - attach by typing `" + name + "` in a node's Script field.\n\nfn " + name + "_ready(i) {\n}\n\nfn " + name + "_update(i, dt) {\n # nd_get(i, \"px\") / nd_set(i, \"py\", v) read and write the node; game_* helpers are in rt/rt_game.ling\n}\n"
}