Wall jumping!
parent
6fc70c1279
commit
1e70cebc3f
Binary file not shown.
Before Width: | Height: | Size: 277 B After Width: | Height: | Size: 283 B |
File diff suppressed because one or more lines are too long
BIN
assets/tiles.png
BIN
assets/tiles.png
Binary file not shown.
Before Width: | Height: | Size: 666 B After Width: | Height: | Size: 648 B |
File diff suppressed because one or more lines are too long
134
src/main.zig
134
src/main.zig
|
@ -57,30 +57,33 @@ const Anim = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
const Pos = Vec2f;
|
const Pos = struct {
|
||||||
/// Stores last position, for velocity
|
pos: Vec2f,
|
||||||
const LastPos = Vec2f;
|
last: Vec2f,
|
||||||
|
pub fn init(pos: Vec2f) @This() {
|
||||||
|
return @This(){ .pos = pos, .last = pos };
|
||||||
|
}
|
||||||
|
};
|
||||||
const Control = struct {
|
const Control = struct {
|
||||||
controller: enum { player },
|
controller: enum { player },
|
||||||
state: enum { stand, walk, jump, fall },
|
state: enum { stand, walk, jump, fall, wallSlide },
|
||||||
facing: enum { left, right } = .right,
|
facing: enum { left, right } = .right,
|
||||||
};
|
};
|
||||||
const Sprite = struct { offset: Vec2f = Vec2f{ 0, 0 }, size: w4.Vec2, index: usize, flags: w4.BlitFlags };
|
const Sprite = struct { offset: Vec2f = Vec2f{ 0, 0 }, size: w4.Vec2, index: usize, flags: w4.BlitFlags };
|
||||||
const StaticAnim = Anim;
|
const StaticAnim = Anim;
|
||||||
const ControlAnim = struct { anims: []AnimData, state: Anim };
|
const ControlAnim = struct { anims: []AnimData, state: Anim };
|
||||||
const Kinematic = struct { col: AABB, onWall: bool = false, onFloor: bool = false, onCeil: bool = false };
|
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 { end: Vec2f, grabbed: ?enum { begin, end } = null };
|
||||||
const Gravity = Vec2f;
|
const Physics = struct { gravity: Vec2f, friction: Vec2f };
|
||||||
const Component = struct {
|
const Component = struct {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
lastpos: LastPos,
|
|
||||||
control: Control,
|
control: Control,
|
||||||
sprite: Sprite,
|
sprite: Sprite,
|
||||||
staticAnim: StaticAnim,
|
staticAnim: StaticAnim,
|
||||||
controlAnim: ControlAnim,
|
controlAnim: ControlAnim,
|
||||||
kinematic: Kinematic,
|
kinematic: Kinematic,
|
||||||
wire: Wire,
|
wire: Wire,
|
||||||
gravity: Gravity,
|
physics: Physics,
|
||||||
};
|
};
|
||||||
const World = ecs.World(Component);
|
const World = ecs.World(Component);
|
||||||
|
|
||||||
|
@ -95,6 +98,7 @@ const anim_store = struct {
|
||||||
const walk = Anim.simple(4, &[_]usize{ 1, 2, 3, 4 });
|
const walk = Anim.simple(4, &[_]usize{ 1, 2, 3, 4 });
|
||||||
const jump = Anim.frame(5);
|
const jump = Anim.frame(5);
|
||||||
const fall = Anim.frame(6);
|
const fall = Anim.frame(6);
|
||||||
|
const wallSlide = Anim.frame(7);
|
||||||
};
|
};
|
||||||
|
|
||||||
const AnimData = []const Anim.Ops;
|
const AnimData = []const Anim.Ops;
|
||||||
|
@ -105,16 +109,16 @@ const playerAnim = pac: {
|
||||||
animArr.append(&anim_store.walk) catch unreachable;
|
animArr.append(&anim_store.walk) catch unreachable;
|
||||||
animArr.append(&anim_store.jump) catch unreachable;
|
animArr.append(&anim_store.jump) catch unreachable;
|
||||||
animArr.append(&anim_store.fall) catch unreachable;
|
animArr.append(&anim_store.fall) catch unreachable;
|
||||||
|
animArr.append(&anim_store.wallSlide) catch unreachable;
|
||||||
break :pac animArr.slice();
|
break :pac animArr.slice();
|
||||||
};
|
};
|
||||||
|
|
||||||
export fn start() void {
|
export fn start() void {
|
||||||
_ = world.create(.{
|
_ = world.create(.{
|
||||||
.pos = .{ 100, 80 },
|
.pos = Pos.init(Vec2f{ 100, 80 }),
|
||||||
.lastpos = .{ 100, 80 },
|
|
||||||
.control = .{ .controller = .player, .state = .stand },
|
.control = .{ .controller = .player, .state = .stand },
|
||||||
.sprite = .{ .offset = .{ -4, -8 }, .size = .{ 8, 8 }, .index = 0, .flags = .{ .bpp = .b1 } },
|
.sprite = .{ .offset = .{ -4, -8 }, .size = .{ 8, 8 }, .index = 0, .flags = .{ .bpp = .b1 } },
|
||||||
.gravity = Vec2f{ 0, 0.25 },
|
.physics = .{ .friction = Vec2f{ 0.05, 0.01 }, .gravity = Vec2f{ 0, 0.25 } },
|
||||||
.controlAnim = ControlAnim{
|
.controlAnim = ControlAnim{
|
||||||
.anims = playerAnim,
|
.anims = playerAnim,
|
||||||
.state = Anim{ .anim = &.{} },
|
.state = Anim{ .anim = &.{} },
|
||||||
|
@ -127,7 +131,7 @@ export fn start() void {
|
||||||
const end = Vec2f{ @intToFloat(f32, wire[1][0]), @intToFloat(f32, wire[1][1]) };
|
const end = Vec2f{ @intToFloat(f32, wire[1][0]), @intToFloat(f32, wire[1][1]) };
|
||||||
const w = Wire{ .end = end };
|
const w = Wire{ .end = end };
|
||||||
_ = world.create(.{
|
_ = world.create(.{
|
||||||
.pos = begin,
|
.pos = Pos.init(begin),
|
||||||
.wire = w,
|
.wire = w,
|
||||||
}) catch {
|
}) catch {
|
||||||
w4.trace("problem", .{});
|
w4.trace("problem", .{});
|
||||||
|
@ -140,10 +144,10 @@ export fn update() void {
|
||||||
w4.DRAW_COLORS.* = 0x0004;
|
w4.DRAW_COLORS.* = 0x0004;
|
||||||
w4.rect(.{ 0, 0 }, .{ 160, 160 });
|
w4.rect(.{ 0, 0 }, .{ 160, 160 });
|
||||||
|
|
||||||
world.process(1, &.{ .pos, .lastpos }, velocityProcess);
|
world.process(1, &.{.pos}, velocityProcess);
|
||||||
world.process(1, &.{ .pos, .gravity }, gravityProcess);
|
world.process(1, &.{ .pos, .physics }, physicsProcess);
|
||||||
world.process(1, &.{ .pos, .control, .gravity, .kinematic }, controlProcess);
|
world.process(1, &.{ .pos, .control, .physics, .kinematic }, controlProcess);
|
||||||
world.process(1, &.{ .pos, .lastpos, .kinematic }, kinematicProcess);
|
world.process(1, &.{ .pos, .kinematic }, kinematicProcess);
|
||||||
world.process(1, &.{ .sprite, .staticAnim }, staticAnimProcess);
|
world.process(1, &.{ .sprite, .staticAnim }, staticAnimProcess);
|
||||||
world.process(1, &.{ .sprite, .controlAnim, .control }, controlAnimProcess);
|
world.process(1, &.{ .sprite, .controlAnim, .control }, controlAnimProcess);
|
||||||
world.process(1, &.{ .pos, .sprite }, drawProcess);
|
world.process(1, &.{ .pos, .sprite }, drawProcess);
|
||||||
|
@ -176,8 +180,8 @@ fn distance(a: w4.Vec2, b: w4.Vec2) i32 {
|
||||||
var mouseLast = false;
|
var mouseLast = false;
|
||||||
|
|
||||||
fn wireProcess(_: f32, pos: *Pos, wire: *Wire) void {
|
fn wireProcess(_: f32, pos: *Pos, wire: *Wire) void {
|
||||||
const begin = w4.Vec2{ @floatToInt(i32, pos.*[0]), @floatToInt(i32, pos.*[1]) };
|
const begin = vec2ftovec2(pos.pos);
|
||||||
const end = w4.Vec2{ @floatToInt(i32, wire.end[0]), @floatToInt(i32, wire.end[1]) };
|
const end = vec2ftovec2(wire.end);
|
||||||
|
|
||||||
w4.DRAW_COLORS.* = 0x0001;
|
w4.DRAW_COLORS.* = 0x0001;
|
||||||
w4.line(begin, end);
|
w4.line(begin, end);
|
||||||
|
@ -188,7 +192,7 @@ fn wireProcess(_: f32, pos: *Pos, wire: *Wire) void {
|
||||||
|
|
||||||
if (wire.grabbed) |whichEnd| {
|
if (wire.grabbed) |whichEnd| {
|
||||||
switch (whichEnd) {
|
switch (whichEnd) {
|
||||||
.begin => pos.* = vec2tovec2f(w4.MOUSE.pos()),
|
.begin => pos.pos = vec2tovec2f(w4.MOUSE.pos()),
|
||||||
.end => wire.end = vec2tovec2f(w4.MOUSE.pos()),
|
.end => wire.end = vec2tovec2f(w4.MOUSE.pos()),
|
||||||
}
|
}
|
||||||
if (w4.MOUSE.buttons.left and !mouseLast) wire.grabbed = null;
|
if (w4.MOUSE.buttons.left and !mouseLast) wire.grabbed = null;
|
||||||
|
@ -209,12 +213,12 @@ fn vec2tovec2f(vec2: w4.Vec2) Vec2f {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vec2ftovec2(vec2f: Vec2f) w4.Vec2 {
|
fn vec2ftovec2(vec2f: Vec2f) w4.Vec2 {
|
||||||
return w4.Vec2{ @floatToInt(i32, vec2f[0]), @intToFloat(i32, vec2f[1]) };
|
return w4.Vec2{ @floatToInt(i32, vec2f[0]), @floatToInt(i32, vec2f[1]) };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drawProcess(_: f32, pos: *Pos, sprite: *Sprite) void {
|
fn drawProcess(_: f32, pos: *Pos, sprite: *Sprite) void {
|
||||||
w4.DRAW_COLORS.* = 0x0010;
|
w4.DRAW_COLORS.* = 0x0010;
|
||||||
const fpos = pos.* + sprite.offset;
|
const fpos = pos.pos + sprite.offset;
|
||||||
const ipos = w4.Vec2{ @floatToInt(i32, fpos[0]), @floatToInt(i32, fpos[1]) };
|
const ipos = w4.Vec2{ @floatToInt(i32, fpos[0]), @floatToInt(i32, fpos[1]) };
|
||||||
const t = w4.Vec2{ @intCast(i32, (sprite.index * 8) % 128), @intCast(i32, (sprite.index * 8) / 128) };
|
const t = w4.Vec2{ @intCast(i32, (sprite.index * 8) % 128), @intCast(i32, (sprite.index * 8) / 128) };
|
||||||
w4.blitSub(&assets.sprites, ipos, sprite.size, t, 128, sprite.flags);
|
w4.blitSub(&assets.sprites, ipos, sprite.size, t, 128, sprite.flags);
|
||||||
|
@ -225,36 +229,47 @@ fn staticAnimProcess(_: f32, sprite: *Sprite, anim: *StaticAnim) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn controlAnimProcess(_: f32, sprite: *Sprite, anim: *ControlAnim, control: *Control) void {
|
fn controlAnimProcess(_: f32, sprite: *Sprite, anim: *ControlAnim, control: *Control) void {
|
||||||
const a: usize = if (control.state == .stand) 0 else 1;
|
const a: usize = switch (control.state) {
|
||||||
|
.stand => 0,
|
||||||
|
.walk => 1,
|
||||||
|
.jump => 2,
|
||||||
|
.fall => 3,
|
||||||
|
.wallSlide => 4,
|
||||||
|
};
|
||||||
sprite.flags.flip_x = (control.facing == .left);
|
sprite.flags.flip_x = (control.facing == .left);
|
||||||
anim.state.play(anim.anims[a]);
|
anim.state.play(anim.anims[a]);
|
||||||
anim.state.update(&sprite.index);
|
anim.state.update(&sprite.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn controlProcess(_: f32, pos: *Pos, control: *Control, gravity: *Gravity, kinematic: *Kinematic) void {
|
const approxEqAbs = std.math.approxEqAbs;
|
||||||
|
|
||||||
|
fn controlProcess(_: f32, pos: *Pos, control: *Control, physics: *Physics, kinematic: *Kinematic) void {
|
||||||
var delta = Vec2f{ 0, 0 };
|
var delta = Vec2f{ 0, 0 };
|
||||||
if (kinematic.onFloor) {
|
if (approxEqAbs(f32, kinematic.move[1], 0, 0.01) and kinematic.lastCol[1] > 0) {
|
||||||
if (input.btnp(.one, .one)) delta[1] -= 23;
|
if (input.btnp(.one, .one)) delta[1] -= 23;
|
||||||
if (input.btn(.one, .left)) delta[0] -= 1;
|
if (input.btn(.one, .left)) delta[0] -= 1;
|
||||||
if (input.btn(.one, .right)) delta[0] += 1;
|
if (input.btn(.one, .right)) delta[0] += 1;
|
||||||
if (delta[0] != 0 or delta[1] != 0) {
|
if (delta[0] != 0 or delta[1] != 0) {
|
||||||
control.state = .walk;
|
control.state = .walk;
|
||||||
if (delta[0] > 0) control.facing = .right;
|
|
||||||
if (delta[0] < 0) control.facing = .left;
|
|
||||||
} else {
|
} else {
|
||||||
control.state = .stand;
|
control.state = .stand;
|
||||||
}
|
}
|
||||||
} else if (kinematic.onWall) {
|
} else if (kinematic.move[1] > 0 and !approxEqAbs(f32, kinematic.lastCol[0], 0, 0.01) and approxEqAbs(f32, kinematic.lastCol[1], 0, 0.01)) {
|
||||||
if (input.btn(.one, .left) and input.btnp(.one, .one)) delta = Vec2f{ -1, -23 };
|
// w4.trace("{}, {}", .{ kinematic.move, kinematic.lastCol });
|
||||||
if (input.btn(.one, .right) and input.btnp(.one, .one)) delta = Vec2f{ 1, -23 };
|
if (kinematic.lastCol[0] > 0 and input.btnp(.one, .one)) delta = Vec2f{ -10, -15 };
|
||||||
gravity.* = Vec2f{ 0, 0.01 };
|
if (kinematic.lastCol[0] < 0 and input.btnp(.one, .one)) delta = Vec2f{ 10, -15 };
|
||||||
|
physics.gravity = Vec2f{ 0, 0.05 };
|
||||||
|
control.state = .wallSlide;
|
||||||
} else {
|
} else {
|
||||||
if (w4.GAMEPAD1.button_left) delta[0] -= 1;
|
if (input.btn(.one, .left)) delta[0] -= 1;
|
||||||
if (w4.GAMEPAD1.button_right) delta[0] += 1;
|
if (input.btn(.one, .right)) delta[0] += 1;
|
||||||
gravity.* = Vec2f{ 0, 0.25 };
|
physics.gravity = Vec2f{ 0, 0.25 };
|
||||||
|
if (kinematic.move[1] < 0) control.state = .jump else control.state = .fall;
|
||||||
}
|
}
|
||||||
|
if (delta[0] > 0) control.facing = .right;
|
||||||
|
if (delta[0] < 0) control.facing = .left;
|
||||||
var move = delta * @splat(2, @as(f32, 0.2));
|
var move = delta * @splat(2, @as(f32, 0.2));
|
||||||
pos.* += move;
|
pos.pos += move;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// pos should be in tile coordinates, not world coordinates
|
/// pos should be in tile coordinates, not world coordinates
|
||||||
|
@ -291,43 +306,50 @@ fn level_collide(rect: AABB) std.BoundedArray(AABB, 9) {
|
||||||
return collisions;
|
return collisions;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kinematicProcess(_: f32, pos: *Pos, lastpos: *LastPos, kinematic: *Kinematic) void {
|
fn kinematicProcess(_: f32, pos: *Pos, kinematic: *Kinematic) void {
|
||||||
var next = lastpos.*;
|
var next = pos.last;
|
||||||
next[0] = pos.*[0];
|
next[0] = pos.pos[0];
|
||||||
var hcol = level_collide(kinematic.col.addv(next));
|
var hcol = level_collide(kinematic.col.addv(next));
|
||||||
if (hcol.len > 0) {
|
if (hcol.len > 0) {
|
||||||
kinematic.onWall = true;
|
kinematic.lastCol[0] = next[0] - pos.last[0];
|
||||||
next[0] = lastpos.*[0];
|
next[0] = pos.last[0];
|
||||||
} else {
|
} else if (!approxEqAbs(f32, next[0] - pos.last[0], 0, 0.01)) {
|
||||||
kinematic.onWall = false;
|
kinematic.lastCol[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
next[1] = pos.*[1];
|
next[1] = pos.pos[1];
|
||||||
var vcol = level_collide(kinematic.col.addv(next));
|
var vcol = level_collide(kinematic.col.addv(next));
|
||||||
if (vcol.len > 0) {
|
if (vcol.len > 0) {
|
||||||
if (next[1] > lastpos.*[1]) kinematic.onFloor = true;
|
kinematic.lastCol[1] = next[1] - pos.last[1];
|
||||||
if (next[1] < lastpos.*[1]) kinematic.onCeil = true;
|
next[1] = pos.last[1];
|
||||||
next[1] = lastpos.*[1];
|
} else if (!approxEqAbs(f32, next[1] - pos.last[1], 0, 0.01)) {
|
||||||
} else {
|
kinematic.lastCol[1] = 0;
|
||||||
kinematic.onFloor = false;
|
|
||||||
kinematic.onCeil = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pos.* = next;
|
var colPosAbs = next + kinematic.lastCol;
|
||||||
|
var lastCol = level_collide(kinematic.col.addv(colPosAbs));
|
||||||
|
if (lastCol.len == 0) {
|
||||||
|
kinematic.lastCol = Vec2f{ 0, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
kinematic.move = next - pos.last;
|
||||||
|
|
||||||
|
pos.pos = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn velocityProcess(_: f32, pos: *Pos, lastpos: *LastPos) void {
|
fn velocityProcess(_: f32, pos: *Pos) void {
|
||||||
var vel = pos.* - lastpos.*;
|
var vel = pos.pos - pos.last;
|
||||||
|
|
||||||
vel *= @splat(2, @as(f32, 0.9));
|
vel *= @splat(2, @as(f32, 0.9));
|
||||||
// vel += Vec2f{ 0, 0.25 };
|
|
||||||
vel = @minimum(Vec2f{ 8, 8 }, @maximum(Vec2f{ -8, -8 }, vel));
|
vel = @minimum(Vec2f{ 8, 8 }, @maximum(Vec2f{ -8, -8 }, vel));
|
||||||
|
|
||||||
lastpos.* = pos.*;
|
pos.last = pos.pos;
|
||||||
pos.* += vel;
|
pos.pos += vel;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gravityProcess(dt: f32, pos: *Pos, gravity: *Gravity) void {
|
fn physicsProcess(dt: f32, pos: *Pos, physics: *Physics) void {
|
||||||
_ = dt;
|
_ = dt;
|
||||||
pos.* = pos.* + gravity.*;
|
var friction = @splat(2, @as(f32, 1)) - physics.friction;
|
||||||
|
pos.pos = pos.last + (pos.pos - pos.last) * friction;
|
||||||
|
pos.pos += physics.gravity;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue