mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 13:44:58 +00:00
feat: Add fly up and fly down input bindings and update related services
This commit is contained in:
@@ -18,6 +18,8 @@
|
||||
"move_back": "S",
|
||||
"move_left": "A",
|
||||
"move_right": "D",
|
||||
"fly_up": "Q",
|
||||
"fly_down": "Z",
|
||||
"music_toggle": "M",
|
||||
"music_toggle_gamepad": "start",
|
||||
"gamepad_move_x_axis": "leftx",
|
||||
|
||||
@@ -70,21 +70,25 @@ end
|
||||
start_music()
|
||||
|
||||
local math3d = require("math3d")
|
||||
local Gui = require("gui")
|
||||
local string_format = string.format
|
||||
|
||||
local InputState = {}
|
||||
InputState.__index = InputState
|
||||
|
||||
function InputState:new()
|
||||
local sharedKeys = {}
|
||||
local instance = {
|
||||
mouseX = 0.0,
|
||||
mouseY = 0.0,
|
||||
mouseDeltaX = 0.0,
|
||||
mouseDeltaY = 0.0,
|
||||
mouseDown = false,
|
||||
mouseDownPrevious = false,
|
||||
wheel = 0.0,
|
||||
textInput = "",
|
||||
keyStates = {},
|
||||
keyStates = sharedKeys,
|
||||
keys = sharedKeys,
|
||||
lastMouseX = nil,
|
||||
lastMouseY = nil,
|
||||
gamepad = {
|
||||
@@ -107,6 +111,7 @@ function InputState:resetTransient()
|
||||
end
|
||||
|
||||
function InputState:setMouse(x, y, isDown, deltaX, deltaY)
|
||||
self.mouseDownPrevious = self.mouseDown
|
||||
if type(deltaX) == "number" and type(deltaY) == "number" then
|
||||
self.mouseDeltaX = deltaX
|
||||
self.mouseDeltaY = deltaY
|
||||
@@ -129,6 +134,18 @@ function InputState:setKey(keyName, isDown)
|
||||
self.keyStates[keyName] = isDown
|
||||
end
|
||||
|
||||
function InputState:mouseJustPressed()
|
||||
return self.mouseDown and not self.mouseDownPrevious
|
||||
end
|
||||
|
||||
function InputState:mouseJustReleased()
|
||||
return not self.mouseDown and self.mouseDownPrevious
|
||||
end
|
||||
|
||||
function InputState:isKeyDown(keyName)
|
||||
return self.keyStates[keyName]
|
||||
end
|
||||
|
||||
function InputState:addTextInput(text)
|
||||
if text then
|
||||
self.textInput = self.textInput .. text
|
||||
@@ -147,6 +164,29 @@ end
|
||||
|
||||
gui_input = InputState:new()
|
||||
|
||||
local gui_context = Gui.newContext()
|
||||
local ui_layout = {
|
||||
width = 1024,
|
||||
height = 768,
|
||||
margin = 16,
|
||||
}
|
||||
local compass_layout = {
|
||||
size = 120,
|
||||
label_height = 18,
|
||||
padding = 10,
|
||||
}
|
||||
local flight_layout = {
|
||||
width = 120,
|
||||
height = 28,
|
||||
spacing = 8,
|
||||
}
|
||||
local ui_state = {
|
||||
flyUpActive = false,
|
||||
flyDownActive = false,
|
||||
flyUpPulse = false,
|
||||
flyDownPulse = false,
|
||||
}
|
||||
|
||||
local function log_debug(fmt, ...)
|
||||
if not lua_debug or not fmt then
|
||||
return
|
||||
@@ -177,6 +217,7 @@ local camera = {
|
||||
|
||||
local controls = {
|
||||
move_speed = 4.0,
|
||||
fly_speed = 3.0,
|
||||
mouse_sensitivity = 0.0025,
|
||||
gamepad_look_speed = 2.5,
|
||||
stick_deadzone = 0.2,
|
||||
@@ -259,6 +300,7 @@ local function update_camera(dt)
|
||||
|
||||
local move_x = 0.0
|
||||
local move_z = 0.0
|
||||
local move_y = 0.0
|
||||
|
||||
if gui_input.keyStates["move_forward"] then
|
||||
move_z = move_z + 1.0
|
||||
@@ -273,6 +315,15 @@ local function update_camera(dt)
|
||||
move_x = move_x - 1.0
|
||||
end
|
||||
|
||||
if gui_input.keyStates["fly_up"] or ui_state.flyUpActive or ui_state.flyUpPulse then
|
||||
move_y = move_y + 1.0
|
||||
end
|
||||
if gui_input.keyStates["fly_down"] or ui_state.flyDownActive or ui_state.flyDownPulse then
|
||||
move_y = move_y - 1.0
|
||||
end
|
||||
ui_state.flyUpPulse = false
|
||||
ui_state.flyDownPulse = false
|
||||
|
||||
if pad and pad.connected then
|
||||
move_x = move_x + apply_deadzone(pad.leftX, controls.stick_deadzone)
|
||||
move_z = move_z - apply_deadzone(pad.leftY, controls.stick_deadzone)
|
||||
@@ -290,6 +341,10 @@ local function update_camera(dt)
|
||||
camera.position[2] = camera.position[2] + (right[2] * move_x + forward_flat[2] * move_z) * speed
|
||||
camera.position[3] = camera.position[3] + (right[3] * move_x + forward_flat[3] * move_z) * speed
|
||||
end
|
||||
|
||||
if move_y ~= 0.0 then
|
||||
camera.position[2] = camera.position[2] + move_y * controls.fly_speed * dt
|
||||
end
|
||||
end
|
||||
|
||||
local function update_audio_controls()
|
||||
@@ -325,6 +380,122 @@ local function create_spinning_cube()
|
||||
}
|
||||
end
|
||||
|
||||
local function heading_from_yaw(yaw)
|
||||
local forward = forward_from_angles(yaw, 0.0)
|
||||
local heading = math.deg(math.atan2(forward[1], -forward[3])) % 360
|
||||
return heading
|
||||
end
|
||||
|
||||
local function heading_to_cardinal(degrees)
|
||||
local directions = {"N", "NE", "E", "SE", "S", "SW", "W", "NW"}
|
||||
local index = math.floor((degrees + 22.5) / 45.0) % 8 + 1
|
||||
return directions[index]
|
||||
end
|
||||
|
||||
local function draw_compass_widget()
|
||||
local size = compass_layout.size
|
||||
local x = ui_layout.width - size - ui_layout.margin
|
||||
local y = ui_layout.margin
|
||||
local rect = {x = x, y = y, width = size, height = size}
|
||||
gui_context:pushRect(rect, {
|
||||
color = {0.06, 0.07, 0.09, 0.88},
|
||||
borderColor = {0.35, 0.38, 0.42, 1.0},
|
||||
})
|
||||
|
||||
Gui.text(gui_context, {
|
||||
x = x,
|
||||
y = y + 2,
|
||||
width = size,
|
||||
height = compass_layout.label_height,
|
||||
}, "Compass", {
|
||||
fontSize = 12,
|
||||
alignX = "center",
|
||||
color = {0.82, 0.88, 0.95, 1.0},
|
||||
})
|
||||
|
||||
local ring_rect = {
|
||||
x = x + compass_layout.padding,
|
||||
y = y + compass_layout.label_height,
|
||||
width = size - compass_layout.padding * 2,
|
||||
height = size - compass_layout.label_height - compass_layout.padding,
|
||||
}
|
||||
local center_x = ring_rect.x + ring_rect.width / 2
|
||||
local center_y = ring_rect.y + ring_rect.height / 2
|
||||
local radius = math.min(ring_rect.width, ring_rect.height) / 2 - 6
|
||||
|
||||
Gui.text(gui_context, {x = center_x - 8, y = ring_rect.y - 2, width = 16, height = 14}, "N", {
|
||||
fontSize = 12,
|
||||
alignX = "center",
|
||||
color = {0.78, 0.82, 0.88, 1.0},
|
||||
})
|
||||
Gui.text(gui_context, {x = ring_rect.x + ring_rect.width - 12, y = center_y - 7, width = 14, height = 14}, "E", {
|
||||
fontSize = 12,
|
||||
alignX = "center",
|
||||
color = {0.78, 0.82, 0.88, 1.0},
|
||||
})
|
||||
Gui.text(gui_context, {x = center_x - 8, y = ring_rect.y + ring_rect.height - 12, width = 16, height = 14}, "S", {
|
||||
fontSize = 12,
|
||||
alignX = "center",
|
||||
color = {0.78, 0.82, 0.88, 1.0},
|
||||
})
|
||||
Gui.text(gui_context, {x = ring_rect.x - 2, y = center_y - 7, width = 14, height = 14}, "W", {
|
||||
fontSize = 12,
|
||||
alignX = "center",
|
||||
color = {0.78, 0.82, 0.88, 1.0},
|
||||
})
|
||||
|
||||
local heading = heading_from_yaw(camera.yaw)
|
||||
local heading_int = math.floor(heading + 0.5) % 360
|
||||
local direction = heading_to_cardinal(heading)
|
||||
local angle = math.rad(heading) - math.pi / 2.0
|
||||
local needle_x = center_x + math.cos(angle) * radius
|
||||
local needle_y = center_y + math.sin(angle) * radius
|
||||
|
||||
gui_context:pushRect({
|
||||
x = needle_x - 3,
|
||||
y = needle_y - 3,
|
||||
width = 6,
|
||||
height = 6,
|
||||
}, {
|
||||
color = {0.98, 0.78, 0.35, 1.0},
|
||||
radius = 2,
|
||||
})
|
||||
|
||||
Gui.text(gui_context, {
|
||||
x = x,
|
||||
y = y + size - 18,
|
||||
width = size,
|
||||
height = 16,
|
||||
}, string_format("%03d deg %s", heading_int, direction), {
|
||||
fontSize = 11,
|
||||
alignX = "center",
|
||||
color = {0.9, 0.92, 0.95, 1.0},
|
||||
})
|
||||
end
|
||||
|
||||
local function draw_flight_buttons()
|
||||
local x = ui_layout.width - flight_layout.width - ui_layout.margin
|
||||
local y = ui_layout.margin * 2 + compass_layout.size
|
||||
|
||||
local up_clicked = Gui.button(gui_context, "fly_up", {
|
||||
x = x,
|
||||
y = y,
|
||||
width = flight_layout.width,
|
||||
height = flight_layout.height,
|
||||
}, "Fly Up")
|
||||
local down_clicked = Gui.button(gui_context, "fly_down", {
|
||||
x = x,
|
||||
y = y + flight_layout.height + flight_layout.spacing,
|
||||
width = flight_layout.width,
|
||||
height = flight_layout.height,
|
||||
}, "Fly Down")
|
||||
|
||||
ui_state.flyUpActive = gui_context.activeWidget == "fly_up"
|
||||
ui_state.flyDownActive = gui_context.activeWidget == "fly_down"
|
||||
ui_state.flyUpPulse = up_clicked
|
||||
ui_state.flyDownPulse = down_clicked
|
||||
end
|
||||
|
||||
function get_scene_objects()
|
||||
return {
|
||||
create_spinning_cube(),
|
||||
@@ -362,3 +533,11 @@ function get_view_projection(aspect)
|
||||
local projection = math3d.perspective(camera.fov, aspect, camera.near, camera.far)
|
||||
return math3d.multiply(projection, view)
|
||||
end
|
||||
|
||||
function get_gui_commands()
|
||||
gui_context:beginFrame(gui_input)
|
||||
draw_compass_widget()
|
||||
draw_flight_buttons()
|
||||
gui_context:endFrame()
|
||||
return gui_context:getCommands()
|
||||
end
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
@@ -216,31 +217,43 @@ RuntimeConfig JsonConfigService::LoadFromJson(std::shared_ptr<ILogger> logger, c
|
||||
if (!bindingsValue.IsObject()) {
|
||||
throw std::runtime_error("JSON member 'input_bindings' must be an object");
|
||||
}
|
||||
auto readBinding = [&](const char* name, std::string& target) {
|
||||
if (!bindingsValue.HasMember(name)) {
|
||||
struct BindingSpec {
|
||||
const char* name;
|
||||
std::string InputBindings::* member;
|
||||
};
|
||||
const std::array<BindingSpec, 16> bindingSpecs = {{
|
||||
{"move_forward", &InputBindings::moveForwardKey},
|
||||
{"move_back", &InputBindings::moveBackKey},
|
||||
{"move_left", &InputBindings::moveLeftKey},
|
||||
{"move_right", &InputBindings::moveRightKey},
|
||||
{"fly_up", &InputBindings::flyUpKey},
|
||||
{"fly_down", &InputBindings::flyDownKey},
|
||||
{"music_toggle", &InputBindings::musicToggleKey},
|
||||
{"music_toggle_gamepad", &InputBindings::musicToggleGamepadButton},
|
||||
{"gamepad_move_x_axis", &InputBindings::gamepadMoveXAxis},
|
||||
{"gamepad_move_y_axis", &InputBindings::gamepadMoveYAxis},
|
||||
{"gamepad_look_x_axis", &InputBindings::gamepadLookXAxis},
|
||||
{"gamepad_look_y_axis", &InputBindings::gamepadLookYAxis},
|
||||
{"gamepad_dpad_up", &InputBindings::gamepadDpadUpButton},
|
||||
{"gamepad_dpad_down", &InputBindings::gamepadDpadDownButton},
|
||||
{"gamepad_dpad_left", &InputBindings::gamepadDpadLeftButton},
|
||||
{"gamepad_dpad_right", &InputBindings::gamepadDpadRightButton},
|
||||
}};
|
||||
|
||||
auto readBinding = [&](const BindingSpec& spec) {
|
||||
if (!bindingsValue.HasMember(spec.name)) {
|
||||
return;
|
||||
}
|
||||
const auto& value = bindingsValue[name];
|
||||
const auto& value = bindingsValue[spec.name];
|
||||
if (!value.IsString()) {
|
||||
throw std::runtime_error("JSON member 'input_bindings." + std::string(name) + "' must be a string");
|
||||
throw std::runtime_error("JSON member 'input_bindings." + std::string(spec.name) + "' must be a string");
|
||||
}
|
||||
target = value.GetString();
|
||||
config.inputBindings.*(spec.member) = value.GetString();
|
||||
};
|
||||
|
||||
readBinding("move_forward", config.inputBindings.moveForwardKey);
|
||||
readBinding("move_back", config.inputBindings.moveBackKey);
|
||||
readBinding("move_left", config.inputBindings.moveLeftKey);
|
||||
readBinding("move_right", config.inputBindings.moveRightKey);
|
||||
readBinding("music_toggle", config.inputBindings.musicToggleKey);
|
||||
readBinding("music_toggle_gamepad", config.inputBindings.musicToggleGamepadButton);
|
||||
readBinding("gamepad_move_x_axis", config.inputBindings.gamepadMoveXAxis);
|
||||
readBinding("gamepad_move_y_axis", config.inputBindings.gamepadMoveYAxis);
|
||||
readBinding("gamepad_look_x_axis", config.inputBindings.gamepadLookXAxis);
|
||||
readBinding("gamepad_look_y_axis", config.inputBindings.gamepadLookYAxis);
|
||||
readBinding("gamepad_dpad_up", config.inputBindings.gamepadDpadUpButton);
|
||||
readBinding("gamepad_dpad_down", config.inputBindings.gamepadDpadDownButton);
|
||||
readBinding("gamepad_dpad_left", config.inputBindings.gamepadDpadLeftButton);
|
||||
readBinding("gamepad_dpad_right", config.inputBindings.gamepadDpadRightButton);
|
||||
for (const auto& spec : bindingSpecs) {
|
||||
readBinding(spec);
|
||||
}
|
||||
|
||||
auto readMapping = [&](const char* name,
|
||||
std::unordered_map<std::string, std::string>& target) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <rapidjson/writer.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
@@ -70,20 +71,31 @@ void JsonConfigWriterService::WriteConfig(const RuntimeConfig& config, const std
|
||||
rapidjson::Value stringValue(value.c_str(), allocator);
|
||||
bindingsObject.AddMember(nameValue, stringValue, allocator);
|
||||
};
|
||||
addBindingMember("move_forward", config.inputBindings.moveForwardKey);
|
||||
addBindingMember("move_back", config.inputBindings.moveBackKey);
|
||||
addBindingMember("move_left", config.inputBindings.moveLeftKey);
|
||||
addBindingMember("move_right", config.inputBindings.moveRightKey);
|
||||
addBindingMember("music_toggle", config.inputBindings.musicToggleKey);
|
||||
addBindingMember("music_toggle_gamepad", config.inputBindings.musicToggleGamepadButton);
|
||||
addBindingMember("gamepad_move_x_axis", config.inputBindings.gamepadMoveXAxis);
|
||||
addBindingMember("gamepad_move_y_axis", config.inputBindings.gamepadMoveYAxis);
|
||||
addBindingMember("gamepad_look_x_axis", config.inputBindings.gamepadLookXAxis);
|
||||
addBindingMember("gamepad_look_y_axis", config.inputBindings.gamepadLookYAxis);
|
||||
addBindingMember("gamepad_dpad_up", config.inputBindings.gamepadDpadUpButton);
|
||||
addBindingMember("gamepad_dpad_down", config.inputBindings.gamepadDpadDownButton);
|
||||
addBindingMember("gamepad_dpad_left", config.inputBindings.gamepadDpadLeftButton);
|
||||
addBindingMember("gamepad_dpad_right", config.inputBindings.gamepadDpadRightButton);
|
||||
struct BindingSpec {
|
||||
const char* name;
|
||||
std::string InputBindings::* member;
|
||||
};
|
||||
const std::array<BindingSpec, 16> bindingSpecs = {{
|
||||
{"move_forward", &InputBindings::moveForwardKey},
|
||||
{"move_back", &InputBindings::moveBackKey},
|
||||
{"move_left", &InputBindings::moveLeftKey},
|
||||
{"move_right", &InputBindings::moveRightKey},
|
||||
{"fly_up", &InputBindings::flyUpKey},
|
||||
{"fly_down", &InputBindings::flyDownKey},
|
||||
{"music_toggle", &InputBindings::musicToggleKey},
|
||||
{"music_toggle_gamepad", &InputBindings::musicToggleGamepadButton},
|
||||
{"gamepad_move_x_axis", &InputBindings::gamepadMoveXAxis},
|
||||
{"gamepad_move_y_axis", &InputBindings::gamepadMoveYAxis},
|
||||
{"gamepad_look_x_axis", &InputBindings::gamepadLookXAxis},
|
||||
{"gamepad_look_y_axis", &InputBindings::gamepadLookYAxis},
|
||||
{"gamepad_dpad_up", &InputBindings::gamepadDpadUpButton},
|
||||
{"gamepad_dpad_down", &InputBindings::gamepadDpadDownButton},
|
||||
{"gamepad_dpad_left", &InputBindings::gamepadDpadLeftButton},
|
||||
{"gamepad_dpad_right", &InputBindings::gamepadDpadRightButton},
|
||||
}};
|
||||
for (const auto& spec : bindingSpecs) {
|
||||
addBindingMember(spec.name, config.inputBindings.*(spec.member));
|
||||
}
|
||||
|
||||
auto addMappingObject = [&](const char* name,
|
||||
const std::unordered_map<std::string, std::string>& mappings,
|
||||
|
||||
@@ -312,6 +312,8 @@ void SdlInputService::BuildActionKeyMapping() {
|
||||
addKey("move_back", bindings.moveBackKey);
|
||||
addKey("move_left", bindings.moveLeftKey);
|
||||
addKey("move_right", bindings.moveRightKey);
|
||||
addKey("fly_up", bindings.flyUpKey);
|
||||
addKey("fly_down", bindings.flyDownKey);
|
||||
addKey("music_toggle", bindings.musicToggleKey);
|
||||
|
||||
auto toLower = [](std::string value) {
|
||||
|
||||
@@ -15,6 +15,8 @@ struct InputBindings {
|
||||
std::string moveBackKey = "S";
|
||||
std::string moveLeftKey = "A";
|
||||
std::string moveRightKey = "D";
|
||||
std::string flyUpKey = "Q";
|
||||
std::string flyDownKey = "Z";
|
||||
std::string musicToggleKey = "M";
|
||||
std::string musicToggleGamepadButton = "start";
|
||||
std::string gamepadMoveXAxis = "leftx";
|
||||
|
||||
Reference in New Issue
Block a user