@flare-engine/动力学
2D 刚性体动力学取向外方框(OBBs):分离轴+参考/事件面
剪接会构建1-2个点接触多路,一个相继的冲动解析器用
Coulomb摩擦,还原, 和Baumgarte去渗透. @flare-engine/physics 答题 对
这些重叠· ; @flare-engine/dynamics 答题 他们最终会在哪里。两个软件包都取决于
另一段——按场景选取,或并用(physics 用于触发/重叠查询,
dynamics 身体实际上相互推压.
安装
bun add @flare-engine/dynamics
使用量
import { addBody, createBody, createWorld, step } from '@flare-engine/dynamics';
const world = createWorld({ gravity: { x: 0, y: 1800 } });
// Static floor: invMass 0, never moves.
addBody(
world,
createBody({ position: { x: 0, y: 100 }, half: { x: 500, y: 10 }, static: true }),
);
const box = addBody(
world,
createBody({ position: { x: 0, y: -50 }, half: { x: 10, y: 10 } }),
);
// Fixed-timestep loop.
step(world, 1 / 60);
console.log(box.position.y, box.velocity.y);
Body.position 财务报告和审定财务报表 Body.velocity 是可变的平面对象字段- createBody 分配它们
一次,还有 step() 每分一秒都写在书上 这是一个刻意的API合同,不是
监督:它符合引擎的"零分权"风格,让消费者读取或推翻
步骤之间的直接字段(例如,夹住休息速度-见下文限制)
通过一个接入器.
step() 平稳状态下不分配任何内容——每步结构(接触方块、接触)
积分 宽相刮痕缓冲器) 活在由该 World 并被反复使用,
只有在体数增加时才生长.
记录和重播
半隐蔽的欧拉有摩擦和还原 时间是无法逆转的,所以你想要跌倒
刷新、倒转或以另一种速度重放 烤过一次 改为 a Timeline 并读回:
取样. record 取一个写作员的回调而不是一个身体阵列,所以时间线永远不用知道
它记录了什么 until 它的上游在一个呼叫中烘焙, 没有一个记录一个
框架,适合一个时间预算的循环,每个滴答一次.
stepUntil(world, dt, { budgetMs: 6, done: isSettled }) 是这个循环: 它步入到 done() 返回时
没错, maxSteps 达到或用完预算(至少一次),并报告
{ steps, complete }· ; stepUntilInto 是零分配器形式.
import { createTimeline, createWorld, step } from '@flare-engine/dynamics';
const world = createWorld(); // add a floor and some boxes as above
const dt = 1 / 60;
const timeline = createTimeline(world.bodies.length, dt); // stride 3: x, y, angle
timeline.record(
(buf, base) => {
step(world, dt);
for (let i = 0; i < world.bodies.length; i++) {
const b = world.bodies[i]!;
buf[base + i * 3] = b.position.x;
buf[base + i * 3 + 1] = b.position.y;
buf[base + i * 3 + 2] = b.angle;
}
},
() => world.bodies.every((b) => Math.abs(b.velocity.y) < 1), // your own settle rule
);
const out = new Float32Array(timeline.elementCount * 3);
timeline.sampleInto(0.5, out); // body i at out[i * 3 ..], lerped between frames
存储为 Float32Array ——回放精度为f64的半字节,角度为:
保持原样: Body.angle 是累积的,从不包起来, 所以一个平地的麻鼠是正确的
穿过多回转的转弯,最短路径的麻风病会解开它们. sampleInto 分配数
没什么,上面的刮伤缓冲器为整个回放服务.
睡觉
睡眠默认关闭(参见限制). createWorld({ sleep }) 选择加入:
速度保持在 linearThreshold (px/s)及其角速度保持在 angularThreshold
(弧度/秒)用于 steps 连续步骤的速度被设定为0,被跳过
与其他入睡或静态尸体进行对接试验。 当一个醒了的身体醒来
在前一步上移动的, 当你写它的时候 velocity, (中文(简体) ), angularVelocity, (中文(简体) ),
position 或者说 angle,或者当你打电话 wakeBody。 。 。 . isSleeping(world, body) 读取一个正文
indexOf 结束 world.bodies(三); isAtRest(world) 一旦每个活体都睡了 就会变成
这是停止规则 对于一个 stepUntil 烘焙:
import {
addBody,
createBody,
createWorld,
isAtRest,
isSleeping,
stepUntil,
wakeBody,
} from '@flare-engine/dynamics';
const world = createWorld({
gravity: { x: 0, y: 1800 },
sleep: { linearThreshold: 14, angularThreshold: 1.5, steps: 30 },
});
addBody(
world,
createBody({ position: { x: 0, y: 100 }, half: { x: 500, y: 10 }, static: true }),
);
const box = addBody(world, createBody({ position: { x: 0, y: 0 }, half: { x: 5, y: 5 } }));
const result = stepUntil(world, 1 / 120, {
budgetMs: 8,
maxSteps: 600,
done: () => isAtRest(world),
});
if (result.complete) console.log(isSleeping(world, box), box.velocity.y); // true 0
wakeBody(world, box); // e.g. after removing the floor from world.bodies
阈值必须清除限制下描述的休息紧张,这取决于身体大小和
dt:在包件本身的测试中(半至5个盒, dt = 1/120休息箱峰约5.8px/s
和 1.12 弧度/秒(角峰平面有 1 / 半宽),因此有 14 / 1.5 个入睡在91 步中,
0.6弧度/s 从未看到连续30个安静的台阶. 在设定值前测量您自己的场景 .
睡觉不会做什么:
- 没有岛屿. 一个被唤醒的躯体 唤醒了它触摸到的卧底 下一个 步骤,所以一个着陆箱 每步唤醒一叠一层, 沉睡者只被身体所触动,而身体本身正在安顿 (下入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入入.
- 潜伏者不会跟随被移动的静态身体. 在睡匣子下移动地上
框所在; 调用
wakeBody移动或删除支持后. - 将尸体从
world.bodies唤醒掉入槽中的尸体 没有别的了. 潜伏者,其支撑被移除而未转移 它在中空保持睡眠,直到wakeBody。 。 。 . 在睡前插入一具尸体同样会唤醒它(在睡后重新入睡)steps安静的台阶). - 一个从未经历过的世界
createWorld(散页副本或structuredClone交给step(a) 被收养时不睡觉;isSleeping财务报告和审定财务报表isAtRest任何动态都是假的 躯体.
限制(v1)
- 休息的身体不会完全停止 除非你选择睡觉. 一个盒子安放在
地板到达马厩 职位 ——在包件自身的测试情景中,位置移动小于
0.1个单位超过600个进一步步骤——但
velocity保持小的、有界的振荡(大约2.6个) (在同一情况中)在穿透-斜坡平衡时。 这是天生的 包姆加特偏见相继冲动解析器,不是错误,也不是更大的迭代计数 或调音解想.createWorld({ sleep })(以上) 将身体速度零 停留在您的阈值下; 休眠-高速大坝保持超出范围. - 仅收箱. 没有圆形或多边形形状.
- 无关节,无CCD(连续相撞探测). 快速移动的瘦身能通地道 在一个步骤内互相穿透.
- 没有ECS对接.
World编号 :Body是纯数据;如果使用电子数据,则直接输入ECS. - 宽相是蛮力 (所有AABB拒绝)——小身体计数的罚款,而不是空间 分区。 在经过之后,没有广泛的阶段.
见 API 规格。 。 。 .
许可证
阿帕奇 - 2.0