feat: add basic canvas / improve inspector

This commit is contained in:
2026-10-18 20:42:04 +02:00
parent 7df65f52b5
commit ad16daea7c
47 changed files with 3142 additions and 405 deletions
+51
View File
@@ -0,0 +1,51 @@
#include <components/Checkpoint.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Serialization/GlmJson.h>
#include <components/PlayerRespawn.h>
#include <imgui.h>
#include <glm/gtc/type_ptr.hpp>
#include <spdlog/spdlog.h>
Checkpoint::Checkpoint(GameObject& parent)
: Component(parent, "Checkpoint")
{
}
glm::vec3 Checkpoint::GetSpawnPoint() const
{
return GetTransform().GetWorldPosition() + m_SpawnOffset;
}
void Checkpoint::OnTriggerEnter(GameObject* other)
{
if (other == nullptr) {
return;
}
auto* respawn = other->GetComponent<PlayerRespawn>();
const glm::vec3 spawnPoint = GetSpawnPoint();
if (respawn == nullptr || respawn->GetRespawnPoint() == spawnPoint) {
return;
}
respawn->SetRespawnPoint(spawnPoint);
spdlog::info("{} reached checkpoint {}", other->GetName(), GetGameObject()->GetName());
}
nlohmann::json Checkpoint::Serialize() const
{
return {{"spawnOffset", m_SpawnOffset}};
}
void Checkpoint::Deserialize(const nlohmann::json& data)
{
m_SpawnOffset = data.value("spawnOffset", m_SpawnOffset);
}
void Checkpoint::ImGuiInspector()
{
ImGui::DragFloat3("Spawn offset", glm::value_ptr(m_SpawnOffset), 0.05f);
}
+107
View File
@@ -0,0 +1,107 @@
#include <components/CrosshairUI.h>
#include <components/FirstPersonController.h>
#include <destrum/Components/UI/RectTransform.h>
#include <destrum/Components/UI/UIGraphic.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Scene/Scene.h>
#include <destrum/Serialization/GlmJson.h>
#include <destrum/Util/ImGuiUtils.h>
#include <imgui.h>
namespace {
void TintHierarchy(GameObject& object, const glm::vec4& color)
{
for (const auto& component : object.GetComponents()) {
if (auto* graphic = dynamic_cast<UIGraphic*>(component.get())) {
graphic->SetColor(color);
}
}
for (Transform* child : object.GetTransform().GetChildren()) {
if (child != nullptr && child->GetOwner() != nullptr) {
TintHierarchy(*child->GetOwner(), color);
}
}
}
}
CrosshairUI::CrosshairUI(GameObject& parent)
: Component(parent, "CrosshairUI")
{
}
void CrosshairUI::LateUpdate(float)
{
const Scene* scene = GetGameObject()->GetScene();
if (scene == nullptr) {
return;
}
GameObject* player = m_Player.Get(*scene);
const auto* controller = player != nullptr ? player->GetComponent<FirstPersonController>() : nullptr;
const bool possessed = controller != nullptr && controller->IsPossessed();
const bool focused = possessed && controller->GetFocusedInteractable() != nullptr;
if (GameObject* indicator = m_FocusIndicator.Get(*scene)) {
indicator->SetActive(focused);
}
GameObject* crosshair = m_Crosshair.Get(*scene);
if (crosshair == nullptr) {
return;
}
crosshair->SetActive(possessed);
if (auto* rectTransform = crosshair->GetComponent<RectTransform>()) {
rectTransform->SetSize(glm::vec2{focused ? m_FocusedSize : m_Size});
}
TintHierarchy(*crosshair, focused ? m_FocusedColor : m_Color);
}
nlohmann::json CrosshairUI::Serialize() const
{
return {
{"player", m_Player},
{"crosshair", m_Crosshair},
{"focusIndicator", m_FocusIndicator},
{"color", m_Color},
{"focusedColor", m_FocusedColor},
{"size", m_Size},
{"focusedSize", m_FocusedSize},
};
}
void CrosshairUI::Deserialize(const nlohmann::json& data)
{
m_Player = data.value("player", ObjectRef{});
m_Crosshair = data.value("crosshair", ObjectRef{});
m_FocusIndicator = data.value("focusIndicator", ObjectRef{});
m_Color = data.value("color", m_Color);
m_FocusedColor = data.value("focusedColor", m_FocusedColor);
m_Size = data.value("size", m_Size);
m_FocusedSize = data.value("focusedSize", m_FocusedSize);
}
void CrosshairUI::ResolveReferences(const ObjectMap& objects)
{
m_Player.Resolve(objects);
m_Crosshair.Resolve(objects);
m_FocusIndicator.Resolve(objects);
}
void CrosshairUI::ImGuiInspector()
{
if (const Scene* scene = GetGameObject()->GetScene()) {
ImGuiUtils::DrawObjectRefPicker("Player", m_Player, *scene, [](const GameObject& object) {
return object.GetComponent<FirstPersonController>() != nullptr;
});
ImGuiUtils::DrawObjectRefPicker("Crosshair", m_Crosshair, *scene);
ImGuiUtils::DrawObjectRefPicker("Focus Indicator", m_FocusIndicator, *scene);
}
ImGui::ColorEdit4("Color", &m_Color.x);
ImGui::ColorEdit4("Focused Color", &m_FocusedColor.x);
ImGui::DragFloat("Size", &m_Size, 0.5f, 0.0f, 500.0f);
ImGui::DragFloat("Focused Size", &m_FocusedSize, 0.5f, 0.0f, 500.0f);
}
@@ -132,6 +132,17 @@ Interactable* FirstPersonController::FindFocusedInteractable() const
return Interactable::FindOnObjectOrParents(hit.object);
}
void FirstPersonController::Teleport(const glm::vec3& worldPosition)
{
if (m_Rigidbody != nullptr) {
m_Rigidbody->Teleport(worldPosition);
} else {
GetTransform().SetWorldPosition(worldPosition);
}
m_PreviousPosition = worldPosition;
m_HasPreviousPosition = true;
}
void FirstPersonController::FixedUpdate(float fixedDt)
{
if (m_Rigidbody == nullptr || !m_Rigidbody->HasPhysicsBody())
@@ -0,0 +1,73 @@
#include <components/PlayerRespawn.h>
#include <destrum/Components/Physics/Rigidbody.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Serialization/GlmJson.h>
#include <components/FirstPersonController.h>
#include <imgui.h>
#include <glm/gtc/type_ptr.hpp>
PlayerRespawn::PlayerRespawn(GameObject& parent)
: Component(parent, "PlayerRespawn")
{
}
void PlayerRespawn::Start()
{
if (!m_HasRespawnPoint) {
SetRespawnPoint(GetTransform().GetWorldPosition());
}
}
void PlayerRespawn::FixedUpdate(float)
{
if (GetTransform().GetWorldPosition().y < m_KillHeight) {
Respawn();
}
}
void PlayerRespawn::SetRespawnPoint(const glm::vec3& worldPosition)
{
m_RespawnPoint = worldPosition;
m_HasRespawnPoint = true;
}
void PlayerRespawn::Respawn()
{
GameObject* object = GetGameObject();
if (auto* controller = object->GetComponent<FirstPersonController>()) {
controller->Teleport(m_RespawnPoint);
} else if (auto* rigidbody = object->GetComponent<Rigidbody>()) {
rigidbody->Teleport(m_RespawnPoint);
} else {
GetTransform().SetWorldPosition(m_RespawnPoint);
}
}
nlohmann::json PlayerRespawn::Serialize() const
{
nlohmann::json data{{"killHeight", m_KillHeight}};
if (m_HasRespawnPoint) {
data["respawnPoint"] = m_RespawnPoint;
}
return data;
}
void PlayerRespawn::Deserialize(const nlohmann::json& data)
{
m_KillHeight = data.value("killHeight", m_KillHeight);
if (data.contains("respawnPoint")) {
SetRespawnPoint(data.at("respawnPoint").get<glm::vec3>());
}
}
void PlayerRespawn::ImGuiInspector()
{
ImGui::DragFloat("Kill height", &m_KillHeight, 0.1f);
ImGui::DragFloat3("Respawn point", glm::value_ptr(m_RespawnPoint), 0.1f);
if (ImGui::Button("Respawn now")) {
Respawn();
}
}
+34
View File
@@ -0,0 +1,34 @@
#include <components/WinGoal.h>
#include <destrum/ObjectModel/GameObject.h>
#include <imgui.h>
#include <spdlog/spdlog.h>
WinGoal::WinGoal(GameObject& parent)
: Activatable(parent, "WinGoal")
{
}
void WinGoal::OnActivate(GameObject& instigator)
{
if (!m_Reached) {
spdlog::info("{} reached the goal", instigator.GetName());
}
m_Reached = true;
}
nlohmann::json WinGoal::Serialize() const
{
return {{"reached", m_Reached}};
}
void WinGoal::Deserialize(const nlohmann::json& data)
{
m_Reached = data.value("reached", m_Reached);
}
void WinGoal::ImGuiInspector()
{
ImGui::Checkbox("Reached", &m_Reached);
}
@@ -0,0 +1,62 @@
#include <components/WinScreenUI.h>
#include <components/WinGoal.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Scene/Scene.h>
#include <destrum/Util/ImGuiUtils.h>
WinScreenUI::WinScreenUI(GameObject& parent)
: Component(parent, "WinScreenUI")
{
}
void WinScreenUI::LateUpdate(float)
{
const Scene* scene = GetGameObject()->GetScene();
if (scene == nullptr) {
return;
}
GameObject* screen = m_Screen.Get(*scene);
if (screen == nullptr) {
return;
}
GameObject* goal = m_Goal.Get(*scene);
const auto* winGoal = goal != nullptr ? goal->GetComponent<WinGoal>() : nullptr;
screen->SetActive(winGoal != nullptr && winGoal->IsReached());
}
nlohmann::json WinScreenUI::Serialize() const
{
return {
{"goal", m_Goal},
{"screen", m_Screen},
};
}
void WinScreenUI::Deserialize(const nlohmann::json& data)
{
m_Goal = data.value("goal", ObjectRef{});
m_Screen = data.value("screen", ObjectRef{});
}
void WinScreenUI::ResolveReferences(const ObjectMap& objects)
{
m_Goal.Resolve(objects);
m_Screen.Resolve(objects);
}
void WinScreenUI::ImGuiInspector()
{
const Scene* scene = GetGameObject()->GetScene();
if (scene == nullptr) {
return;
}
ImGuiUtils::DrawObjectRefPicker("Goal", m_Goal, *scene, [](const GameObject& object) {
return object.GetComponent<WinGoal>() != nullptr;
});
ImGuiUtils::DrawObjectRefPicker("Screen", m_Screen, *scene);
}