feat: add interactable component and test button

This commit is contained in:
2026-10-16 23:25:11 +02:00
parent faba4171ec
commit 7dd5ee7903
14 changed files with 236 additions and 6 deletions
@@ -1,4 +1,5 @@
#include <components/FirstPersonController.h>
#include <components/Interactable.h>
#include <glm/glm.hpp>
#include <glm/gtx/norm.hpp>
@@ -69,6 +70,8 @@ void FirstPersonController::Update(float dt)
SetMouseCaptured(!IsMouseCaptured());
}
m_FocusedInteractable = nullptr;
if (!IsMouseCaptured())
{
m_JumpHeld = false;
@@ -85,7 +88,6 @@ void FirstPersonController::Update(float dt)
return;
}
// Jump presses are remembered for a moment, so pressing just before landing still jumps
m_JumpBufferTimer = std::max(m_JumpBufferTimer - dt, 0.0f);
if (input.WasKeyPressed(SDL_SCANCODE_SPACE))
{
@@ -96,6 +98,35 @@ void FirstPersonController::Update(float dt)
m_Yaw += static_cast<float>(input.MouseDeltaX()) * m_MouseSensitivity;
m_Pitch -= static_cast<float>(input.MouseDeltaY()) * m_MouseSensitivity;
m_Pitch = glm::clamp(m_Pitch, glm::radians(-89.0f), glm::radians(89.0f));
m_FocusedInteractable = FindFocusedInteractable();
if (m_FocusedInteractable != nullptr && input.WasKeyPressed(SDL_SCANCODE_E))
{
m_FocusedInteractable->Interact(*GetGameObject());
}
}
glm::vec3 FirstPersonController::GetLookDirection() const
{
return glm::vec3{
std::cos(m_Yaw) * std::cos(m_Pitch),
std::sin(m_Pitch),
std::sin(m_Yaw) * std::cos(m_Pitch),
};
}
Interactable* FirstPersonController::FindFocusedInteractable() const
{
if (m_Rigidbody == nullptr || m_Rigidbody->GetPhysicsWorld() == nullptr)
return nullptr;
PhysicsRaycastHit hit{};
if (!m_Rigidbody->GetPhysicsWorld()->Raycast(
GetEyePosition(), GetLookDirection(), m_InteractDistance, hit, m_Rigidbody->GetBody()))
{
return nullptr;
}
return Interactable::FindOnObjectOrParents(hit.object);
}
void FirstPersonController::FixedUpdate(float fixedDt)
@@ -220,6 +251,7 @@ void FirstPersonController::ImGuiInspector()
ImGui::DragFloat("Fall Gravity Multiplier", &m_FallGravityMultiplier, 0.01f, 1.0f, 5.0f);
ImGui::DragFloat("Coyote Time", &m_CoyoteTime, 0.005f, 0.0f, 1.0f);
ImGui::DragFloat("Jump Buffer Time", &m_JumpBufferTime, 0.005f, 0.0f, 1.0f);
ImGui::DragFloat("Interact Distance", &m_InteractDistance, 0.05f, 0.0f, 20.0f);
ImGui::DragFloat("Mouse Sensitivity", &m_MouseSensitivity, 0.0001f, 0.0f, 0.1f);
ImGui::DragFloat("Eye Height", &m_EyeHeight, 0.01f, -2.0f, 2.0f);
ImGui::Text("Grounded: %s", m_Grounded ? "yes" : "no");
@@ -244,6 +276,7 @@ nlohmann::json FirstPersonController::Serialize() const
{"fallGravityMultiplier", m_FallGravityMultiplier},
{"coyoteTime", m_CoyoteTime},
{"jumpBufferTime", m_JumpBufferTime},
{"interactDistance", m_InteractDistance},
{"mouseSensitivity", m_MouseSensitivity},
{"eyeHeight", m_EyeHeight},
{"yaw", m_Yaw},
@@ -262,6 +295,7 @@ void FirstPersonController::Deserialize(const nlohmann::json& data)
m_FallGravityMultiplier = data.value("fallGravityMultiplier", m_FallGravityMultiplier);
m_CoyoteTime = data.value("coyoteTime", m_CoyoteTime);
m_JumpBufferTime = data.value("jumpBufferTime", m_JumpBufferTime);
m_InteractDistance = data.value("interactDistance", m_InteractDistance);
m_MouseSensitivity = data.value("mouseSensitivity", m_MouseSensitivity);
m_EyeHeight = data.value("eyeHeight", m_EyeHeight);
m_Yaw = data.value("yaw", m_Yaw);
@@ -0,0 +1,17 @@
#include <components/Interactable.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/ObjectModel/Transform.h>
Interactable* Interactable::FindOnObjectOrParents(GameObject* object)
{
while (object != nullptr) {
if (auto* interactable = object->GetComponent<Interactable>();
interactable != nullptr && interactable->isEnabled()) {
return interactable;
}
Transform* parent = object->GetTransform().GetParent();
object = parent != nullptr ? parent->GetOwner() : nullptr;
}
return nullptr;
}
@@ -0,0 +1,78 @@
#include <components/InteractableButton.h>
#include <destrum/Components/MeshRendererComponent.h>
#include <destrum/Graphics/Renderer.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Util/GameState.h>
#include <spdlog/spdlog.h>
#include <array>
#include <imgui.h>
namespace {
constexpr std::array<glm::vec3, 5> ButtonColors{{
{0.9f, 0.1f, 0.1f},
{0.1f, 0.8f, 0.2f},
{0.1f, 0.3f, 0.9f},
{0.9f, 0.8f, 0.1f},
{0.8f, 0.2f, 0.9f},
}};
}
InteractableButton::InteractableButton(GameObject& parent)
: Interactable(parent, "InteractableButton")
{
}
void InteractableButton::Start()
{
ApplyColor();
}
void InteractableButton::Interact(GameObject& interactor)
{
m_ColorIndex = (m_ColorIndex + 1) % ButtonColors.size();
++m_PressCount;
ApplyColor();
spdlog::info("{} pressed {} by {}", GetGameObject()->GetName(), m_PressCount, interactor.GetName());
}
void InteractableButton::ApplyColor()
{
const auto* meshRenderer = GetGameObject()->GetComponent<MeshRendererComponent>();
if (meshRenderer == nullptr || meshRenderer->GetMaterialID() == NULL_MATERIAL_ID ||
!GameState::GetInstance().HasRenderer()) {
return;
}
GameRenderer& renderer = GameState::GetInstance().Renderer();
Material& material = renderer.getMaterialMutable(meshRenderer->GetMaterialID());
material.baseColor = ButtonColors[m_ColorIndex];
material.emissiveColor = ButtonColors[m_ColorIndex] * 0.4f;
renderer.updateMaterialGPU(meshRenderer->GetMaterialID());
}
nlohmann::json InteractableButton::Serialize() const
{
return {
{"colorIndex", m_ColorIndex},
{"pressCount", m_PressCount},
};
}
void InteractableButton::Deserialize(const nlohmann::json& data)
{
m_ColorIndex = data.value("colorIndex", m_ColorIndex) % ButtonColors.size();
m_PressCount = data.value("pressCount", m_PressCount);
}
void InteractableButton::ImGuiInspector()
{
ImGui::Text("Presses: %d", m_PressCount);
if (ImGui::Button("Press")) {
Interact(*GetGameObject());
}
}