Pārlūkot izejas kodu

Adding Main Menu

Vitaliy Polonski 1 dienu atpakaļ
vecāks
revīzija
b0df8966dd

+ 2 - 0
CMakeLists.txt

@@ -37,6 +37,8 @@ add_executable(kariokaEngine
 	src/Engine.cpp
 
 	# States
+	src/states/StateManager.cpp
+	src/states/MainMenuState.cpp
 
 	# Systems
 

+ 0 - 0
assets/scripts/init.py


+ 4 - 0
imgui.ini

@@ -2,3 +2,7 @@
 Pos=60,60
 Size=400,400
 
+[Window][Main Menu]
+Pos=440,110
+Size=400,500
+

+ 3 - 0
run.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./compile.sh && ./BUILD/kariokaEngine.exe

+ 37 - 11
src/Engine.cpp

@@ -1,19 +1,25 @@
 #include "Engine.h"
+#include "states/MainMenuState.h"
 #include <SDL2/SDL_log.h>
 #include <SDL2/SDL_mixer.h>
 
 Engine::Engine()
 {
+    // Init systems
     InitSDL();
     InitImGui();
     InitAudio();
-//    InitPython();
-    SDL_Log("Engine::Engine()");
+    InitPython();
+    
+    // Load assets ...
+    
+    // Run MainMenuState
+    //stateManager.ChangeState(std::make_unique<MainMenuState>(), *this);
+    stateManager.PushState(std::make_unique<MainMenuState>(), *this);
 }
 
 Engine::~Engine()
 {	
-    SDL_Log("Engine::~Engine()");
     ImGui_ImplSDLRenderer2_Shutdown();
     ImGui_ImplSDL2_Shutdown();
     ImGui::DestroyContext();
@@ -23,7 +29,7 @@ Engine::~Engine()
     if (renderer) { SDL_DestroyRenderer(renderer); }
     if (window)   { SDL_DestroyWindow(window); }
     
-//    ShutdownPython();
+    ShutdownPython();
     
     SDL_Quit();
 }
@@ -75,25 +81,25 @@ void Engine::HandleEvents()
             isRunning = false;
         }
         
-        // stateManager.HandleEvents(*this, e);
+        stateManager.HandleEvents(*this, e);
     }
 }
 
 void Engine::Update(float dt)
 {
-    // stateManager.Update(*this, dt);
+    stateManager.Update(*this, dt);
 }
 
 void Engine::Render()
 {
     SDL_RenderClear(renderer);
     
-    // stateManager.Render(*this);
+    stateManager.Render(*this);
     
     ImGui_ImplSDLRenderer2_NewFrame();
     ImGui_ImplSDL2_NewFrame();
     ImGui::NewFrame();
-    // stateManager.RenderImGui(*this);
+    stateManager.RenderImGui(*this);
     ImGui::Render();
     ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer);
     
@@ -114,7 +120,16 @@ void Engine::Run()
         Render();
         
         // Safe State Transition
-        // ...
+        if (pendingNewState)
+        {
+            stateManager.ChangeState(std::move(pendingNewState), *this);
+            pendingNewState = nullptr;
+        }
+        else if (pendingPop)
+        {
+            stateManager.PopState(*this);
+            pendingPop = false;
+        }
     }
 }
 
@@ -128,9 +143,8 @@ void Engine::InitPython()
         
         py::module_ sys = py::module_::import("sys");
         auto path = sys.attr("path").cast<py::list>();
-        path.append("assets/scripts");
         SDL_Log("Python interpreter initialized");
-        RunPythonScript("init.py");
+        RunPythonScript("assets/scripts/init.py");
     }
     catch (const py::error_already_set& e)
     {
@@ -157,4 +171,16 @@ void Engine::RunPythonScript(const std::string& filename)
     {
         SDL_Log("Python script error in %s:\n%s", filename.c_str(), e.what());
     }
+}
+
+void Engine::RequestStateChange(std::unique_ptr<BaseState> newState)
+{
+    pendingNewState = std::move(newState);
+    pendingPop = false;
+}
+
+void Engine::RequestPopState()
+{
+    pendingPop = true;
+    pendingNewState = nullptr;
 }

+ 10 - 1
src/Engine.h

@@ -10,6 +10,8 @@
 
 #include "EngineConfig.h"
 
+#include "states/StateManager.h"
+
 #include <pybind11/embed.h>
 namespace py = pybind11;
 
