diff --git a/include/client/ClientApp.h b/include/client/ClientApp.h new file mode 100644 index 0000000..6139675 --- /dev/null +++ b/include/client/ClientApp.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace Nz { + +class Window; +class Canvas; +class StateMachine; + +} // namespace Nz + +namespace blitz { +namespace client { + +class StateData; + +class ClientApp : public Nz::ApplicationComponent { + public: + ClientApp(Nz::ApplicationBase& app); + ~ClientApp(); + + void Update(Nz::Time elapsedTime) override; + + private: + Nz::Window* m_Window; + std::unique_ptr m_StateMachine; + std::shared_ptr m_StateData; +}; + +} // namespace client +} // namespace blitz diff --git a/include/client/states/AbstractState.h b/include/client/states/AbstractState.h new file mode 100644 index 0000000..bed14b3 --- /dev/null +++ b/include/client/states/AbstractState.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace blitz { +namespace client { + +class AbstractState : public Nz::State, public std::enable_shared_from_this { + public: + AbstractState(std::shared_ptr stateData); + ~AbstractState(); + + protected: + template + void ConnectSignal(T& signal, Args&&... args) { + m_CleanupFunctions.emplace_back( + [connection = signal.Connect(std::forward(args)...)]() mutable { connection.Disconnect(); }); + } + + template + T* CreateWidget(Args&&... args) { + T* widget = m_StateData->m_Canvas->Add(std::forward(args)...); + + auto& entry = m_Widgets.emplace_back(); + entry.m_Widget = widget; + + if (!m_IsVisible) + entry.m_Widget->Hide(); + + return widget; + } + + void DestroyWidget(Nz::BaseWidget* widget); + + StateData& GetStateData() { + return *m_StateData; + } + + const StateData& GetStateData() const { + return *m_StateData; + } + + const std::shared_ptr& GetStateDataPtr() { + return m_StateData; + } + + void Enter(Nz::StateMachine& fsm) override; + void Leave(Nz::StateMachine& fsm) override; + bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override; + + virtual void LayoutWidgets() {} + + private: + NazaraSlot(Nz::RenderTarget, OnRenderTargetSizeChange, m_OnTargetChangeSizeSlot); + + struct WidgetEntry { + Nz::BaseWidget* m_Widget; + bool m_WasVisible = true; + }; + + std::shared_ptr m_StateData; + std::vector> m_CleanupFunctions; + std::vector m_Widgets; + bool m_IsVisible; +}; + +} // namespace client +} // namespace blitz diff --git a/include/client/states/CreateServerState.h b/include/client/states/CreateServerState.h new file mode 100644 index 0000000..c73b73c --- /dev/null +++ b/include/client/states/CreateServerState.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace blitz { +namespace client { + +class CreateServerState : public AbstractState { + public: + CreateServerState(std::shared_ptr a_StateData, std::shared_ptr a_PreviousState); + ~CreateServerState(); + + private: + Nz::ButtonWidget* m_CreateServerButton; + Nz::ButtonWidget* m_BackButton; + std::shared_ptr m_NextState; + std::shared_ptr m_PreviousState; + + void LayoutWidgets() override; + bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override; + + void OnCreateServerPressed(); + void OnBackPressed(); +}; + +} // namespace client +} // namespace blitz diff --git a/include/client/states/JoinServerState.h b/include/client/states/JoinServerState.h new file mode 100644 index 0000000..7d26599 --- /dev/null +++ b/include/client/states/JoinServerState.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace blitz { +namespace client { + +class JoinServerState : public AbstractState { + public: + JoinServerState(std::shared_ptr a_StateData, std::shared_ptr a_PreviousState); + ~JoinServerState(); + + private: + Nz::ButtonWidget* m_JoinServerButton; + Nz::ButtonWidget* m_BackButton; + std::shared_ptr m_NextState; + std::shared_ptr m_PreviousState; + + void LayoutWidgets() override; + bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override; + + void OnJoinServerPressed(); + void OnBackPressed(); +}; + +} // namespace client +} // namespace blitz diff --git a/include/client/states/MainMenuState.h b/include/client/states/MainMenuState.h new file mode 100644 index 0000000..5568aa0 --- /dev/null +++ b/include/client/states/MainMenuState.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace blitz { +namespace client { + +class MainMenuState : public AbstractState { + public: + MainMenuState(std::shared_ptr); + ~MainMenuState(); + + private: + Nz::ButtonWidget* m_JoinServerButton; + Nz::ButtonWidget* m_CreateServerButton; + Nz::ButtonWidget* m_OptionButton; + Nz::ButtonWidget* m_QuitButton; + std::shared_ptr m_NextState; + + void LayoutWidgets() override; + bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override; + + void OnJoinServerPressed(); + void OnCreateServerPressed(); + void OnOptionPressed(); + void OnQuitPressed(); +}; + +} // namespace client +} // namespace blitz diff --git a/include/client/states/OptionState.h b/include/client/states/OptionState.h new file mode 100644 index 0000000..69d9797 --- /dev/null +++ b/include/client/states/OptionState.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace blitz { +namespace client { + +class OptionState : public AbstractState { + public: + OptionState(std::shared_ptr a_StateData, std::shared_ptr a_PreviousState); + ~OptionState(); + + private: + Nz::ButtonWidget* m_OptionButton; + Nz::ButtonWidget* m_BackButton; + std::shared_ptr m_NextState; + std::shared_ptr m_PreviousState; + + void LayoutWidgets() override; + bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override; + + void OnOptionPressed(); + void OnBackPressed(); +}; + +} // namespace client +} // namespace blitz diff --git a/include/client/states/StateData.h b/include/client/states/StateData.h new file mode 100644 index 0000000..e0a461d --- /dev/null +++ b/include/client/states/StateData.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz { + +class ApplicationBase; +class RenderTarget; + +} // namespace Nz + +namespace blitz { +namespace client { + +class ClientApp; + +struct StateData { + std::optional m_Canvas; + std::shared_ptr m_RenderTarget; + ClientApp* m_AppComponent; + Nz::ApplicationBase* m_App; + Nz::EnttWorld* m_World; + Nz::Window* m_Window; + Nz::WindowSwapchain* m_Swapchain; +}; + +} // namespace client +} // namespace blitz diff --git a/src/ClientMain.cpp b/src/ClientMain.cpp index 61d1ad2..bb2f789 100644 --- a/src/ClientMain.cpp +++ b/src/ClientMain.cpp @@ -1,349 +1,17 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -constexpr Nz::UInt32 RenderMaskUI = 0x00010000; -constexpr Nz::UInt32 RenderMask3D = 0x0000FFFF; - -static Nz::Vector3f Get2DDirectionVectorFromRotation(float yaw) { - return { - -std::sin(yaw), - 0, - -std::cos(yaw), - }; -} - -static void CreateLight(Nz::EnttWorld& world) { - entt::handle lightEntity = world.CreateEntity(); - { - auto& lightNode = lightEntity.emplace(); - lightNode.SetPosition({0, 5, 0}); - - auto& entityLight = lightEntity.emplace(); - auto& spotLight = entityLight.AddLight(RenderMask3D); - spotLight.EnableShadowCasting(true); - spotLight.UpdateShadowMapSize(1024); - spotLight.UpdateRadius(10.0f); - spotLight.UpdateDiffuseFactor(0.5f); - } -} - -static void CreateBoxes(Nz::EnttWorld& world) { - constexpr float BoxDims = 1.f; - - std::mt19937 rd(42); - std::uniform_real_distribution colorDis(0.f, 360.f); - std::uniform_real_distribution radiusDis(0.1f, 0.5f); - - std::uniform_real_distribution lengthDis(0.2f, 1.5f); - std::shared_ptr boxMesh = Nz::GraphicalMesh::Build(Nz::Primitive::Box(Nz::Vector3f(1.f))); - - constexpr std::size_t BoxCount = 100; - for (std::size_t i = 0; i < BoxCount; ++i) { - float width = lengthDis(rd); - float height = lengthDis(rd); - float depth = lengthDis(rd); - - std::uniform_real_distribution xRandom(-BoxDims * 0.5f + width, BoxDims * 0.5f - width); - std::uniform_real_distribution yRandom(-BoxDims * 0.5f + height, BoxDims * 0.5f - height); - std::uniform_real_distribution zRandom(-BoxDims * 0.5f + depth, BoxDims * 0.5f - depth); - - entt::handle boxEntity = world.CreateEntity(); - - std::shared_ptr boxMaterial = Nz::MaterialInstance::Instantiate(Nz::MaterialType::Phong); - boxMaterial->SetValueProperty("BaseColor", Nz::Color::sRGBToLinear(Nz::Color::FromHSV(colorDis(rd), 1.f, 1.f))); - - std::shared_ptr sphereModel = std::make_shared(boxMesh); - sphereModel->SetMaterial(0, std::move(boxMaterial)); - - boxEntity.emplace(std::move(sphereModel), RenderMask3D); - - auto& ballNode = boxEntity.emplace(); - ballNode.SetPosition({xRandom(rd), yRandom(rd) + 20.0f, zRandom(rd)}); - ballNode.SetScale({width, height, depth}); - - std::shared_ptr boxCollider = std::make_shared(Nz::Vector3f(width, height, depth)); - - Nz::RigidBody3D::DynamicSettings settings; - settings.geom = boxCollider; - settings.mass = width * height * depth; - - boxEntity.emplace(settings); - } -} - -static void CreateModel(Nz::EnttWorld& world) { - std::filesystem::path resourceDir = "assets/models"; - - Nz::MeshParams meshParams; - meshParams.center = true; - meshParams.vertexRotation = Nz::EulerAnglesf(0.f, 0.f, 0.f); - meshParams.vertexScale = Nz::Vector3f(1.0f); - meshParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Normal_UV_Tangent); - - std::shared_ptr deambuMesh = Nz::Mesh::LoadFromFile(resourceDir / "sol.obj", meshParams); - if (!deambuMesh) { - NazaraError("failed to load model"); - return; - } - - std::shared_ptr gfxMesh = Nz::GraphicalMesh::BuildFromMesh(*deambuMesh); - std::shared_ptr deambModel = std::make_shared(std::move(gfxMesh)); - - entt::handle deambEntity = world.CreateEntity(); - { - auto& entityGfx = deambEntity.emplace(deambModel, RenderMask3D); - // entityGfx.AttachRenderable(deambModel); - - auto& entityNode = deambEntity.emplace(); - entityNode.SetPosition(Nz::Vector3f(0.f, 0.f, 0.f)); - } - - std::shared_ptr device = Nz::Graphics::Instance()->GetRenderDevice(); - - std::shared_ptr material = Nz::MaterialInstance::Instantiate(Nz::MaterialType::Phong); - for (std::string_view passName : {"DepthPass", "ForwardPass"}) { - material->UpdatePassStates(passName, [](Nz::RenderStates& states) { - states.depthClamp = true; - return true; - }); - } - - std::mt19937 rd(42); - std::uniform_real_distribution colorDis(0.f, 360.f); - material->SetValueProperty("BaseColor", Nz::Color::sRGBToLinear(Nz::Color::FromHSV(colorDis(rd), 1.f, 1.f))); - - for (std::size_t i = 0; i < deambModel->GetSubMeshCount(); ++i) - deambModel->SetMaterial(i, material); - - Nz::VertexMapper vertexMapper(*deambuMesh->GetSubMesh(0)); - Nz::SparsePtr vertices = vertexMapper.GetComponentPtr(Nz::VertexComponent::Position); - - auto shipCollider = std::make_shared(vertices, vertexMapper.GetVertexCount(), 0.1f); - - Nz::RigidBody3D::StaticSettings settings; - settings.geom = shipCollider; - - deambEntity.emplace(settings); -} - -static Nz::EulerAnglesf camAngles(0.f, 0.f, 0.f); -static Nz::MillisecondClock updateClock; - -static void CreateCamera(Nz::EnttWorld& world, Nz::Window& window, Nz::Application& app, - std::shared_ptr& renderTarget) { - // Création de la caméra - entt::handle cameraEntity = world.CreateEntity(); - - auto& cameraNode = cameraEntity.emplace(); - cameraNode.SetPosition({0, 2.5, 0}); - - auto& cameraComponent = cameraEntity.emplace(renderTarget, Nz::ProjectionType::Perspective); - - cameraComponent.UpdateClearColor(Nz::Color(0.3f, 0.8f, 1.0f)); - cameraComponent.UpdateZNear(0.1f); - cameraComponent.UpdateZFar(100.0f); - cameraComponent.UpdateRenderMask(RenderMask3D); - cameraComponent.UpdateRenderOrder(-1); - - entt::handle playerEntity = world.CreateEntity(); - - auto& playerNode = playerEntity.emplace(); - playerNode.SetPosition({0, 5, 0}); - cameraNode.SetParent(playerEntity); - - window.GetEventHandler().OnMouseMoved.Connect( - [&](const Nz::WindowEventHandler* /*eventHandler*/, const Nz::WindowEvent::MouseMoveEvent& event) { - constexpr float sensitivity = 0.3f; - - camAngles.yaw -= event.deltaX * sensitivity; - camAngles.yaw.Normalize(); - - camAngles.pitch = Nz::Clamp(camAngles.pitch - event.deltaY * sensitivity, -89.f, 89.f); - - camAngles.roll = 0.0f; - - cameraNode.SetRotation(camAngles); - }); - - Nz::Vector3f playerSize{0.5, 2, 0.5}; - - { - - std::shared_ptr boxMesh = Nz::GraphicalMesh::Build(Nz::Primitive::Box(playerSize)); - - std::shared_ptr boxMaterial = Nz::MaterialInstance::Instantiate(Nz::MaterialType::Phong); - boxMaterial->SetValueProperty("BaseColor", Nz::Color::sRGBToLinear({1.0, 0.0, 0.0})); - - std::shared_ptr sphereModel = std::make_shared(boxMesh); - sphereModel->SetMaterial(0, std::move(boxMaterial)); - playerEntity.emplace(std::move(sphereModel), RenderMask3D); - } - - std::shared_ptr boxCollider = std::make_shared(playerSize); - - static const int playerMass = 100000; - - Nz::RigidBody3D::DynamicSettings settings; - settings.geom = boxCollider; - settings.mass = playerMass; - - playerEntity.emplace(settings); - - app.AddUpdaterFunc([playerEntity]() { - if (std::optional deltaTime = updateClock.RestartIfOver(Nz::Time::Milliseconds(30))) { - Nz::Vector3f front = Get2DDirectionVectorFromRotation(camAngles.yaw.ToRadians()); - Nz::Vector3f left = Get2DDirectionVectorFromRotation(camAngles.yaw.ToRadians() + 3.1415926535 / 2.0); - - auto& playerBody = playerEntity.get(); - - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Z)) { - // std::cout << front << "\n"; - // std::cout << "Pos : " << playerBody.GetPosition() << " \n"; - playerBody.AddForce(front * 10.f * playerMass, Nz::CoordSys::Global); - } - - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::S)) - playerBody.AddForce(-front * 10.f * playerMass, Nz::CoordSys::Local); - - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Q)) - playerBody.AddForce(left * 10.f * playerMass, Nz::CoordSys::Local); - - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::D)) - playerBody.AddForce(-left * 10.f * playerMass, Nz::CoordSys::Local); - - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Space)) { - playerBody.AddForce(Nz::Vector3f::Up() * 15.f * playerMass, Nz::CoordSys::Global); - } - - if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::R)) { - auto& playerNode = playerEntity.get(); - playerNode.SetPosition({0, 10, 0}); - playerBody.SetPosition({0, 10, 0}); - } - - // playerBody.SetRotation({}); - } - }); -} - -static void SetupWidgets(Nz::EnttWorld& world, std::shared_ptr renderTarget, Nz::Window& window) { - entt::handle cameraEntity = world.CreateEntity(); - { - cameraEntity.emplace(); - - auto& cameraComponent = cameraEntity.emplace(renderTarget, Nz::ProjectionType::Orthographic); - - cameraComponent.UpdateClearColor(Nz::Color(0.3f, 0.8f, 1.0f, 0.0f)); - cameraComponent.UpdateRenderMask(RenderMaskUI); - cameraComponent.UpdateRenderOrder(1); - // cameraComponent.UpdateSize({100, 100}); - } - - Nz::Canvas canvas(world.GetRegistry(), window.GetEventHandler(), window.GetCursorController().CreateHandle(), RenderMaskUI); - canvas.Resize({static_cast(window.GetSize().x), static_cast(window.GetSize().y)}); - canvas.EnableBackground(false); - auto widget = canvas.Add(); - widget->EnableBackground(false); - widget->EnableLineWrap(true); - widget->SetBackgroundColor(Nz::Color(0, 0, 0, 50)); - widget->SetCharacterSize(22); - widget->SetTextColor(Nz::Color::White()); - widget->SetTextOutlineThickness(1.f); - widget->SetReadOnly(true); - - auto m_chatEnteringBox = canvas.Add(); - m_chatEnteringBox->EnableBackground(false); - m_chatEnteringBox->SetBackgroundColor(Nz::Color(255, 255, 255, 150)); - m_chatEnteringBox->SetTextColor(Nz::Color::Black()); - m_chatEnteringBox->SetText("ceci est un test incroyable"); - // m_chatEnteringBox->SetMaximumWidth(m_chatEnteringBox->GetPreferredWidth()); - - auto versionLabel = canvas.Add(); - versionLabel->UpdateText( - Nz::SimpleTextDrawer::Draw(std::to_string(100000000) + "." + std::to_string(0) + "." + std::to_string(0), 14)); - versionLabel->Resize({500, 500}); - versionLabel->SetPosition({1000, 800}); - versionLabel->SetPosition({canvas.GetWidth() - versionLabel->GetWidth(), canvas.GetHeight() - versionLabel->GetHeight()}); - - - // Création d'un texte - Nz::SimpleTextDrawer textDrawer; - textDrawer.SetText("Hello world !"); - textDrawer.SetCharacterSize(72); - textDrawer.SetTextOutlineThickness(4.f); - - std::shared_ptr textSprite = std::make_shared(); - textSprite->Update(textDrawer); - - entt::handle textEntity = world.CreateEntity(); - { - auto& nodeComponent = textEntity.emplace(); - - auto& gfxComponent = textEntity.emplace(textSprite, RenderMaskUI); - - Nz::Boxf textBox = textSprite->GetAABB(); - Nz::Vector2ui windowSize = window.GetSize(); - nodeComponent.SetPosition({windowSize.x / 2 - textBox.width / 2, windowSize.y / 2 - textBox.height / 2}); - } -} - -int Video(int argc, char** argv) { - Nz::Application app(argc, argv); - - auto& windowing = app.AddComponent(); - - std::string windowTitle = "Blitz 2"; - Nz::Window& window = windowing.CreateWindow(Nz::VideoMode(1920, 1080, 32), windowTitle); - - auto& ecs = app.AddComponent(); - - auto& world = ecs.AddWorld(); - - auto& physSystem = world.AddSystem(); - physSystem.GetPhysWorld().SetMaxStepCount(1); - physSystem.GetPhysWorld().SetStepSize(Nz::Time::Milliseconds(20)); - physSystem.GetPhysWorld().SetGravity(Nz::Vector3f::Down() * 9.81f); - - Nz::RenderSystem& renderSystem = world.AddSystem(); - - Nz::SwapchainParameters params; - params.presentMode.clear(); - params.presentMode.push_back(Nz::PresentMode::VerticalSync); - - Nz::WindowSwapchain& windowSwapchain = renderSystem.CreateSwapchain(window, params); - - auto renderTarget = std::make_shared(windowSwapchain); - - CreateCamera(world, window, app, renderTarget); - CreateBoxes(world); - CreateModel(world); - CreateLight(world); - - Nz::Mouse::SetRelativeMouseMode(true); - - Nz::MillisecondClock fpsClock; - unsigned int fps = 0; - - app.AddUpdaterFunc([&]() { - fps++; - - if (fpsClock.RestartIfOver(Nz::Time::Second())) { - window.SetTitle(windowTitle + " - " + Nz::NumberToString(fps) + " FPS" + " - " + - Nz::NumberToString(world.GetAliveEntityCount()) + " entities"); - fps = 0; - } - }); +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + Nz::Application app(argc, argv); + + app.AddComponent(); + app.AddComponent(); + app.AddComponent(); return app.Run(); } - -int main(int argc, char** argv) { - return Video(argc, argv); -} diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 644b03d..6466fc6 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -6,23 +6,23 @@ #include #include -#define RegisterHandler(Handler) m_Handlers.push_back(std::make_unique(m_NetworkClient->GetConnection(), m_World)) - namespace blitz { namespace client { -Client::Client(Nz::EnttWorld& a_World) : m_World({a_World}) {} +Client::Client(Nz::EnttWorld& a_World) : m_World({a_World}) { + // TODO: bind RemovePlayersSystem +} Client::~Client() { Disconnect(); } void Client::BindHandlers() { - RegisterHandler(KeepAliveHandler); - RegisterHandler(LoggingSuccessHandler); - RegisterHandler(PlayerJoinHandler); - RegisterHandler(PlayerLeaveHandler); - RegisterHandler(PlayerListHandler); + m_Handlers.push_back(std::make_unique(m_NetworkClient->GetConnection(), m_World)); + m_Handlers.push_back(std::make_unique(m_NetworkClient->GetConnection(), m_World)); + m_Handlers.push_back(std::make_unique(m_NetworkClient->GetConnection(), m_World)); + m_Handlers.push_back(std::make_unique(m_NetworkClient->GetConnection(), m_World)); + m_Handlers.push_back(std::make_unique(m_NetworkClient->GetConnection(), m_World)); } void Client::UnbindHandlers() { diff --git a/src/client/ClientApp.cpp b/src/client/ClientApp.cpp new file mode 100644 index 0000000..e053787 --- /dev/null +++ b/src/client/ClientApp.cpp @@ -0,0 +1,74 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace blitz { +namespace client { + +constexpr Nz::UInt32 RenderMaskUI = 0x00010000; +constexpr Nz::UInt32 RenderMask3D = 0x0000FFFF; + +ClientApp::ClientApp(Nz::ApplicationBase& app) : Nz::ApplicationComponent(app), m_StateMachine(std::make_unique()) { + auto& windowing = app.GetComponent(); + + std::string windowTitle = "Blitz 2"; + m_Window = &windowing.CreateWindow(Nz::VideoMode(1920, 1080, 32), windowTitle); + + auto& ecs = app.GetComponent(); + + auto& world = ecs.AddWorld(); + + Nz::RenderSystem& renderSystem = world.AddSystem(); + + Nz::SwapchainParameters params; + params.presentMode.clear(); + params.presentMode.push_back(Nz::PresentMode::VerticalSync); + + Nz::WindowSwapchain& windowSwapchain = renderSystem.CreateSwapchain(*m_Window, params); + + auto renderTarget = std::make_shared(windowSwapchain); + + entt::handle cameraEntity = world.CreateEntity(); + { + cameraEntity.emplace(); + + auto& cameraComponent = cameraEntity.emplace(renderTarget, Nz::ProjectionType::Orthographic); + + cameraComponent.UpdateClearColor(Nz::Color(0.0f, 0.f, .0f, 0.0f)); + cameraComponent.UpdateRenderMask(RenderMaskUI); + cameraComponent.UpdateRenderOrder(1); + } + + m_StateData = std::make_shared(); + m_StateData->m_App = &app; + m_StateData->m_AppComponent = this; + m_StateData->m_RenderTarget = renderTarget; + m_StateData->m_Window = m_Window; + m_StateData->m_World = &world; + m_StateData->m_Canvas.emplace( + world.GetRegistry(), m_Window->GetEventHandler(), m_Window->GetCursorController().CreateHandle(), RenderMaskUI); + m_StateData->m_Canvas->Resize(Nz::Vector2f(m_Window->GetSize())); + + m_StateMachine->PushState(std::make_shared(m_StateData)); +} + +ClientApp::~ClientApp() { + m_StateMachine->ResetState(nullptr); + m_StateMachine->Update(Nz::Time::Zero()); +} + +void ClientApp::Update(Nz::Time elapsedTime) { + if (!m_StateMachine->Update(elapsedTime)) + GetApp().Quit(); +} + +} // namespace client +} // namespace blitz diff --git a/src/client/states/AbstractState.cpp b/src/client/states/AbstractState.cpp new file mode 100644 index 0000000..edd7e08 --- /dev/null +++ b/src/client/states/AbstractState.cpp @@ -0,0 +1,57 @@ +#include + +namespace blitz { +namespace client { + +AbstractState::AbstractState(std::shared_ptr a_StateData) : m_StateData(std::move(a_StateData)), m_IsVisible(false) { + m_OnTargetChangeSizeSlot.Connect( + m_StateData->m_RenderTarget->OnRenderTargetSizeChange, [this](const Nz::RenderTarget*, const Nz::Vector2ui& /*newSize*/) { + if (m_IsVisible) + LayoutWidgets(); + }); +} + +AbstractState::~AbstractState() { + for (const auto& cleanupFunc : m_CleanupFunctions) + cleanupFunc(); + + for (WidgetEntry& entry : m_Widgets) + entry.m_Widget->Destroy(); +} + +void AbstractState::DestroyWidget(Nz::BaseWidget* widget) { + auto it = std::find_if( + m_Widgets.begin(), m_Widgets.end(), [&](const WidgetEntry& widgetEntity) { return widgetEntity.m_Widget == widget; }); + assert(it != m_Widgets.end()); + + m_Widgets.erase(it); + + widget->Destroy(); +} + +void AbstractState::Enter(Nz::StateMachine& /*fsm*/) { + m_IsVisible = true; + + for (WidgetEntry& entry : m_Widgets) { + if (entry.m_WasVisible) + entry.m_Widget->Show(); + } + + LayoutWidgets(); +} + +void AbstractState::Leave(Nz::StateMachine& /*fsm*/) { + m_IsVisible = false; + + for (WidgetEntry& entry : m_Widgets) { + entry.m_WasVisible = entry.m_Widget->IsVisible(); + entry.m_Widget->Hide(); + } +} + +bool AbstractState::Update(Nz::StateMachine& /*fsm*/, Nz::Time /*elapsedTime*/) { + return true; +} + +} // namespace client +} // namespace blitz \ No newline at end of file diff --git a/src/client/states/CreateServerState.cpp b/src/client/states/CreateServerState.cpp new file mode 100644 index 0000000..56c5de6 --- /dev/null +++ b/src/client/states/CreateServerState.cpp @@ -0,0 +1,73 @@ +#include + +#include +#include + +namespace blitz { +namespace client { + +CreateServerState::CreateServerState(std::shared_ptr a_StateData, std::shared_ptr a_PreviousState) : + AbstractState(std::move(a_StateData)), m_PreviousState(std::move(a_PreviousState)) { + Nz::SimpleTextDrawer textDrawer; + textDrawer.SetTextColor({0.0, 0.0, 0.0, 1.0}); + textDrawer.SetCharacterSize(75); + + m_CreateServerButton = CreateWidget(); + textDrawer.SetText("Create Server"); + m_CreateServerButton->UpdateText(textDrawer); + m_CreateServerButton->Resize(m_CreateServerButton->GetPreferredSize()); + m_CreateServerButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnCreateServerPressed(); }); + + m_BackButton = CreateWidget(); + textDrawer.SetText("Back"); + m_BackButton->UpdateText(textDrawer); + m_BackButton->Resize(m_BackButton->GetPreferredSize()); + m_BackButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnBackPressed(); }); +} + +CreateServerState::~CreateServerState() {} + +bool CreateServerState::Update(Nz::StateMachine& fsm, Nz::Time /*elapsedTime*/) { + if (m_NextState) + fsm.ChangeState(std::move(m_NextState)); + + return true; +} + +void CreateServerState::LayoutWidgets() { + Nz::Vector2f canvasSize = GetStateData().m_Canvas->GetSize(); + Nz::Vector2f center = canvasSize / 2.f; + + constexpr float padding = 10.f; + + std::array widgets = {m_CreateServerButton, m_BackButton}; + + float maxWidth = 0.f; + float totalSize = padding * (widgets.size() - 1); + for (Nz::BaseWidget* widget : widgets) { + Nz::Vector2f size = widget->GetSize(); + + maxWidth = std::max(maxWidth, size.x); + totalSize += size.y; + } + + Nz::Vector2f cursor = center; + cursor.y += totalSize / 2.f; + + for (Nz::BaseWidget* widget : widgets) { + widget->Resize({maxWidth, widget->GetHeight()}); + widget->SetPosition({0.f, cursor.y - widget->GetSize().y, 0.f}); + widget->CenterHorizontal(); + cursor.y -= widget->GetSize().y + padding; + } +} + +void CreateServerState::OnCreateServerPressed() {} + +void CreateServerState::OnBackPressed() { + m_NextState = std::move(m_PreviousState); +} + + +} // namespace client +} // namespace blitz diff --git a/src/client/states/JoinServerState.cpp b/src/client/states/JoinServerState.cpp new file mode 100644 index 0000000..b111a40 --- /dev/null +++ b/src/client/states/JoinServerState.cpp @@ -0,0 +1,73 @@ +#include + +#include +#include + +namespace blitz { +namespace client { + +JoinServerState::JoinServerState(std::shared_ptr a_StateData, std::shared_ptr a_PreviousState) : + AbstractState(std::move(a_StateData)), m_PreviousState(std::move(a_PreviousState)) { + Nz::SimpleTextDrawer textDrawer; + textDrawer.SetTextColor({0.0, 0.0, 0.0, 1.0}); + textDrawer.SetCharacterSize(75); + + m_JoinServerButton = CreateWidget(); + textDrawer.SetText("Join Server"); + m_JoinServerButton->UpdateText(textDrawer); + m_JoinServerButton->Resize(m_JoinServerButton->GetPreferredSize()); + m_JoinServerButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnJoinServerPressed(); }); + + m_BackButton = CreateWidget(); + textDrawer.SetText("Back"); + m_BackButton->UpdateText(textDrawer); + m_BackButton->Resize(m_BackButton->GetPreferredSize()); + m_BackButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnBackPressed(); }); +} + +JoinServerState::~JoinServerState() {} + +bool JoinServerState::Update(Nz::StateMachine& fsm, Nz::Time /*elapsedTime*/) { + if (m_NextState) + fsm.ChangeState(std::move(m_NextState)); + + return true; +} + +void JoinServerState::LayoutWidgets() { + Nz::Vector2f canvasSize = GetStateData().m_Canvas->GetSize(); + Nz::Vector2f center = canvasSize / 2.f; + + constexpr float padding = 10.f; + + std::array widgets = {m_JoinServerButton, m_BackButton}; + + float maxWidth = 0.f; + float totalSize = padding * (widgets.size() - 1); + for (Nz::BaseWidget* widget : widgets) { + Nz::Vector2f size = widget->GetSize(); + + maxWidth = std::max(maxWidth, size.x); + totalSize += size.y; + } + + Nz::Vector2f cursor = center; + cursor.y += totalSize / 2.f; + + for (Nz::BaseWidget* widget : widgets) { + widget->Resize({maxWidth, widget->GetHeight()}); + widget->SetPosition({0.f, cursor.y - widget->GetSize().y, 0.f}); + widget->CenterHorizontal(); + cursor.y -= widget->GetSize().y + padding; + } +} + +void JoinServerState::OnJoinServerPressed() {} + +void JoinServerState::OnBackPressed() { + m_NextState = std::move(m_PreviousState); +} + + +} // namespace client +} // namespace blitz diff --git a/src/client/states/MainMenuState.cpp b/src/client/states/MainMenuState.cpp new file mode 100644 index 0000000..05cb285 --- /dev/null +++ b/src/client/states/MainMenuState.cpp @@ -0,0 +1,101 @@ +#include + +#include +#include +#include + +#include +#include +#include + +namespace blitz { +namespace client { + +MainMenuState::MainMenuState(std::shared_ptr a_StateData) : AbstractState(std::move(a_StateData)) { + Nz::SimpleTextDrawer textDrawer; + textDrawer.SetTextColor({0.0, 0.0, 0.0, 1.0}); + textDrawer.SetCharacterSize(75); + + m_JoinServerButton = CreateWidget(); + textDrawer.SetText("Join Server"); + m_JoinServerButton->UpdateText(textDrawer); + m_JoinServerButton->Resize(m_JoinServerButton->GetPreferredSize()); + m_JoinServerButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnJoinServerPressed(); }); + + m_CreateServerButton = CreateWidget(); + textDrawer.SetText("Create Server"); + m_CreateServerButton->UpdateText(textDrawer); + m_CreateServerButton->Resize(m_CreateServerButton->GetPreferredSize()); + m_CreateServerButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnCreateServerPressed(); }); + + m_OptionButton = CreateWidget(); + textDrawer.SetText("Settings"); + m_OptionButton->UpdateText(textDrawer); + m_OptionButton->Resize(m_OptionButton->GetPreferredSize()); + m_OptionButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnOptionPressed(); }); + + m_QuitButton = CreateWidget(); + textDrawer.SetText("Quit game"); + m_QuitButton->UpdateText(textDrawer); + m_QuitButton->Resize(m_QuitButton->GetPreferredSize()); + m_QuitButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnQuitPressed(); }); +} + +MainMenuState::~MainMenuState() {} + +bool MainMenuState::Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) { + if (!AbstractState::Update(fsm, elapsedTime)) + return false; + + if (m_NextState) + fsm.ChangeState(std::move(m_NextState)); + + return true; +} + +void MainMenuState::LayoutWidgets() { + Nz::Vector2f canvasSize = GetStateData().m_Canvas->GetSize(); + Nz::Vector2f center = canvasSize / 2.f; + + constexpr float padding = 10.f; + + std::array widgets = {m_JoinServerButton, m_CreateServerButton, m_OptionButton, m_QuitButton}; + + float maxWidth = 0.f; + float totalSize = padding * (widgets.size() - 1); + for (Nz::BaseWidget* widget : widgets) { + Nz::Vector2f size = widget->GetSize(); + + maxWidth = std::max(maxWidth, size.x); + totalSize += size.y; + } + + Nz::Vector2f cursor = center; + cursor.y += totalSize / 2.f; + + for (Nz::BaseWidget* widget : widgets) { + widget->Resize({maxWidth, widget->GetHeight()}); + widget->SetPosition({0.f, cursor.y - widget->GetSize().y, 0.f}); + widget->CenterHorizontal(); + cursor.y -= widget->GetSize().y + padding; + } +} + +void MainMenuState::OnJoinServerPressed() { + m_NextState = std::make_shared(GetStateDataPtr(), shared_from_this()); +} + +void MainMenuState::OnCreateServerPressed() { + m_NextState = std::make_shared(GetStateDataPtr(), shared_from_this()); +} + +void MainMenuState::OnOptionPressed() { + m_NextState = std::make_shared(GetStateDataPtr(), shared_from_this()); +} + +void MainMenuState::OnQuitPressed() { + GetStateData().m_App->Quit(); +} + +} // namespace client +} // namespace blitz diff --git a/src/client/states/OptionState.cpp b/src/client/states/OptionState.cpp new file mode 100644 index 0000000..3eb7603 --- /dev/null +++ b/src/client/states/OptionState.cpp @@ -0,0 +1,73 @@ +#include + +#include +#include + +namespace blitz { +namespace client { + +OptionState::OptionState(std::shared_ptr a_StateData, std::shared_ptr a_PreviousState) : + AbstractState(std::move(a_StateData)), m_PreviousState(std::move(a_PreviousState)) { + Nz::SimpleTextDrawer textDrawer; + textDrawer.SetTextColor({0.0, 0.0, 0.0, 1.0}); + textDrawer.SetCharacterSize(75); + + m_OptionButton = CreateWidget(); + textDrawer.SetText("Settings"); + m_OptionButton->UpdateText(textDrawer); + m_OptionButton->Resize(m_OptionButton->GetPreferredSize()); + m_OptionButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnOptionPressed(); }); + + m_BackButton = CreateWidget(); + textDrawer.SetText("Back"); + m_BackButton->UpdateText(textDrawer); + m_BackButton->Resize(m_BackButton->GetPreferredSize()); + m_BackButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnBackPressed(); }); +} + +OptionState::~OptionState() {} + +void OptionState::LayoutWidgets() { + Nz::Vector2f canvasSize = GetStateData().m_Canvas->GetSize(); + Nz::Vector2f center = canvasSize / 2.f; + + constexpr float padding = 10.f; + + std::array widgets = {m_OptionButton, m_BackButton}; + + float maxWidth = 0.f; + float totalSize = padding * (widgets.size() - 1); + for (Nz::BaseWidget* widget : widgets) { + Nz::Vector2f size = widget->GetSize(); + + maxWidth = std::max(maxWidth, size.x); + totalSize += size.y; + } + + Nz::Vector2f cursor = center; + cursor.y += totalSize / 2.f; + + for (Nz::BaseWidget* widget : widgets) { + widget->Resize({maxWidth, widget->GetHeight()}); + widget->SetPosition({0.f, cursor.y - widget->GetSize().y, 0.f}); + widget->CenterHorizontal(); + cursor.y -= widget->GetSize().y + padding; + } +} + +bool OptionState::Update(Nz::StateMachine& fsm, Nz::Time /*elapsedTime*/) { + if (m_NextState) + fsm.ChangeState(std::move(m_NextState)); + + return true; +} + +void OptionState::OnOptionPressed() {} + +void OptionState::OnBackPressed() { + m_NextState = std::move(m_PreviousState); +} + + +} // namespace client +} // namespace blitz