Skip to content

例程:弹跳小球(2D 物理沙盒) ​

对标版本:渲染库 v1.3.0、XTC v1.0-rc.3。

实机演示 ​

源代码 ​

xuantie
// 由玄铁铸造厂官方内置AI(DeepSeek-Flash)开发。
// 渲染示例.xt —— 玄铁渲染库(raylib 6.0)直用演示:弹跳小球
// 编译: xtc 铁 渲染示例.xt     运行: 渲染示例.exe
// 操作: 左键=加球   右键=清空   滚轮=调重力   ESC/关闭按钮=退出

引 "渲染" 予 R

设 宽 = 920
设 高 = 620

R.初始化窗口(宽, 高, "玄铁渲染库 Demo — 弹跳小球")
R.设窗口可调整(真)
// 中文必须先加载字体(绘文字 走内置位图字体,只有 ASCII)
设 字体 = R.加载系统字体("微软雅黑", 20)

// 造一只球:位置 / 速度 / 半径 / 颜色([r,g,b] 列表)
函 造球(x, y) {
    设 vx = R.取随机数(-7, 7)
    若 vx == 0 { vx = 4 }
    返 {
        "x": x, "y": y,
        "vx": vx, "vy": R.取随机数(-10, -2),
        "r": R.取随机数(9, 24),
        "色": [R.取随机数(90, 255), R.取随机数(90, 255), R.取随机数(90, 255)]
    }
}

设 球集 = []
设 序 = 0
当 序 < 9 {
    球集.追加(造球(R.取随机数(60, 宽 - 60), R.取随机数(60, 高 / 2)))
    序 = 序 + 1
}

设 重力 = 1

当 非 R.窗口应关闭?() {
    设 屏宽 = R.取屏幕宽()
    设 屏高 = R.取屏幕高()
    设 鼠x = R.鼠X()
    设 鼠y = R.鼠Y()

    // ── 交互 ──
    若 R.鼠标刚按下?(R.鼠左键) { 球集.追加(造球(鼠x, 鼠y)) }
    若 R.鼠标刚按下?(R.鼠右键) { 球集 = [] }
    设 轮 = R.取鼠标滚轮()
    若 轮 != 0 {
        重力 = 重力 - 轮
        若 重力 < 0 { 重力 = 0 }
        若 重力 > 8 { 重力 = 8 }
    }

    R.开始绘图()
    R.清背景(16, 18, 28, 255)

    // 网格底纹
    设 gx = 0
    当 gx <= 屏宽 {
        R.绘直线(gx, 0, gx, 屏高, 255, 255, 255, 12)
        gx = gx + 60
    }
    设 gy = 0
    当 gy <= 屏高 {
        R.绘直线(0, gy, 屏宽, gy, 255, 255, 255, 12)
        gy = gy + 60
    }

    // ── 物理 + 绘制 ──
    遍历 球 于 球集 {
        设 r = 球["r"]
        球["vy"] = 球["vy"] + 重力
        球["x"] = 球["x"] + 球["vx"]
        球["y"] = 球["y"] + 球["vy"]

        // 撞墙反弹(竖直方向带阻尼)
        若 球["x"] - r < 0 { 球["x"] = r ; 球["vx"] = -球["vx"] }
        若 球["x"] + r > 屏宽 { 球["x"] = 屏宽 - r ; 球["vx"] = -球["vx"] }
        若 球["y"] - r < 0 { 球["y"] = r ; 球["vy"] = -球["vy"] }
        若 球["y"] + r > 屏高 {
            球["y"] = 屏高 - r
            球["vy"] = -球["vy"] * 9 / 10
            若 球["vy"] == 0 { 球["vy"] = -重力 - 3 }
        }

        设 c = 球["色"]
        R.绘圆(球["x"], 球["y"], r + 5, c[0], c[1], c[2], 36)   // 外发光
        R.绘圆(球["x"], 球["y"], r, c[0], c[1], c[2], 255)
        R.绘圆线(球["x"], 球["y"], r, 255, 255, 255, 70)
    }

    // 鼠标准星
    R.绘圆线(鼠x, 鼠y, 16, 255, 220, 90, 170)
    R.绘直线(鼠x - 26, 鼠y, 鼠x - 6, 鼠y, 255, 220, 90, 170)
    R.绘直线(鼠x + 6, 鼠y, 鼠x + 26, 鼠y, 255, 220, 90, 170)

    // ── HUD ──
    R.绘圆角矩形(14, 12, 620, 44, 45, 10, 12, 20, 190)
    R.绘字体文字(字体, "球数 " & 字(球集.长度) & "    重力 " & 字(重力) & "    左键加球 · 右键清空 · 滚轮调重力",
        28, 24, 20, 1, 225, 232, 245, 255)
    R.绘帧率(屏宽 - 110, 24)

    R.结束绘图()
}