@@ -26,7 +28,12 @@ public:
     bool          IsRunning()   const { return isRunning; }
     void          StopRunning()       { isRunning = false; }
     
+    // StateManager
+    StateManager  stateManager;
+    
     // Safe State transition
+    void RequestStateChange(std::unique_ptr<BaseState> newState);
+    void RequestPopState();
     
 private:
     SDL_Window*   window    = nullptr;
@@ -49,7 +56,9 @@ private:
     void RunPythonScript(const std::string& filename);
     std::unique_ptr<py::scoped_interpreter> pythonInterpreter;
 
-
+    // StateManager
+    std::unique_ptr<BaseState> pendingNewState = nullptr;
+    bool pendingPop = false;
 };
 
 #endif

+ 0 - 2
src/main.cpp

@@ -3,9 +3,7 @@
 
 int main(int argc, char* argv[])
 {
-    fprintf(stderr, "Starting main()\n");
     Engine engine;
-    fprintf(stderr, "Calling engine.Run()\n");
     engine.Run();
     return 0;
 }

+ 24 - 0
src/states/BaseState.h

@@ -0,0 +1,24 @@
+#ifndef __STATES__BASE_STATE_H__
+#define __STATES__BASE_STATE_H__
+
+#include <SDL2/SDL.h>
+
+class Engine; // Forward declaration
+
+class BaseState
+{
+public:
+    virtual ~BaseState() = default;
+    
+    virtual void Enter(Engine& engine) {};
+    virtual void Exit(Engine& engine) {};
+    virtual void HandleEvents(Engine& engine, SDL_Event& e) = 0;
+    virtual void Update(Engine& engine, float dt) = 0;
+    virtual void Render(Engine& engine) = 0;
+    virtual void RenderImGui(Engine& engine) {};
+
+private:
+
+};
+
+#endif

+ 82 - 0
src/states/MainMenuState.cpp

@@ -0,0 +1,82 @@
+#include "MainMenuState.h"
+#include "../Engine.h"
+#include <imgui.h>
+#include <SDL2/SDL_log.h>
+
+void MainMenuState::Enter(Engine& engine)
+{
+    fadeAlpha = 1.0f;
+}
+
+void MainMenuState::Exit(Engine& engine)
+{
+}
+
+void MainMenuState::HandleEvents(Engine& engine, SDL_Event& e)
+{
+}
+
+void MainMenuState::Update(Engine& engine, float dt)
+{
+    if (fadeAlpha < 1.0f)
+    {
+        fadeAlpha += dt * 2.0f;
+        if (fadeAlpha > 1.0f)
+        {
+            fadeAlpha = 1.0f;
+        }
+    }
+}
+
+void MainMenuState::Render(Engine& engine)
+{
+    SDL_Renderer* renderer = engine.GetRenderer();
+    SDL_SetRenderDrawColor(renderer, 10, 20, 40, 255);
+    SDL_RenderClear(renderer);
+}
+
+void MainMenuState::RenderImGui(Engine& engine)
+{
+    ImGuiIO& io = ImGui::GetIO();
+    
+    ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f),
+                                   ImGuiCond_Always,
+                                   ImVec2(0.5f, 0.5f));
+    ImGui::SetNextWindowSize(ImVec2(400, 500), ImGuiCond_FirstUseEver);
+    
+    ImGui::Begin("Main Menu", nullptr, ImGuiWindowFlags_NoTitleBar |
+                                       ImGuiWindowFlags_NoResize |
+                                       ImGuiWindowFlags_NoMove |
+                                       ImGuiWindowFlags_NoCollapse);
+    ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]);
+    ImGui::Dummy(ImVec2(0, 40));
+    ImGui::SetCursorPosX((ImGui::GetWindowSize().x - ImGui::CalcTextSize("kariokaEngine").x) * 0.5f);
+    ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, fadeAlpha), "kariokaEngine");
+
+    ImGui::Dummy(ImVec2(0, 40));    
+    if (ImGui::Button("Enter World", ImVec2(300, 30)))
+    {
+        SDL_Log("TODO: GamePlayState");
+    }
+    
+    ImGui::Dummy(ImVec2(0, 50));
+    if (ImGui::Button("Map Editor", ImVec2(300, 30)))
+    {
+        SDL_Log("TODO: MapEditorState");
+    }
+    
+    ImGui::Dummy(ImVec2(0, 60));
+    if (ImGui::Button("Asset Editor", ImVec2(300, 30)))
+    {
+        SDL_Log("TODO: AssetEditorState");
+    }
+    
+    ImGui::Dummy(ImVec2(0, 70));
+    if (ImGui::Button("Exit", ImVec2(300, 30)))
+    {
+        engine.StopRunning();
+    }
+    
+    ImGui::PopFont();
+    ImGui::End();
+}

