例程:弹跳小球(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. 防"躺平"
}
... 绘制 ...
}四个要点:
- 顺序:先加速度、再积分、最后碰撞。顺序错了会出现"穿墙"或"越弹越高"。
- 碰撞要做位置修正:
球["x"] = r把它摆回刚好贴着墙的位置,只把速度取反。不修正的话球会短暂陷在墙里,下一帧继续反向 → 高频抖动。 - 落地阻尼:
* 9 / 10。整数除法在这里是"先乘后除",只要vy非零就不会被截断成 0;真的变成 0 时用-重力 - 3补一个最小弹跳,避免球永远躺在地上不动。 - 这个循环是"物理 + 绘制"同帧:简单、够用。要做固定时间步长(不同帧率下物理一致),把积分部分抽出来、按累积时间多次调用即可(思路同 贪吃蛇)。
五、交互:鼠标与滚轮
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 已经写了):鼠标交互的游戏没有"说明书",用户不会去翻文档。