R.释字体(字体)
R.关闭窗口()
示("渲染示例 正常退出")

一、它是什么 ​

一个物理沙盒:满屏小球受重力下落、撞墙反弹,落地带阻尼。可以随手加球、清空、调重力,用来看渲染与物理的手感。

操作键 / 鼠标
加一只球(在鼠标位置)鼠标左键
清空所有球鼠标右键
调重力(滚轮上 = 减小,下 = 增大)鼠标滚轮,范围 0..8
退出ESC / 关闭窗口

初始 9 只球,每只的颜色、半径、初始速度都随机。

二、编译与运行 ​

bash
# 在源码所在目录(它按相对路径的裸包名引库,需要铁铺里已装渲染包,或自带 lib/渲染)
xtc 铁 渲染示例.xt -sc 渲染示例.exe
./渲染示例.exe

第一行是 引 "渲染" 予 R——裸包名,由铁铺解析。想让它跟仓库自带的 lib/渲染 一起走,就改成 引 "./lib/渲染/渲染.xt" 予 R(相对源文件路径),或把渲染包装进铁铺。

三、状态设计与"球"的表示 ​

xuantie
// 造一只球:位置 / 速度 / 半径 / 颜色([r,g,b] 列表)
函 造球(x, y) {
    设 vx = R.取随机数(-7, 7)
    若 vx == 0 { vx = 4 }                                  // 避免纯垂直下落
    返 {
        "x": x, "y": y,
        "vx": vx, "vy": R.取随机数(-10, -2),               // 初始向上
        "r": R.取随机数(9, 24),
        "色": [R.取随机数(90, 255), R.取随机数(90, 255), R.取随机数(90, 255)]
    }
}

设 球集 = []
设 序 = 0
当 序 < 9 {
    球集.追加(造球(R.取随机数(60, 宽 - 60), R.取随机数(60, 高 / 2)))
    序 = 序 + 1
}

一只球就是一个字典:几何(x/y/r)、运动(vx/vy)、外观(色)。颜色用 [r,g,b] 列表存而不是存十六进制整数——绘制时直接 c[0], c[1], c[2] 三个参数摊开,比位运算拆色可读得多(每个球多占 3 个对象,对这种量级完全无所谓)。

四、物理:一帧四步 ​

xuantie
遍历 球 于 球集 {
    设 r = 球["r"]
    球["vy"] = 球["vy"] + 重力          // 1. 加速度
    球["x"]  = 球["x"] + 球["vx"]        // 2. 位置积分
    球["y"]  = 球["y"] + 球["vy"]

    // 3. 碰撞:先摆回界内,再反向速度(否则会"陷进墙里"来回抖)
    若 球["x"] - r < 0 { 球["x"] = r ; 球["vx"] = -球["vx"] }
    若 球["x"] + r > 屏宽 { 球["x"] = 屏宽 - r ; 球["vx"] = -球["vx"] }
    若 球["y"] - r < 0 { 球["y"] = r ; 球["vy"] = -球["vy"] }
    若 球["y"] + r > 屏高 {
        球["y"] = 屏高 - r
        球["vy"] = -球["vy"] * 9 / 10                    // 落地阻尼
        若 球["vy"] == 0 { 球["vy"] = -重力 - 3 }         // 4. 防"躺平"
    }
    ... 绘制 ...
}

