add key repeat
This commit is contained in:
parent
d91fd0f4dc
commit
a153a3f04a
24
src/app.zig
24
src/app.zig
@ -280,16 +280,16 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
const input = plt.input;
|
||||
|
||||
var dir: Vec2 = .init(0, 0);
|
||||
if (input.isKeyPressed(.S)) {
|
||||
if (input.isKeyPressedOrRepeat(.S)) {
|
||||
dir.y += 1;
|
||||
}
|
||||
if (input.isKeyPressed(.W)) {
|
||||
if (input.isKeyPressedOrRepeat(.W)) {
|
||||
dir.y -= 1;
|
||||
}
|
||||
if (input.isKeyPressed(.D)) {
|
||||
if (input.isKeyPressedOrRepeat(.D)) {
|
||||
dir.x += 1;
|
||||
}
|
||||
if (input.isKeyPressed(.A)) {
|
||||
if (input.isKeyPressedOrRepeat(.A)) {
|
||||
dir.x -= 1;
|
||||
}
|
||||
if (dir.x != 0) {
|
||||
@ -297,13 +297,8 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
}
|
||||
self.player_pos = self.player_pos.add(dir);
|
||||
|
||||
if (input.isKeyPressed(.F)) {
|
||||
Audio.stop(self.footstep_sound);
|
||||
}
|
||||
|
||||
if (input.isKeyPressed(.E)) {
|
||||
Audio.stop(self.footstep_sound);
|
||||
self.footstep_sound = Audio.play(.{ .buffer = self.footstep_buffer, .loop = true, .bus = self.sfx_bus });
|
||||
if (dir.x != 0 or dir.y != 0) {
|
||||
self.footstep_sound = Audio.play(.{ .buffer = self.footstep_buffer, .bus = self.sfx_bus });
|
||||
}
|
||||
|
||||
var new_direction = self.player_direction;
|
||||
@ -325,7 +320,7 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
const frames = self.player_sprites.getDirection(self.player_direction);
|
||||
|
||||
if (dir.x != 0 or dir.y != 0) {
|
||||
self.animation_timer += plt.dt;
|
||||
self.animation_timer += plt.input.delta_time;
|
||||
const animatin_time = std.time.ns_per_s / 5;
|
||||
while (self.animation_timer >= animatin_time) {
|
||||
self.player_frame_index = (self.player_frame_index + 1) % @as(u32, @intCast(frames.len));
|
||||
@ -354,11 +349,6 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
}
|
||||
|
||||
Gfx.drawSprite(frames[self.player_frame_index], self.player_pos.multiply(tile_size), tile_size, .white);
|
||||
Gfx.drawText(self.roboto_font, .{
|
||||
.pos = .init(300, 100),
|
||||
.text = try std.fmt.allocPrint(plt.frame, "Hello, World! {:.0}", .{ plt.time() }),
|
||||
.height = 64,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn deinit(self: *App, plt: Platform.Deinit) void {
|
||||
|
||||
@ -6,9 +6,16 @@ const sokol = @import("sokol");
|
||||
const Math = @import("math");
|
||||
const Vec2 = Math.Vec2;
|
||||
|
||||
const Nanoseconds = @import("./root.zig").Nanoseconds;
|
||||
|
||||
const Input = @This();
|
||||
|
||||
key_code_mapping: std.EnumMap(KeyCode, u21),
|
||||
// TODO: Make this configurable from the app
|
||||
const key_repeat_first: Nanoseconds = 0.4 * std.time.ns_per_s;
|
||||
const key_repeat_delay: Nanoseconds = 0.2 * std.time.ns_per_s;
|
||||
|
||||
now: Nanoseconds,
|
||||
delta_time: Nanoseconds,
|
||||
|
||||
window_size: Vec2,
|
||||
focused: bool,
|
||||
@ -18,8 +25,13 @@ mouse_position: ?Vec2,
|
||||
mouse_delta: Vec2,
|
||||
mouse_scroll: Vec2,
|
||||
|
||||
key_code_mapping: std.EnumMap(KeyCode, u21),
|
||||
last_key_repeat_at: std.EnumMap(KeyCode, Nanoseconds),
|
||||
|
||||
pub fn init(window_size: Vec2) Input {
|
||||
return Input{
|
||||
.now = 0,
|
||||
.delta_time = 0,
|
||||
.key_code_mapping = .{},
|
||||
.focused = false,
|
||||
.keyboard = .empty,
|
||||
@ -27,7 +39,8 @@ pub fn init(window_size: Vec2) Input {
|
||||
.mouse_position = null,
|
||||
.mouse_delta = .init(0, 0),
|
||||
.mouse_scroll = .init(0, 0),
|
||||
.window_size = window_size
|
||||
.window_size = window_size,
|
||||
.last_key_repeat_at = .{}
|
||||
};
|
||||
}
|
||||
|
||||
@ -40,7 +53,33 @@ pub fn isKeyReleased(self: Input, key: KeyCode) bool {
|
||||
}
|
||||
|
||||
pub fn isKeyDown(self: Input, key: KeyCode) bool {
|
||||
return self.keyboard.down.contains(key);
|
||||
return self.keyboard.pressed_at.contains(key);
|
||||
}
|
||||
|
||||
pub fn getKeyDown(self: Input, key: KeyCode) ?Nanoseconds {
|
||||
return self.keyboard.getDown(key, self.now);
|
||||
}
|
||||
|
||||
pub fn isKeyRepeat(self: *Input, key: KeyCode) bool {
|
||||
const pressed_at = self.keyboard.pressed_at.get(key) orelse return false;
|
||||
|
||||
var next_repeat_at: Nanoseconds = undefined;
|
||||
if (self.last_key_repeat_at.get(key)) |last_repeat_at| {
|
||||
next_repeat_at = last_repeat_at + key_repeat_delay;
|
||||
} else {
|
||||
next_repeat_at = pressed_at + key_repeat_first;
|
||||
}
|
||||
|
||||
if (self.now >= next_repeat_at) {
|
||||
self.last_key_repeat_at.put(key, next_repeat_at);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isKeyPressedOrRepeat(self: *Input, key: KeyCode) bool {
|
||||
return self.isKeyPressed(key) or self.isKeyRepeat(key);
|
||||
}
|
||||
|
||||
pub fn isMousePressed(self: Input, button: MouseButton) bool {
|
||||
@ -52,7 +91,11 @@ pub fn isMouseReleased(self: Input, button: MouseButton) bool {
|
||||
}
|
||||
|
||||
pub fn isMouseDown(self: Input, button: MouseButton) bool {
|
||||
return self.mouse_buttons.down.contains(button);
|
||||
return self.mouse_buttons.pressed_at.contains(button);
|
||||
}
|
||||
|
||||
pub fn getMouseDown(self: Input, button: MouseButton) ?Nanoseconds {
|
||||
return self.mouse_buttons.getDown(button, self.now);
|
||||
}
|
||||
|
||||
pub const KeyCode = enum(std.math.IntFittingRange(0, sokol.app.max_keycodes-1)) {
|
||||
@ -212,28 +255,35 @@ fn KeyStateType(T: type) type {
|
||||
return struct {
|
||||
pressed: std.EnumSet(T),
|
||||
released: std.EnumSet(T),
|
||||
down: std.EnumSet(T),
|
||||
pressed_at: std.EnumMap(T, Nanoseconds),
|
||||
|
||||
pub const empty = @This(){
|
||||
.pressed = .empty,
|
||||
.released = .empty,
|
||||
.down = .empty,
|
||||
.pressed_at = .{},
|
||||
};
|
||||
|
||||
pub fn press(self: *@This(), key: T) void {
|
||||
pub fn press(self: *@This(), key: T, now: Nanoseconds) void {
|
||||
self.pressed.insert(key);
|
||||
self.down.insert(key);
|
||||
self.pressed_at.put(key, now);
|
||||
}
|
||||
|
||||
pub fn release(self: *@This(), key: T) void {
|
||||
self.released.insert(key);
|
||||
self.down.remove(key);
|
||||
self.pressed_at.remove(key);
|
||||
}
|
||||
|
||||
pub fn getDown(self: @This(), key: T, now: Nanoseconds) ?Nanoseconds {
|
||||
if (self.pressed_at.get(key)) |pressed_at| {
|
||||
return now - pressed_at;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn releaseAll(self: *@This()) void {
|
||||
var iter = self.down.iterator();
|
||||
while (iter.next()) |key| {
|
||||
self.release(key);
|
||||
var iter = self.pressed_at.iterator();
|
||||
while (iter.next()) |e| {
|
||||
self.release(e.key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -105,10 +105,11 @@ fn PlatformType(App: type) type {
|
||||
_ = self.frame_arena.reset(.free_all); // TODO: make arena reset mode configurable
|
||||
|
||||
const now = Io.Timestamp.now(self.io, .awake);
|
||||
const t = self.started_at.durationTo(now);
|
||||
const dt = self.last_frame_at.durationTo(now);
|
||||
self.last_frame_at = now;
|
||||
|
||||
self.input.now = @intCast(self.started_at.durationTo(now).nanoseconds);
|
||||
self.input.delta_time = @intCast(self.last_frame_at.durationTo(now).nanoseconds);
|
||||
|
||||
if (self.input.keyboard.pressed.contains(.F4)) {
|
||||
self.show_imgui = !self.show_imgui;
|
||||
}
|
||||
@ -126,8 +127,6 @@ fn PlatformType(App: type) type {
|
||||
|
||||
if (@hasDecl(App, "frame")) {
|
||||
const plt = Frame{
|
||||
.t = t.nanoseconds,
|
||||
.dt = dt.nanoseconds,
|
||||
.gpa = self.gpa,
|
||||
.arena = self.arena.allocator(),
|
||||
.io = self.io,
|
||||
@ -195,7 +194,9 @@ fn PlatformType(App: type) type {
|
||||
return false;
|
||||
}
|
||||
|
||||
input.keyboard.press(key);
|
||||
const now = self.started_at.durationTo(Io.Timestamp.now(self.io, .awake));
|
||||
|
||||
input.keyboard.press(key, @intCast(now.nanoseconds));
|
||||
self.last_key_pressed = key;
|
||||
},
|
||||
.key_released => |key| {
|
||||
@ -204,13 +205,16 @@ fn PlatformType(App: type) type {
|
||||
}
|
||||
|
||||
input.keyboard.release(key);
|
||||
input.last_key_repeat_at.remove(key);
|
||||
},
|
||||
.mouse_pressed => |button| {
|
||||
if (input.mouse_position == null or input.isMouseDown(button)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
input.mouse_buttons.press(button);
|
||||
const now = self.started_at.durationTo(Io.Timestamp.now(self.io, .awake));
|
||||
|
||||
input.mouse_buttons.press(button, @intCast(now.nanoseconds));
|
||||
},
|
||||
.mouse_released => |button| {
|
||||
if (input.mouse_position == null or !input.isMouseDown(button)) {
|
||||
@ -274,8 +278,8 @@ fn PlatformType(App: type) type {
|
||||
}
|
||||
|
||||
assert(input.mouse_position == null);
|
||||
assert(input.keyboard.down.eql(.empty));
|
||||
assert(input.mouse_buttons.down.eql(.empty));
|
||||
assert(input.keyboard.pressed_at.count() == 0);
|
||||
assert(input.mouse_buttons.pressed_at.count() == 0);
|
||||
input.focused = false;
|
||||
}
|
||||
}
|
||||
@ -306,13 +310,13 @@ fn PlatformType(App: type) type {
|
||||
}
|
||||
|
||||
if (e == .unfocused) {
|
||||
var key_iter = input.keyboard.down.iterator();
|
||||
while (key_iter.next()) |key| {
|
||||
self.appendEvent(.{ .key_released = key });
|
||||
var key_iter = input.keyboard.pressed_at.iterator();
|
||||
while (key_iter.next()) |key_pressed_at| {
|
||||
self.appendEvent(.{ .key_released = key_pressed_at.key });
|
||||
}
|
||||
var mouse_buttons_iter = input.mouse_buttons.down.iterator();
|
||||
while (mouse_buttons_iter.next()) |button| {
|
||||
self.appendEvent(.{ .mouse_released = button });
|
||||
var mouse_buttons_iter = input.mouse_buttons.pressed_at.iterator();
|
||||
while (mouse_buttons_iter.next()) |key_pressed_at| {
|
||||
self.appendEvent(.{ .mouse_released = key_pressed_at.key });
|
||||
}
|
||||
self.appendEvent(.{ .mouse_leave = {} });
|
||||
}
|
||||
@ -566,12 +570,9 @@ pub const Deinit = struct {
|
||||
io: Io,
|
||||
};
|
||||
|
||||
pub const Nanoseconds = i96;
|
||||
pub const Nanoseconds = i64;
|
||||
|
||||
pub const Frame = struct {
|
||||
t: Nanoseconds,
|
||||
dt: Nanoseconds,
|
||||
|
||||
gpa: Allocator,
|
||||
arena: Allocator,
|
||||
frame: Allocator,
|
||||
@ -580,16 +581,16 @@ pub const Frame = struct {
|
||||
input: *Input,
|
||||
input_events: []Input.Event,
|
||||
|
||||
fn nanosecondsToSeconds(nanoseconds: i96) f32 {
|
||||
fn nanosecondsToSeconds(nanoseconds: Nanoseconds) f32 {
|
||||
return @floatCast(@as(f64, @floatFromInt(nanoseconds)) / std.time.ns_per_s);
|
||||
}
|
||||
|
||||
pub fn deltaTime(self: Frame) f32 {
|
||||
return nanosecondsToSeconds(self.dt);
|
||||
return nanosecondsToSeconds(self.input.delta_time);
|
||||
}
|
||||
|
||||
pub fn time(self: Frame) f32 {
|
||||
return nanosecondsToSeconds(self.t);
|
||||
return nanosecondsToSeconds(self.input.now);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user