feat: add interactable component and test button
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
|
||||
#include <destrum/Components/CameraComponent.h>
|
||||
#include <destrum/FS/AssetFS.h>
|
||||
#include <destrum/Graphics/Managers/Renderer2D.h>
|
||||
#include <destrum/Scene/Scene.h>
|
||||
|
||||
#include <components/FirstPersonController.h>
|
||||
#include <components/Interactable.h>
|
||||
#include <components/GameComponentRegistry.h>
|
||||
|
||||
LightKeeper::LightKeeper() : App()
|
||||
@@ -69,6 +71,29 @@ void LightKeeper::customUpdate(float dt)
|
||||
updateDebugUi();
|
||||
}
|
||||
|
||||
void LightKeeper::drawCrosshair()
|
||||
{
|
||||
if (!isPlayerViewActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool focused = mainScene.playerController->GetFocusedInteractable() != nullptr;
|
||||
const glm::vec4 color = focused ? glm::vec4{1.0f, 0.8f, 0.25f, 1.0f} : glm::vec4{1.0f, 1.0f, 1.0f, 0.8f};
|
||||
const float armLength = focused ? 16.0f : 10.0f;
|
||||
constexpr float thickness = 2.0f;
|
||||
|
||||
const VkExtent3D extent = renderer.getDrawImage().extent;
|
||||
const glm::vec2 center{static_cast<float>(extent.width) * 0.5f, static_cast<float>(extent.height) * 0.5f};
|
||||
|
||||
auto& renderer2D = Renderer2D::GetInstance();
|
||||
const float halfArm = armLength * 0.5f;
|
||||
renderer2D.DrawLine(center - glm::vec2{halfArm, 0.0f}, center + glm::vec2{halfArm, 0.0f}, thickness, color);
|
||||
renderer2D.DrawLine(center - glm::vec2{0.0f, halfArm}, center + glm::vec2{0.0f, halfArm}, thickness, color);
|
||||
if (focused) {
|
||||
renderer2D.DrawEllipseOutline(center, glm::vec2{14.0f}, thickness, color);
|
||||
}
|
||||
}
|
||||
|
||||
bool LightKeeper::isPlayerViewActive() const
|
||||
{
|
||||
return mainScene.playerController != nullptr && mainScene.playerController->IsMouseCaptured();
|
||||
@@ -92,8 +117,8 @@ void LightKeeper::customDraw()
|
||||
|
||||
renderer.beginDrawing(gfxDevice);
|
||||
submitDebugDraws();
|
||||
drawCrosshair();
|
||||
|
||||
// The player camera is placed here, after the fixed steps, so it can be interpolated between them
|
||||
if (isPlayerViewActive())
|
||||
{
|
||||
camera.SetRotation(mainScene.playerController->GetYaw(), mainScene.playerController->GetPitch());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <destrum/Util/ModelDocUtils.h>
|
||||
|
||||
#include <components/FirstPersonController.h>
|
||||
#include <components/InteractableButton.h>
|
||||
#include <components/TriggerLoggerComponent.h>
|
||||
|
||||
#include <glm/gtc/constants.hpp>
|
||||
@@ -158,6 +159,22 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
void CreateButton(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources)
|
||||
{
|
||||
const MaterialID pedestalMaterial = AddPrototypeMaterial(gfxDevice, resources, "Dark", 0.9f);
|
||||
CreateBox(scene, gfxDevice, resources, "ButtonPedestal", {1.5f, -0.45f, -4.5f}, {0.4f, 1.1f, 0.4f},
|
||||
pedestalMaterial, RigidbodyType::Static);
|
||||
|
||||
// Every button needs its own material, pressing it recolors the material
|
||||
Material buttonMaterial = Material::SimpleColor(glm::vec3{1.0f}, "ButtonMaterial");
|
||||
buttonMaterial.roughnessFactor = 0.4f;
|
||||
const MaterialID buttonMaterialID = resources.materials().addMaterial(std::move(buttonMaterial));
|
||||
|
||||
GameObject* button = CreateBox(scene, gfxDevice, resources, "Button", {1.5f, 0.15f, -4.5f}, {0.25f, 0.1f, 0.25f},
|
||||
buttonMaterialID, RigidbodyType::Static);
|
||||
button->AddComponent<InteractableButton>();
|
||||
}
|
||||
|
||||
void CreateFlightHelmet(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources)
|
||||
{
|
||||
GameObject* helmetObj = ModelSpawner::SpawnStaticModel(
|
||||
@@ -282,6 +299,7 @@ MainSceneObjects BuildMainScene(Scene& scene, GfxDevice& gfxDevice, RenderResour
|
||||
CreateTree(scene, gfxDevice, resources);
|
||||
CreateCapybara(scene, gfxDevice, resources);
|
||||
CreateTriggerZone(scene);
|
||||
CreateButton(scene, gfxDevice, resources);
|
||||
CreatePlayer(scene, objects);
|
||||
LoadSpawnBallResources(gfxDevice, resources, objects);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user