+ 25 - 0
src/states/MainMenuState.h

@@ -0,0 +1,25 @@
+#ifndef __STATES__MAIN_MENU_STATE_H__
+#define __STATES__MAIN_MENU_STATE_H__
+
+#include "BaseState.h"
+#include <SDL2/SDL.h>
+#include <string>
+
+class MainMenuState : public BaseState
+{
+public:
+    void Enter(Engine& engine) override;
+    void Exit(Engine& engine) override;
+    void HandleEvents(Engine& engine, SDL_Event& e) override;
+    void Update(Engine& engine, float dt) override;
+    void Render(Engine& engine) override;
+    void RenderImGui(Engine& engine) override;
+
+private:
+    std::string versionText = "kariokaEngine v0.01 - Built with SDL2 + EnTT + ImGui + Python - (c) poloniumv 2026";
+
+    float fadeAlpha = 0.0f;
+
+};
+
+#endif

+ 91 - 0
src/states/StateManager.cpp

@@ -0,0 +1,91 @@
+#include "StateManager.h"
+#include "../Engine.h"
+
+void StateManager::PushState(std::unique_ptr<BaseState> state, Engine& engine)
+{
+    if (!states.empty()) { states.back()->Exit(engine); }
+    
+    states.push_back(std::move(state));
+    states.back()->Enter(engine);
+}
+
+void StateManager::PopState(Engine& engine)
+{
+    if (states.empty())
+    {
+        SDL_Log("StateManager::PopState() warning: PopState() called on empty stack");
+        return;
+    }
+    
+    states.back()->Exit(engine);
+    states.pop_back();
+    
+    if (!states.empty())
+    {
+        states.back()->Enter(engine);
+    }
+    else
+    {
+        SDL_Log("StateManager::PopState() warning: Stack is now empty after PopState");
+    }
+}
+
+void StateManager::ChangeState(std::unique_ptr<BaseState> state, Engine& engine)
+{
+    if (!states.empty())
+    {
+        states.back()->Exit(engine);
+        states.pop_back();
+    }
+    
+    states.push_back(std::move(state));
+    states.back()->Enter(engine);
+}
+
+void StateManager::HandleEvents(Engine& engine, SDL_Event& e)
+{
+    if (!states.empty())
+    {
+        states.back()->HandleEvents(engine, e);
+    }
+}
+
+void StateManager::Update(Engine& engine, float dt)
+{
+    if (!states.empty())
+    {
+        states.back()->Update(engine, dt);
+    }
+}
+
+void StateManager::Render(Engine& engine)
+{
+    // TODO: Check if this should be here
+    //for (const auto& state : states)
+    //{
+    //    state->Render(engine);
+    //}
+    
+    if (!states.empty())
+    {
+        states.back()->Render(engine);
+    }
+}
+
+void StateManager::RenderImGui(Engine& engine)
+{
+    if (!states.empty())
+    {
+        states.back()->RenderImGui(engine);
+    }
+}
+
+BaseState* StateManager::GetCurrentState() const
+{
+    if (!states.empty())
+    {
+        return states.back().get();
+    }
+    
+    return nullptr;
+}

+ 31 - 0
src/states/StateManager.h

@@ -0,0 +1,31 @@
+#ifndef __STATES__STATE_MANAGER_H__
+#define __STATES__STATE_MANAGER_H__
+
+#include <vector>
+#include <memory>
+#include <SDL2/SDL.h>
+#include "BaseState.h"
+
+class StateManager
+{
+public:
+    ~StateManager() = default;
+    
+    void PushState(std::unique_ptr<BaseState> state, Engine& engine);
+    void PopState(Engine& engine);
+    void ChangeState(std::unique_ptr<BaseState> state, Engine& engine);
+    
+    void HandleEvents(Engine& engine, SDL_Event& e);
+    void Update(Engine& engine, float dt);
+    void Render(Engine& engine);
+    void RenderImGui(Engine& engine);
+    
+    BaseState* GetCurrentState() const;
+    bool IsEmpty() const { return states.empty(); }
+
+private:
+    std::vector<std::unique_ptr<BaseState>> states;
+
+};
+
+#endif