四个要点:

  1. 顺序:先加速度、再积分、最后碰撞。顺序错了会出现"穿墙"或"越弹越高"。
  2. 碰撞要做位置修正:球["x"] = r 把它摆回刚好贴着墙的位置,只把速度取反。不修正的话球会短暂陷在墙里,下一帧继续反向 → 高频抖动。
  3. 落地阻尼:* 9 / 10。整数除法在这里是"先乘后除",只要 vy 非零就不会被截断成 0;真的变成 0 时用 -重力 - 3 补一个最小弹跳,避免球永远躺在地上不动。
  4. 这个循环是"物理 + 绘制"同帧:简单、够用。要做固定时间步长(不同帧率下物理一致),把积分部分抽出来、按累积时间多次调用即可(思路同 贪吃蛇)。

五、交互:鼠标与滚轮 ​

xuantie
设 屏宽 = R.取屏幕宽()      // 每帧重取 → 支持拖拽改变窗口大小
设 屏高 = R.取屏幕高()
设 鼠x = R.鼠X()
设 鼠y = R.鼠Y()

若 R.鼠标刚按下?(R.鼠左键) { 球集.追加(造球(鼠x, 鼠y)) }
若 R.鼠标刚按下?(R.鼠右键) { 球集 = [] }              // 清空:直接换一个空数组
设 轮 = R.取鼠标滚轮()                                 // 每帧只读一次,否则会累加
若 轮 != 0 {
    重力 = 重力 - 轮
    若 重力 < 0 { 重力 = 0 }
    若 重力 > 8 { 重力 = 8 }
}
  • 鼠标刚按下? 是边沿触发(这一帧刚按下),鼠标按下? 是电平触发(按住期间每帧都真)。加球用前者,不然按住左键会疯狂生成。
  • 取鼠标滚轮() 必须一帧只调一次:它返回自上次调用以来的滚动量,调两次第二次就是 0(或漏掉事件)。
  • 屏宽/屏高 每帧重取 + R.设窗口可调整(真)(见下节),窗口一拖大,物理边界立刻跟着变——不需要处理"窗口尺寸变化事件"。
  • 清空用 球集 = [](换新数组),比循环 删 更省事,旧数组由 ARC 回收。

六、绘制:层次感靠"叠圆" ​

xuantie
设 c = 球["色"]
R.绘圆(球["x"], 球["y"], r + 5, c[0], c[1], c[2], 36)     // 外发光(大一圈、低 alpha)
R.绘圆(球["x"], 球["y"], r,     c[0], c[1], c[2], 255)    // 本体
R.绘圆线(球["x"], 球["y"], r,   255, 255, 255, 70)        // 白色轮廓

三笔就是一个"发光球":半径 +5、alpha 36 的同心圆当光晕,不用任何着色器/贴图。同色系加一层低 alpha 大圆是 2D 里最便宜的高级感来源。

其余绘制:

  • 网格底纹:横向与纵向各一条 当 循环,每 60 像素画一条 alpha 12 的白色直线——给空旷背景一点空间感。
  • 鼠标准星:一个圆线 + 左右两段短线组成的十字,随鼠标移动;有它才能看清"左键会在哪加球"。
  • HUD 面板:
xuantie
R.绘圆角矩形(14, 12, 620, 44, 45, 10, 12, 20, 190)        // 圆角 10,深色半透明底
R.绘字体文字(字体, "球数 " & 字(球集.长度) & "    重力 " & 字(重力) & "    左键加球 · 右键清空 · 滚轮调重力",
    28, 24, 20, 1, 225, 232, 245, 255)
R.绘帧率(屏宽 - 110, 24)                                   // 右上角帧率(开发期必备)

绘帧率(x, y) 是库提供的现成帧率显示,调机器性能时第一件事就挂上它。

操作提示要写在界面上(这段 HUD 已经写了):鼠标交互的游戏没有"说明书",用户不会去翻文档。

基于 VitePress 静态构建 · 文档内容与仓库 GUIDE/ 同源

玄铁玄铁 XUANTIE · 中文系统级编程语言MARK 玄·折 120×120 · STROKE 8 · NO CURVES