Add wire physics

It's only half working. Each side that is anchored needs to be iterated
out from, and the iteration should stop when the next node(s) will be
constrained from both sides.
master
Louis Pearson 2022-01-17 20:19:38 -07:00
parent 1e70cebc3f
commit 18d3243221
1 changed files with 125 additions and 32 deletions

View File

@ -73,7 +73,7 @@ const Sprite = struct { offset: Vec2f = Vec2f{ 0, 0 }, size: w4.Vec2, index: usi
const StaticAnim = Anim;
const ControlAnim = struct { anims: []AnimData, state: Anim };
const Kinematic = struct { col: AABB, move: Vec2f = Vec2f{ 0, 0 }, lastCol: Vec2f = Vec2f{ 0, 0 } };
const Wire = struct { end: Vec2f, grabbed: ?enum { begin, end } = null };
const Wire = struct { nodes: std.BoundedArray(Pos, 10), anchored: [2]?enum { stationary, grabbed } = null };
const Physics = struct { gravity: Vec2f, friction: Vec2f };
const Component = struct {
pos: Pos,
@ -113,6 +113,11 @@ const playerAnim = pac: {
break :pac animArr.slice();
};
fn showErr(msg: []const u8) noreturn {
w4.trace("{s}", .{msg});
unreachable;
}
export fn start() void {
_ = world.create(.{
.pos = Pos.init(Vec2f{ 100, 80 }),
@ -124,19 +129,23 @@ export fn start() void {
.state = Anim{ .anim = &.{} },
},
.kinematic = .{ .col = .{ .pos = .{ -3, -6 }, .size = .{ 5, 5 } } },
}) catch unreachable;
}) catch showErr("Creating player");
for (assets.wire) |wire| {
const begin = Vec2f{ @intToFloat(f32, wire[0][0]), @intToFloat(f32, wire[0][1]) };
const end = Vec2f{ @intToFloat(f32, wire[1][0]), @intToFloat(f32, wire[1][1]) };
const w = Wire{ .end = end };
const size = end - begin;
var nodes = std.BoundedArray(Pos, 10).init(0) catch showErr("Nodes");
var i: usize = 0;
while (i <= 5) : (i += 1) {
const pos = begin + @splat(2, @intToFloat(f32, i)) * size / @splat(2, @as(f32, 5));
nodes.append(Pos.init(pos)) catch showErr("Appending nodes");
}
const w = Wire{ .nodes = nodes, .anchored = .{ .stationary, .stationary } };
_ = world.create(.{
.pos = Pos.init(begin),
.wire = w,
}) catch {
w4.trace("problem", .{});
unreachable;
};
}) catch showErr("Adding wire entity");
}
}
@ -146,6 +155,7 @@ export fn update() void {
world.process(1, &.{.pos}, velocityProcess);
world.process(1, &.{ .pos, .physics }, physicsProcess);
world.process(1, &.{.wire}, wirePhysicsProcess);
world.process(1, &.{ .pos, .control, .physics, .kinematic }, controlProcess);
world.process(1, &.{ .pos, .kinematic }, kinematicProcess);
world.process(1, &.{ .sprite, .staticAnim }, staticAnimProcess);
@ -166,7 +176,7 @@ export fn update() void {
}
}
world.process(1, &.{ .pos, .wire }, wireProcess);
world.process(1, &.{.wire}, wireDrawProcess);
input.update();
}
@ -177,35 +187,118 @@ fn distance(a: w4.Vec2, b: w4.Vec2) i32 {
return @reduce(.Max, subbed);
}
var mouseLast = false;
fn distancef(a: Vec2f, b: Vec2f) f32 {
var subbed = @fabs(a - b);
return @reduce(.Max, subbed);
}
fn wireProcess(_: f32, pos: *Pos, wire: *Wire) void {
const begin = vec2ftovec2(pos.pos);
const end = vec2ftovec2(wire.end);
fn is_solid(pos: Vec2) bool {
if (get_tile(pos[0], pos[1])) |tile| {
return tile != 1;
}
return true;
}
// -- Returns distance to map cell
// function tilemap_distance_to(a, m)
// local dx, dy = 0, 0
// local mx, my, mw, mh = m.x * 8, m.y * 8, 8, 8
// if a.x < mx then dx = mx - (a.x + a.w)
// elseif a.x > mx then dx = a.x - (mx + mw)
// end
// if a.y < my then dy = my - (a.y + a.h)
// elseif a.y > my then dy = a.y - (my + mh)
// end
// return dx, dy
// end
fn length(vec: Vec2f) f32 {
var squared = vec * vec;
return @sqrt(@reduce(.Add, squared));
}
fn normalize(vec: Vec2f) Vec2f {
return vec / @splat(2, length(vec));
}
fn wirePhysicsProcess(dt: f32, wire: *Wire) void {
var nodes = wire.nodes.slice();
if (nodes.len == 0) return;
for (nodes) |*node, i| {
var physics = Physics{ .gravity = Vec2f{ 0, 0.25 }, .friction = Vec2f{ 0.05, 0.05 } };
velocityProcess(dt, node);
physicsProcess(dt, node, &physics);
const tileSize = Vec2{ 8, 8 };
const tileSizef = vec2tovec2f(tileSize);
const iPos = vec2ftovec2(node.pos);
const mapPos = @divTrunc(iPos, tileSize);
if (is_solid(mapPos)) {
// w4.DRAW_COLORS.* = 0x0011;
// w4.rect(mapPos * tileSize, tileSize);
const velNorm = normalize(node.pos - node.last);
var collideVec = node.last;
while (!is_solid(vec2ftovec2((collideVec + velNorm) / tileSizef))) {
collideVec += velNorm;
}
node.pos = collideVec;
}
if (i > 0) {
var nodeBefore = nodes[i - 1];
var diff = nodeBefore.pos - node.pos;
var dist = distancef(node.pos, nodeBefore.pos);
var difference: f32 = 0;
if (dist > 0) {
difference = (8 - dist) / dist;
}
var translate = diff * @splat(2, 0.5 * difference);
nodeBefore.pos += translate;
node.pos -= translate;
}
if (i == 0 and wire.anchored[0] != null) {
node.pos = node.last;
}
// if (i == nodes.len - 1 and wire.anchored[1] != null) {
// node.pos = node.last;
// }
}
}
fn wireDrawProcess(_: f32, wire: *Wire) void {
var nodes = wire.nodes.slice();
if (nodes.len == 0) return;
w4.DRAW_COLORS.* = 0x0001;
w4.line(begin, end);
for (nodes) |node, i| {
if (i == 0) continue;
w4.line(vec2ftovec2(nodes[i - 1].pos), vec2ftovec2(node.pos));
}
w4.DRAW_COLORS.* = 0x0031;
const drawdistance = 16;
const clickdistance = 3;
// const begin = vec2ftovec2(nodes[0].pos);
// const end = vec2ftovec2(nodes[nodes.len - 1].pos);
// const drawdistance = 16;
// const clickdistance = 3;
if (wire.grabbed) |whichEnd| {
switch (whichEnd) {
.begin => pos.pos = vec2tovec2f(w4.MOUSE.pos()),
.end => wire.end = vec2tovec2f(w4.MOUSE.pos()),
}
if (w4.MOUSE.buttons.left and !mouseLast) wire.grabbed = null;
} else {
if (distance(begin, w4.MOUSE.pos()) < drawdistance) {
w4.oval(begin - w4.Vec2{ 2, 2 }, w4.Vec2{ 5, 5 });
if (distance(begin, w4.MOUSE.pos()) < clickdistance and w4.MOUSE.buttons.left and !mouseLast) wire.grabbed = .begin;
}
if (distance(end, w4.MOUSE.pos()) < drawdistance) {
w4.oval(end - w4.Vec2{ 2, 2 }, w4.Vec2{ 5, 5 });
if (distance(end, w4.MOUSE.pos()) < clickdistance and w4.MOUSE.buttons.left and !mouseLast) wire.grabbed = .end;
}
}
// if (wire.grabbed) |whichEnd| {
// switch (whichEnd) {
// .begin => pos.pos = vec2tovec2f(w4.MOUSE.pos()),
// .end => wire.end = vec2tovec2f(w4.MOUSE.pos()),
// }
// if (w4.MOUSE.buttons.left and !mouseLast) wire.grabbed = null;
// } else {
// if (distance(begin, w4.MOUSE.pos()) < drawdistance) {
// w4.oval(begin - w4.Vec2{ 2, 2 }, w4.Vec2{ 5, 5 });
// if (distance(begin, w4.MOUSE.pos()) < clickdistance and w4.MOUSE.buttons.left and !mouseLast) wire.grabbed = .begin;
// }
// if (distance(end, w4.MOUSE.pos()) < drawdistance) {
// w4.oval(end - w4.Vec2{ 2, 2 }, w4.Vec2{ 5, 5 });
// if (distance(end, w4.MOUSE.pos()) < clickdistance and w4.MOUSE.buttons.left and !mouseLast) wire.grabbed = .end;
// }
// }
}
fn vec2tovec2f(vec2: w4.Vec2) Vec2f {