feat: add basic canvas / improve inspector
This commit is contained in:
@@ -13,14 +13,19 @@ set(GAME_SRC
|
||||
|
||||
src/components/GameComponentRegistry.cpp
|
||||
src/components/Activatable.cpp
|
||||
src/components/Checkpoint.cpp
|
||||
src/components/CrosshairUI.cpp
|
||||
src/components/Door.cpp
|
||||
src/components/FirstPersonController.cpp
|
||||
src/components/Interactable.cpp
|
||||
src/components/InteractableButton.cpp
|
||||
src/components/LightToggle.cpp
|
||||
src/components/MovingPlatform.cpp
|
||||
src/components/PlayerRespawn.cpp
|
||||
src/components/PrintComponent.cpp
|
||||
src/components/TriggerLoggerComponent.cpp
|
||||
src/components/WinGoal.cpp
|
||||
src/components/WinScreenUI.cpp
|
||||
)
|
||||
|
||||
add_executable(lightkeeper ${GAME_SRC})
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
#include <destrum/App.h>
|
||||
#include <destrum/Scene/SceneManager.h>
|
||||
#include <destrum/Graphics/RenderResources.h>
|
||||
#include <destrum/Util/RectGizmo.h>
|
||||
#include <destrum/Util/TransformGizmo.h>
|
||||
|
||||
#include "destrum/Graphics/Resources/Cubemap.h"
|
||||
#include "destrum/Graphics/Resources/Font.h"
|
||||
#include "destrum/ObjectModel/GameObject.h"
|
||||
|
||||
#include <scenes/MainScene.h>
|
||||
@@ -31,7 +31,6 @@ public:
|
||||
|
||||
private:
|
||||
void setupSkybox();
|
||||
void drawCrosshair();
|
||||
void updateViewMode();
|
||||
[[nodiscard]] CameraComponent* getSceneCamera() const;
|
||||
[[nodiscard]] Camera& getViewCamera();
|
||||
@@ -59,8 +58,8 @@ private:
|
||||
MainSceneObjects mainScene{};
|
||||
|
||||
ImageID debug2DTexture{NULL_IMAGE_ID};
|
||||
Font hudFont;
|
||||
TransformGizmo transformGizmo;
|
||||
RectGizmo rectGizmo;
|
||||
|
||||
char scenePath[512]{"scenes/debug_scene.json"};
|
||||
char newSceneName[128]{"EmptyScene"};
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef LIGHTKEEPER_CHECKPOINT_H
|
||||
#define LIGHTKEEPER_CHECKPOINT_H
|
||||
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
|
||||
class Checkpoint final : public Component {
|
||||
public:
|
||||
explicit Checkpoint(GameObject& parent);
|
||||
|
||||
std::string GetTypeName() const override { return "Checkpoint"; }
|
||||
|
||||
void OnTriggerEnter(GameObject* other) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
void SetSpawnOffset(const glm::vec3& offset) { m_SpawnOffset = offset; }
|
||||
[[nodiscard]] glm::vec3 GetSpawnPoint() const;
|
||||
|
||||
private:
|
||||
glm::vec3 m_SpawnOffset{0.0f};
|
||||
};
|
||||
|
||||
#endif // LIGHTKEEPER_CHECKPOINT_H
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef LIGHTKEEPER_CROSSHAIRUI_H
|
||||
#define LIGHTKEEPER_CROSSHAIRUI_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
#include <destrum/ObjectModel/ObjectRef.h>
|
||||
|
||||
// Shows the crosshair while the player is possessed, tints and grows it and shows the focus
|
||||
// indicator while the player looks at an Interactable
|
||||
class CrosshairUI final : public Component {
|
||||
public:
|
||||
explicit CrosshairUI(GameObject& parent);
|
||||
|
||||
std::string GetTypeName() const override { return "CrosshairUI"; }
|
||||
|
||||
void LateUpdate(float dt) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ResolveReferences(const ObjectMap& objects) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
void SetPlayer(const GameObject& player) { m_Player = ObjectRef(player); }
|
||||
void SetCrosshair(const GameObject& crosshair) { m_Crosshair = ObjectRef(crosshair); }
|
||||
void SetFocusIndicator(const GameObject& indicator) { m_FocusIndicator = ObjectRef(indicator); }
|
||||
|
||||
[[nodiscard]] const ObjectRef& GetPlayer() const { return m_Player; }
|
||||
[[nodiscard]] const ObjectRef& GetCrosshair() const { return m_Crosshair; }
|
||||
[[nodiscard]] const ObjectRef& GetFocusIndicator() const { return m_FocusIndicator; }
|
||||
|
||||
private:
|
||||
ObjectRef m_Player;
|
||||
ObjectRef m_Crosshair;
|
||||
ObjectRef m_FocusIndicator;
|
||||
|
||||
glm::vec4 m_Color{1.0f, 1.0f, 1.0f, 0.8f};
|
||||
glm::vec4 m_FocusedColor{1.0f, 0.8f, 0.25f, 1.0f};
|
||||
float m_Size{10.0f};
|
||||
float m_FocusedSize{16.0f};
|
||||
};
|
||||
|
||||
#endif // LIGHTKEEPER_CROSSHAIRUI_H
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
[[nodiscard]] float GetMouseSensitivity() const { return m_MouseSensitivity; }
|
||||
|
||||
[[nodiscard]] bool IsGrounded() const { return m_Grounded; }
|
||||
void Teleport(const glm::vec3& worldPosition);
|
||||
|
||||
[[nodiscard]] Interactable* GetFocusedInteractable() const { return m_FocusedInteractable; }
|
||||
[[nodiscard]] glm::vec3 GetLookDirection() const;
|
||||
|
||||
@@ -1,21 +1,31 @@
|
||||
#ifndef LIGHTKEEPER_GAMECOMPONENTLIST_H
|
||||
#define LIGHTKEEPER_GAMECOMPONENTLIST_H
|
||||
|
||||
#include <components/Checkpoint.h>
|
||||
#include <components/CrosshairUI.h>
|
||||
#include <components/Door.h>
|
||||
#include <components/FirstPersonController.h>
|
||||
#include <components/InteractableButton.h>
|
||||
#include <components/LightToggle.h>
|
||||
#include <components/MovingPlatform.h>
|
||||
#include <components/PlayerRespawn.h>
|
||||
#include <components/PrintComponent.h>
|
||||
#include <components/TriggerLoggerComponent.h>
|
||||
#include <components/WinGoal.h>
|
||||
#include <components/WinScreenUI.h>
|
||||
|
||||
#define LIGHTKEEPER_GAME_COMPONENTS(X) \
|
||||
X(Checkpoint) \
|
||||
X(CrosshairUI) \
|
||||
X(Door) \
|
||||
X(FirstPersonController) \
|
||||
X(InteractableButton) \
|
||||
X(LightToggle) \
|
||||
X(MovingPlatform) \
|
||||
X(PlayerRespawn) \
|
||||
X(PrintComponent) \
|
||||
X(TriggerLoggerComponent)
|
||||
X(TriggerLoggerComponent) \
|
||||
X(WinGoal) \
|
||||
X(WinScreenUI)
|
||||
|
||||
#endif // LIGHTKEEPER_GAMECOMPONENTLIST_H
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef LIGHTKEEPER_PLAYERRESPAWN_H
|
||||
#define LIGHTKEEPER_PLAYERRESPAWN_H
|
||||
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
|
||||
class PlayerRespawn final : public Component {
|
||||
public:
|
||||
explicit PlayerRespawn(GameObject& parent);
|
||||
|
||||
std::string GetTypeName() const override { return "PlayerRespawn"; }
|
||||
|
||||
void Start() override;
|
||||
void FixedUpdate(float fixedDt) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
void SetRespawnPoint(const glm::vec3& worldPosition);
|
||||
[[nodiscard]] const glm::vec3& GetRespawnPoint() const { return m_RespawnPoint; }
|
||||
|
||||
void SetKillHeight(float height) { m_KillHeight = height; }
|
||||
[[nodiscard]] float GetKillHeight() const { return m_KillHeight; }
|
||||
|
||||
void Respawn();
|
||||
|
||||
private:
|
||||
glm::vec3 m_RespawnPoint{0.0f};
|
||||
bool m_HasRespawnPoint{false};
|
||||
float m_KillHeight{-15.0f};
|
||||
};
|
||||
|
||||
#endif // LIGHTKEEPER_PLAYERRESPAWN_H
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef LIGHTKEEPER_WINGOAL_H
|
||||
#define LIGHTKEEPER_WINGOAL_H
|
||||
|
||||
#include <components/Activatable.h>
|
||||
|
||||
class WinGoal final : public Activatable {
|
||||
public:
|
||||
explicit WinGoal(GameObject& parent);
|
||||
|
||||
std::string GetTypeName() const override { return "WinGoal"; }
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
[[nodiscard]] bool IsReached() const { return m_Reached; }
|
||||
void Reset() { m_Reached = false; }
|
||||
|
||||
protected:
|
||||
void OnActivate(GameObject& instigator) override;
|
||||
|
||||
private:
|
||||
bool m_Reached{false};
|
||||
};
|
||||
|
||||
#endif // LIGHTKEEPER_WINGOAL_H
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef LIGHTKEEPER_WINSCREENUI_H
|
||||
#define LIGHTKEEPER_WINSCREENUI_H
|
||||
|
||||
#include <destrum/ObjectModel/Component.h>
|
||||
#include <destrum/ObjectModel/ObjectRef.h>
|
||||
|
||||
// Keeps the screen object active while the goal's WinGoal is reached
|
||||
class WinScreenUI final : public Component {
|
||||
public:
|
||||
explicit WinScreenUI(GameObject& parent);
|
||||
|
||||
std::string GetTypeName() const override { return "WinScreenUI"; }
|
||||
|
||||
void LateUpdate(float dt) override;
|
||||
|
||||
nlohmann::json Serialize() const override;
|
||||
void Deserialize(const nlohmann::json& data) override;
|
||||
void ResolveReferences(const ObjectMap& objects) override;
|
||||
void ImGuiInspector() override;
|
||||
|
||||
void SetGoal(const GameObject& goal) { m_Goal = ObjectRef(goal); }
|
||||
void SetScreen(const GameObject& screen) { m_Screen = ObjectRef(screen); }
|
||||
|
||||
[[nodiscard]] const ObjectRef& GetGoal() const { return m_Goal; }
|
||||
[[nodiscard]] const ObjectRef& GetScreen() const { return m_Screen; }
|
||||
|
||||
private:
|
||||
ObjectRef m_Goal;
|
||||
ObjectRef m_Screen;
|
||||
};
|
||||
|
||||
#endif // LIGHTKEEPER_WINSCREENUI_H
|
||||
@@ -19,7 +19,7 @@ struct MainSceneObjects {
|
||||
MaterialID sphereMaterial{};
|
||||
};
|
||||
|
||||
// Sun, ground, parkour course, models, trigger zone and the player
|
||||
// Sun, ground, parkour course, models, trigger zone, the player and the HUD
|
||||
MainSceneObjects BuildMainScene(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources);
|
||||
|
||||
#endif // LIGHTKEEPER_MAINSCENE_H
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
#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()
|
||||
@@ -43,9 +41,6 @@ void LightKeeper::customInit()
|
||||
debug2DTexture = resources.loadImageFromFile(
|
||||
gfxDevice, AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png"));
|
||||
|
||||
hudFont = Font::LoadFromFile(AssetFS::GetInstance().GetFullPath("engine://fonts/Roboto-Medium.ttf"));
|
||||
hudFont.Upload(gfxDevice, resources);
|
||||
|
||||
}
|
||||
|
||||
void LightKeeper::setupSkybox()
|
||||
@@ -103,39 +98,6 @@ void LightKeeper::updateViewMode()
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
constexpr std::string_view prompt = "[E] Interact";
|
||||
constexpr float promptSize = 22.0f;
|
||||
const glm::vec2 promptExtent = hudFont.MeasureText(prompt, promptSize);
|
||||
renderer2D.DrawText(
|
||||
hudFont,
|
||||
prompt,
|
||||
center + glm::vec2{-promptExtent.x * 0.5f, 28.0f},
|
||||
promptSize,
|
||||
TextStyle{.color = color, .outline = 2.0f, .outlineColor = {0.0f, 0.0f, 0.0f, 0.75f}});
|
||||
}
|
||||
}
|
||||
|
||||
bool LightKeeper::isPlayerViewActive() const
|
||||
{
|
||||
return mainScene.playerController != nullptr && mainScene.playerController->IsPossessed();
|
||||
@@ -165,7 +127,6 @@ void LightKeeper::customDraw()
|
||||
|
||||
renderer.beginDrawing(gfxDevice);
|
||||
submitDebugDraws();
|
||||
drawCrosshair();
|
||||
|
||||
if (isPlayerViewActive())
|
||||
{
|
||||
|
||||
@@ -190,6 +190,12 @@ void LightKeeper::drawSelectionGizmo()
|
||||
return;
|
||||
}
|
||||
|
||||
if (RectGizmo::CanManipulate(*selected)) {
|
||||
const VkExtent3D extent = renderer.getDrawImage().extent;
|
||||
rectGizmo.Manipulate(*selected, glm::vec2{static_cast<float>(extent.width), static_cast<float>(extent.height)});
|
||||
return;
|
||||
}
|
||||
|
||||
transformGizmo.HandleShortcuts();
|
||||
transformGizmo.DrawToolbar();
|
||||
transformGizmo.Manipulate(*selected, getViewCamera());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -7,6 +7,10 @@
|
||||
#include <destrum/Components/Physics/BoxCollider.h>
|
||||
#include <destrum/Components/Physics/CapsuleCollider.h>
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
#include <destrum/Components/UI/Canvas.h>
|
||||
#include <destrum/Components/UI/RectTransform.h>
|
||||
#include <destrum/Components/UI/UIImage.h>
|
||||
#include <destrum/Components/UI/UIText.h>
|
||||
#include <destrum/FS/AssetFS.h>
|
||||
#include <destrum/Graphics/RenderResources.h>
|
||||
#include <destrum/Graphics/Resources/PrimitiveMeshes.h>
|
||||
@@ -16,11 +20,16 @@
|
||||
#include <destrum/Util/ModelDocUtils.h>
|
||||
|
||||
#include <components/FirstPersonController.h>
|
||||
#include <components/Checkpoint.h>
|
||||
#include <components/CrosshairUI.h>
|
||||
#include <components/Door.h>
|
||||
#include <components/InteractableButton.h>
|
||||
#include <components/LightToggle.h>
|
||||
#include <components/MovingPlatform.h>
|
||||
#include <components/PlayerRespawn.h>
|
||||
#include <components/TriggerLoggerComponent.h>
|
||||
#include <components/WinGoal.h>
|
||||
#include <components/WinScreenUI.h>
|
||||
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
@@ -262,6 +271,167 @@ namespace {
|
||||
{2.0f, platformHeight, platformSize.z}, rampMaterial, RigidbodyType::Static);
|
||||
}
|
||||
|
||||
// Floats over the void past the west edge of the ground. The controller jumps 1.3 m and sprints 8.5 m/s,
|
||||
// which carries about 5 m on a level jump, the hardest gaps stay a little under that
|
||||
WinGoal* CreateWinCourse(Scene& scene, GfxDevice& gfxDevice, RenderResources& resources)
|
||||
{
|
||||
const MaterialID checkpointMaterial = AddPrototypeMaterial(gfxDevice, resources, "Green", 0.6f);
|
||||
const MaterialID pillarMaterial = AddPrototypeMaterial(gfxDevice, resources, "Orange", 0.6f);
|
||||
const MaterialID stoneMaterial = AddPrototypeMaterial(gfxDevice, resources, "Purple", 0.5f);
|
||||
const MaterialID beamMaterial = AddPrototypeMaterial(gfxDevice, resources, "Dark", 0.9f);
|
||||
const MaterialID movingMaterial = AddPrototypeMaterial(gfxDevice, resources, "Light", 0.5f);
|
||||
const MaterialID goalMaterial = AddPrototypeMaterial(gfxDevice, resources, "Red", 0.6f);
|
||||
|
||||
constexpr float slabThickness = 0.5f;
|
||||
|
||||
GameObject* course = scene.CreateGameObject("WinCourse");
|
||||
course->GetTransform().SetWorldPosition({-GroundHalfExtent, GroundHeight, 0.0f});
|
||||
const auto addToCourse = [&](GameObject* object)
|
||||
{
|
||||
object->GetTransform().SetParent(&course->GetTransform(), true);
|
||||
return object;
|
||||
};
|
||||
|
||||
// Top center and half size of the last placed platform, the course runs along -X
|
||||
glm::vec3 top{-GroundHalfExtent - 2.5f, GroundHeight, 0.0f};
|
||||
glm::vec2 half{2.5f, 2.5f};
|
||||
int platformCount = 0;
|
||||
int checkpointCount = 0;
|
||||
|
||||
const auto slab = [&](const std::string& name, const glm::vec3& topCenter, const glm::vec2& size,
|
||||
MaterialID materialID, RigidbodyType type)
|
||||
{
|
||||
return addToCourse(CreateBox(scene, gfxDevice, resources, "WinCourse_" + name,
|
||||
topCenter - glm::vec3{0.0f, slabThickness * 0.5f, 0.0f},
|
||||
{size.x, slabThickness, size.y}, materialID, type));
|
||||
};
|
||||
|
||||
// gap is the edge to edge distance along X, rise and dz move the top center
|
||||
const auto next = [&](float gap, float rise, float dz, const glm::vec2& size, MaterialID materialID,
|
||||
RigidbodyType type = RigidbodyType::Static)
|
||||
{
|
||||
top = glm::vec3{top.x - half.x - gap - size.x * 0.5f, top.y + rise, top.z + dz};
|
||||
half = size * 0.5f;
|
||||
return slab("Platform" + std::to_string(++platformCount), top, size, materialID, type);
|
||||
};
|
||||
|
||||
const auto mover = [&](float gap, float rise, float dz, const glm::vec2& size, const glm::vec3& travel,
|
||||
float travelDuration, float pauseDuration)
|
||||
{
|
||||
auto* platform = next(gap, rise, dz, size, movingMaterial, RigidbodyType::Kinematic)
|
||||
->AddComponent<MovingPlatform>();
|
||||
platform->SetTravelOffset(travel);
|
||||
platform->SetTravelDuration(travelDuration);
|
||||
platform->SetPauseDuration(pauseDuration);
|
||||
};
|
||||
|
||||
const auto addCheckpoint = [&]()
|
||||
{
|
||||
GameObject* trigger = addToCourse(
|
||||
scene.CreateGameObject("WinCourse_Checkpoint" + std::to_string(++checkpointCount)));
|
||||
trigger->GetTransform().SetWorldPosition(top + glm::vec3{0.0f, 1.0f, 0.0f});
|
||||
trigger->AddComponent<BoxCollider>(glm::vec3{half.x, 1.0f, half.y})->SetTrigger(true);
|
||||
trigger->AddComponent<Rigidbody>()->SetType(RigidbodyType::Static);
|
||||
trigger->AddComponent<Checkpoint>()->SetSpawnOffset({0.0f, 0.2f, 0.0f});
|
||||
scene.GetPhysics().RegisterGameObject(*trigger);
|
||||
};
|
||||
|
||||
// The start pad touches the ground edge so it can be walked onto
|
||||
slab("Start", top, {5.0f, 5.0f}, checkpointMaterial, RigidbodyType::Static);
|
||||
addCheckpoint();
|
||||
|
||||
// Warm-up hops
|
||||
next(2.0f, 0.0f, 0.0f, {2.0f, 2.0f}, pillarMaterial);
|
||||
next(2.4f, 0.5f, 1.5f, {1.5f, 1.5f}, pillarMaterial);
|
||||
next(2.6f, 0.5f, -3.0f, {1.5f, 1.5f}, pillarMaterial);
|
||||
next(2.8f, 0.0f, 1.5f, {1.2f, 1.2f}, pillarMaterial);
|
||||
next(2.2f, -0.5f, 0.0f, {3.0f, 3.0f}, checkpointMaterial);
|
||||
addCheckpoint();
|
||||
|
||||
// Zig-zag over shrinking stones
|
||||
next(2.6f, 0.0f, 1.0f, {1.0f, 1.0f}, stoneMaterial);
|
||||
next(2.8f, 0.4f, -2.0f, {0.8f, 0.8f}, stoneMaterial);
|
||||
next(2.8f, 0.4f, 2.0f, {0.7f, 0.7f}, stoneMaterial);
|
||||
next(3.0f, 0.4f, -2.0f, {0.7f, 0.7f}, stoneMaterial);
|
||||
next(3.2f, -0.8f, 1.0f, {0.7f, 0.7f}, stoneMaterial);
|
||||
next(2.5f, 0.0f, 0.0f, {3.0f, 3.0f}, checkpointMaterial);
|
||||
addCheckpoint();
|
||||
|
||||
// Offset balance beams, then long sprint jumps
|
||||
next(1.2f, 0.0f, 0.0f, {6.0f, 0.4f}, beamMaterial);
|
||||
next(1.4f, 0.3f, 0.9f, {5.0f, 0.35f}, beamMaterial);
|
||||
next(1.4f, 0.3f, -1.8f, {4.0f, 0.3f}, beamMaterial);
|
||||
next(3.6f, 0.0f, 0.9f, {1.5f, 1.5f}, pillarMaterial);
|
||||
next(4.0f, -0.6f, 0.0f, {1.5f, 1.5f}, pillarMaterial);
|
||||
next(3.8f, 0.0f, 0.0f, {1.2f, 1.2f}, pillarMaterial);
|
||||
next(3.0f, 0.0f, 0.0f, {3.0f, 3.0f}, checkpointMaterial);
|
||||
addCheckpoint();
|
||||
|
||||
// A ferry over a 9 m hole, then sideways movers that drift out of sync
|
||||
constexpr float ferryTravel = 9.0f;
|
||||
mover(0.6f, 0.0f, 0.0f, {2.5f, 2.5f}, {-ferryTravel, 0.0f, 0.0f}, 3.5f, 1.0f);
|
||||
top.x -= ferryTravel;
|
||||
next(0.6f, 0.0f, 0.0f, {2.0f, 2.0f}, pillarMaterial);
|
||||
|
||||
constexpr float sway = 3.0f;
|
||||
mover(2.2f, 0.0f, -sway, {1.5f, 1.5f}, {0.0f, 0.0f, sway * 2.0f}, 2.0f, 0.3f);
|
||||
top.z += sway;
|
||||
mover(2.2f, 0.0f, sway, {1.5f, 1.5f}, {0.0f, 0.0f, -sway * 2.0f}, 2.6f, 0.3f);
|
||||
top.z -= sway;
|
||||
mover(2.2f, 0.0f, -sway, {1.5f, 1.5f}, {0.0f, 0.0f, sway * 2.0f}, 1.7f, 0.3f);
|
||||
top.z += sway;
|
||||
next(2.2f, 0.0f, 0.0f, {3.0f, 3.0f}, checkpointMaterial);
|
||||
addCheckpoint();
|
||||
|
||||
// An elevator up to the last stretch
|
||||
constexpr float liftHeight = 5.0f;
|
||||
mover(0.6f, 0.0f, 0.0f, {2.0f, 2.0f}, {0.0f, liftHeight, 0.0f}, 3.0f, 1.5f);
|
||||
top.y += liftHeight;
|
||||
next(1.2f, 0.0f, 0.0f, {1.5f, 1.5f}, pillarMaterial);
|
||||
|
||||
next(2.8f, 0.5f, 1.2f, {0.8f, 0.8f}, stoneMaterial);
|
||||
next(3.0f, 0.5f, -2.4f, {0.7f, 0.7f}, stoneMaterial);
|
||||
next(3.2f, 0.0f, 2.4f, {0.6f, 0.6f}, stoneMaterial);
|
||||
next(3.4f, -0.4f, -2.4f, {0.6f, 0.6f}, stoneMaterial);
|
||||
next(2.5f, 0.0f, 1.2f, {3.0f, 3.0f}, checkpointMaterial);
|
||||
addCheckpoint();
|
||||
|
||||
// Final gauntlet: a fast sideways mover and three tiny stones
|
||||
constexpr float fastSway = 2.5f;
|
||||
mover(2.4f, 0.0f, -fastSway, {1.2f, 1.2f}, {0.0f, 0.0f, fastSway * 2.0f}, 1.6f, 0.2f);
|
||||
top.z += fastSway;
|
||||
next(2.6f, 0.8f, 0.0f, {1.0f, 1.0f}, stoneMaterial);
|
||||
next(3.4f, 0.0f, 1.0f, {0.8f, 0.8f}, stoneMaterial);
|
||||
next(3.6f, -0.3f, -2.0f, {0.8f, 0.8f}, stoneMaterial);
|
||||
|
||||
GameObject* goal = next(2.5f, 0.5f, 1.0f, {4.0f, 4.0f}, goalMaterial);
|
||||
goal->SetName("WinCourse_Goal");
|
||||
auto* winGoal = goal->AddComponent<WinGoal>();
|
||||
|
||||
const glm::vec3 buttonBase = top + glm::vec3{-1.0f, 0.0f, 0.0f};
|
||||
addToCourse(CreateBox(scene, gfxDevice, resources, "WinCourse_ButtonPedestal",
|
||||
buttonBase + glm::vec3{0.0f, 0.55f, 0.0f}, {0.4f, 1.1f, 0.4f},
|
||||
beamMaterial, RigidbodyType::Static));
|
||||
|
||||
// Every button needs its own material, pressing it recolors the material
|
||||
Material buttonMaterial = Material::SimpleColor(glm::vec3{1.0f, 0.85f, 0.2f}, "WinButtonMaterial");
|
||||
buttonMaterial.roughnessFactor = 0.4f;
|
||||
GameObject* button = addToCourse(CreateBox(scene, gfxDevice, resources, "WinCourse_Button",
|
||||
buttonBase + glm::vec3{0.0f, 1.15f, 0.0f}, {0.25f, 0.1f, 0.25f},
|
||||
resources.materials().addMaterial(std::move(buttonMaterial)),
|
||||
RigidbodyType::Static));
|
||||
button->AddComponent<InteractableButton>()->AddTarget(*goal);
|
||||
|
||||
GameObject* goalLight = addToCourse(scene.CreateGameObject("WinCourse_GoalLight"));
|
||||
goalLight->GetTransform().SetWorldPosition(top + glm::vec3{0.0f, 3.0f, 0.0f});
|
||||
auto* light = goalLight->AddComponent<LightComponent>(LightType::Point);
|
||||
light->SetColor(glm::vec3{1.0f, 0.8f, 0.3f});
|
||||
light->SetIntensity(6.0f);
|
||||
light->SetRange(8.0f);
|
||||
|
||||
return winGoal;
|
||||
}
|
||||
|
||||
struct WallOpening {
|
||||
float offset;
|
||||
float width;
|
||||
@@ -493,12 +663,97 @@ namespace {
|
||||
// Facing the parkour course
|
||||
auto* controller = playerObj->AddComponent<FirstPersonController>();
|
||||
controller->SetYaw(glm::half_pi<float>());
|
||||
playerObj->AddComponent<PlayerRespawn>();
|
||||
scene.GetPhysics().RegisterGameObject(*playerObj);
|
||||
|
||||
objects.player = playerObj;
|
||||
objects.playerController = controller;
|
||||
}
|
||||
|
||||
// Fills its parent until it is given anchors or a size
|
||||
RectTransform* CreateUIObject(Scene& scene, const std::string& name, GameObject& parent)
|
||||
{
|
||||
GameObject* object = scene.CreateGameObject(name);
|
||||
object->GetTransform().SetParent(&parent.GetTransform(), false);
|
||||
auto* rect = object->AddComponent<RectTransform>();
|
||||
rect->Stretch();
|
||||
return rect;
|
||||
}
|
||||
|
||||
GameObject* CreateCrosshair(Scene& scene, GameObject& hud)
|
||||
{
|
||||
auto* crosshair = CreateUIObject(scene, "Crosshair", hud);
|
||||
crosshair->SetAnchor({0.5f, 0.5f});
|
||||
crosshair->SetSize({10.0f, 10.0f});
|
||||
|
||||
constexpr float thickness = 2.0f;
|
||||
auto* horizontal = CreateUIObject(scene, "Crosshair_Horizontal", *crosshair->GetGameObject());
|
||||
horizontal->SetAnchors({0.0f, 0.5f}, {1.0f, 0.5f});
|
||||
horizontal->SetSize({0.0f, thickness});
|
||||
horizontal->GetGameObject()->AddComponent<UIImage>();
|
||||
|
||||
auto* vertical = CreateUIObject(scene, "Crosshair_Vertical", *crosshair->GetGameObject());
|
||||
vertical->SetAnchors({0.5f, 0.0f}, {0.5f, 1.0f});
|
||||
vertical->SetSize({thickness, 0.0f});
|
||||
vertical->GetGameObject()->AddComponent<UIImage>();
|
||||
|
||||
return crosshair->GetGameObject();
|
||||
}
|
||||
|
||||
GameObject* CreateFocusIndicator(Scene& scene, GameObject& crosshair)
|
||||
{
|
||||
GameObject* indicator = CreateUIObject(scene, "FocusIndicator", crosshair)->GetGameObject();
|
||||
|
||||
auto* ring = CreateUIObject(scene, "FocusRing", *indicator);
|
||||
ring->SetAnchor({0.5f, 0.5f});
|
||||
ring->SetSize({28.0f, 28.0f});
|
||||
auto* ringImage = ring->GetGameObject()->AddComponent<UIImage>();
|
||||
ringImage->SetShape(UIImageShape::Ellipse);
|
||||
ringImage->SetOutline(2.0f);
|
||||
|
||||
auto* prompt = CreateUIObject(scene, "InteractPrompt", *indicator);
|
||||
prompt->SetAnchor({0.5f, 0.5f});
|
||||
prompt->SetPivot({0.5f, 0.0f});
|
||||
prompt->SetPosition({0.0f, 28.0f});
|
||||
prompt->SetSize({400.0f, 30.0f});
|
||||
auto* promptText = prompt->GetGameObject()->AddComponent<UIText>("[E] Interact", 22.0f);
|
||||
promptText->SetOutline(2.0f, {0.0f, 0.0f, 0.0f, 0.75f});
|
||||
promptText->SetAlignment(UITextAlign::Center, UITextAlign::Start);
|
||||
|
||||
return indicator;
|
||||
}
|
||||
|
||||
GameObject* CreateWinScreen(Scene& scene, GameObject& hud)
|
||||
{
|
||||
GameObject* screen = CreateUIObject(scene, "WinScreen", hud)->GetGameObject();
|
||||
screen->AddComponent<UIImage>(glm::vec4{0.0f, 0.0f, 0.0f, 0.35f});
|
||||
|
||||
auto* titleText = CreateUIObject(scene, "WinScreen_Title", *screen)->GetGameObject()->AddComponent<UIText>("YOU WIN", 195.0f);
|
||||
titleText->SetColor({1.0f, 0.85f, 0.2f, 1.0f});
|
||||
titleText->SetOutline(4.0f, {0.0f, 0.0f, 0.0f, 0.9f});
|
||||
|
||||
screen->SetActive(false);
|
||||
return screen;
|
||||
}
|
||||
|
||||
// Sizes are in 1080p pixels, the canvas scales them with the screen height
|
||||
void CreateHud(Scene& scene, const GameObject& player, const GameObject& winGoal)
|
||||
{
|
||||
GameObject* hud = scene.CreateGameObject("HUD");
|
||||
hud->AddComponent<Canvas>();
|
||||
|
||||
GameObject* crosshair = CreateCrosshair(scene, *hud);
|
||||
const GameObject* focusIndicator = CreateFocusIndicator(scene, *crosshair);
|
||||
auto* crosshairUi = hud->AddComponent<CrosshairUI>();
|
||||
crosshairUi->SetPlayer(player);
|
||||
crosshairUi->SetCrosshair(*crosshair);
|
||||
crosshairUi->SetFocusIndicator(*focusIndicator);
|
||||
|
||||
auto* winScreenUi = hud->AddComponent<WinScreenUI>();
|
||||
winScreenUi->SetGoal(winGoal);
|
||||
winScreenUi->SetScreen(*CreateWinScreen(scene, *hud));
|
||||
}
|
||||
|
||||
void LoadSpawnBallResources(GfxDevice& gfxDevice, RenderResources& resources, MainSceneObjects& objects)
|
||||
{
|
||||
const auto sphereModel = AssetManager::GetInstance().LoadModel("engine://sphere.fbx", StaticModelOptions());
|
||||
@@ -528,7 +783,9 @@ MainSceneObjects BuildMainScene(Scene& scene, GfxDevice& gfxDevice, RenderResour
|
||||
CreateButton(scene, gfxDevice, resources, *door);
|
||||
CreateHouse(scene, gfxDevice, resources);
|
||||
CreateMovingPlatform(scene, gfxDevice, resources);
|
||||
const WinGoal* winGoal = CreateWinCourse(scene, gfxDevice, resources);
|
||||
CreatePlayer(scene, objects);
|
||||
CreateHud(scene, *objects.player, *winGoal->GetGameObject());
|
||||
LoadSpawnBallResources(gfxDevice, resources, objects);
|
||||
|
||||
return objects;
|
||||
|
||||
Reference in New Issue
Block a user