diff --git a/python/ball.py b/python/ball.py new file mode 100644 index 0000000..8582f71 --- /dev/null +++ b/python/ball.py @@ -0,0 +1,36 @@ +class ball: + def __init__(self): + self.x = rnd(10, 100) + self.y = rnd(10, 100) + self.velX = rnd(-5,5) + self.velY = rnd(-5,5) + self.color = rnd(0,64) + + def draw(self): + + self.x += self.velX; + self.y += self.velY; + + if self.x < 0: + self.velX = -self.velX; + self.x = 0 + linecol = rnd(0,64) + if self.y < 0: + self.velY = -self.velY; + self.y = 0 + linecol = rnd(0,64) + + if self.x > 360: + self.velX = -self.velX; + self.x = 360 + linecol = rnd(0,64) + + if self.y > 203: + self.velY = -self.velY; + self.y = 203 + linecol = rnd(0,64) + + + circle(self.x, self.y, 3, self.color) + + diff --git a/python/main.py b/python/main.py index f8423a4..5017374 100644 --- a/python/main.py +++ b/python/main.py @@ -1,3 +1,5 @@ +import ball + linecol = 15 def draw_line(x0, y0, x1, y1, c): dx = abs(x1 - x0) @@ -5,7 +7,7 @@ def draw_line(x0, y0, x1, y1, c): sx = 1 if x0 < x1 else -1 sy = 1 if y0 < y1 else -1 err = dx - dy - + while x0 != x1 or y0 != y1: pixel(x0, y0, c) e2 = 2 * err @@ -17,58 +19,10 @@ def draw_line(x0, y0, x1, y1, c): y0 += sy -class ball: - def __init__(self): - self.x = rnd(10, 100) - self.y = rnd(10, 100) - self.velX = rnd(-5,5) - self.velY = rnd(-5,5) - self.color = rnd(0,64) - - def draw(self): - global linecol - self.x += self.velX; - self.y += self.velY; - - if self.x < 0: - self.velX = -self.velX; - self.x = 0 - linecol = rnd(0,64) - if self.y < 0: - self.velY = -self.velY; - self.y = 0 - linecol = rnd(0,64) - - if self.x > 360: - self.velX = -self.velX; - self.x = 360 - linecol = rnd(0,64) - - if self.y > 203: - self.velY = -self.velY; - self.y = 203 - linecol = rnd(0,64) - - - circle(self.x, self.y, 3, self.color) - -# i = 0 -# def update(): - # global i - # i += 1 - # #clear(43) - # x = sin(i * 0.071234) * 50 + (360 / 2) - # y = sin(i * 0.0236) * 20 + (203 / 2) - # circle(x + sin(i * 0.05) * 30, y + cos(i * 0.05) * 30, 5, 15) - # circle(x + sin(i * 0.05) * 30, y + cos(i * 0.05) * 30, 3, 11) - # circle(x + -sin(i * 0.05) * 30, y + -cos(i * 0.05) * 30, 5, 17) - # circle(x + -sin(i * 0.05) * 30, y + -cos(i * 0.05) * 30, 3, 19) - - balls = [] for i in range(2): - balls.append(ball()) + balls.append(ball.ball()) counter = 0 @@ -81,23 +35,25 @@ def update(): for ball in balls: ball.draw() - + for i in range(64): for j in range(20): if(i == 0 or i == 63 or j == 0 or j == 20): pixel(i * 4, j, i) - pixel(i * 4 + 1, j, i) - pixel(i * 4 + 2, j, i) - pixel(i * 4 + 3, j, i) + pixel(i * 4 + 1, j, i) + pixel(i * 4 + 2, j, i) + pixel(i * 4 + 3, j, i) else: pixel(i * 4, j, 0) - pixel(i * 4 + 1, j, 0) - pixel(i * 4 + 2, j, 0) - pixel(i * 4 + 3, j, 0) + pixel(i * 4 + 1, j, 0) + pixel(i * 4 + 2, j, 0) + pixel(i * 4 + 3, j, 0) t = "Hello from python FPS:" + str(fps()) + " X:" + str(mouseX) + " Y:" + str(mouseY) for i in range(len(t)): text(t[i], 4 + (i * 7), 4, 20 + i) - + circle(mouseX, mouseY, 10, counter % 64) +clear(0) +text("test", 2,2, 32) \ No newline at end of file diff --git a/src/Graphics/Graphics.cpp b/src/Graphics/Graphics.cpp index 024bca8..b70e872 100644 --- a/src/Graphics/Graphics.cpp +++ b/src/Graphics/Graphics.cpp @@ -22,15 +22,11 @@ Graphics::Graphics(int screenWidth, int screenHeight, int startupScale) : m_scre m_virtualScreenLocalBounds = {0.0f, 0.0f, (float)m_virtualScreen.texture.width, -(float)m_virtualScreen.texture.height }; m_virtualScreenWindowBounds = {0.0f, 0.0f, (float)m_windowWidth, (float)m_windowHeight}; - updateFunction = nullptr; calculateScreenPositionInWindow(); } void Graphics::draw(StateManager* stateManager) { -// vm->builtins->attr().set("mouseX", pkpy::py_var(vm, mouseX())); -// vm->builtins->attr().set("mouseY", pkpy::py_var(vm, mouseY())); - m_windowShouldClose = WindowShouldClose(); if (IsWindowResized()) { @@ -179,13 +175,6 @@ void Graphics::Text(std::string s, int x, int y, int paletteIndex) { DrawText(s.c_str(), x, y, 5, Palette[paletteIndex]); } -void Graphics::searchForDrawFunc(pkpy::VM* vm) { - updateFunction = vm->eval("update"); - if(updateFunction == nullptr){ - std::cout << "Can't find update function" << std::endl; - } -} - void Graphics::beginDraw() { BeginTextureMode(m_virtualScreen); } @@ -194,6 +183,11 @@ void Graphics::endDraw() { EndTextureMode(); } +void Graphics::updateVMMouse(pkpy::VM* vm) { + vm->builtins->attr().set("mouseX", pkpy::py_var(vm, mouseX())); + vm->builtins->attr().set("mouseY", pkpy::py_var(vm, mouseY())); +} + diff --git a/src/Graphics/Graphics.h b/src/Graphics/Graphics.h index c279af6..fdf7553 100644 --- a/src/Graphics/Graphics.h +++ b/src/Graphics/Graphics.h @@ -21,7 +21,6 @@ private: Vector2 m_origin; // position of rect texture on window Rectangle m_virtualScreenLocalBounds; // virtual screen bounds RenderTexture2D m_virtualScreen; // actual pixel screen - pkpy::PyObject* updateFunction; private: @@ -47,6 +46,8 @@ public: void beginDraw(); void endDraw(); + void updateVMMouse(pkpy::VM* vm); + void loadPalette(std::string path); int mouseX(); int mouseY(); @@ -54,9 +55,6 @@ public: void bindMethods(pkpy::VM* vm); - void searchForDrawFunc(pkpy::VM* vm); - - void Clear(int paletteIndex); void Pixel(int x, int y, int paletteIndex); void Circle(int x, int y, int radius, int paletteIndex); diff --git a/src/Pycron.cpp b/src/Pycron.cpp index 221fc79..d006034 100644 --- a/src/Pycron.cpp +++ b/src/Pycron.cpp @@ -9,6 +9,7 @@ #include "Utilities.h" #include "Graphics/Graphics.h" +std::string Pycron::PythonDirectory = "./python"; std::string loadFileToString(const std::string& filename) { std::ifstream file(filename); // Open the file @@ -24,30 +25,19 @@ std::string loadFileToString(const std::string& filename) { Pycron::Pycron() { SetTraceLogLevel(LOG_ERROR); - - m_graphics = new Graphics{virtualScreenWidth, virtualScreenHeight, initialScale}; - m_graphics->loadPalette("../resources/palette2.hex"); - - m_stateManager = new StateManager(this); - m_vm = new pkpy::VM(); bindMethods(); + m_graphics = new Graphics{virtualScreenWidth, virtualScreenHeight, initialScale}; + m_graphics->loadPalette("../resources/palette2.hex"); m_graphics->bindMethods(m_vm); + m_graphics->updateVMMouse(m_vm); + m_stateManager = new StateManager(this); + + std::string python = loadFileToString("../python/main.py"); - m_graphics->beginDraw(); - m_graphics->Clear(0); - try { - pkpy::CodeObject_ code = m_vm->compile(python, "main.py", pkpy::EXEC_MODE, false); - m_vm->_exec(code, m_vm->_main); - m_graphics->searchForDrawFunc(m_vm); - }catch (pkpy::Exception e) { - std::cout << e.summary() << std::endl; - } - m_graphics->endDraw(); - } Pycron::~Pycron(){ @@ -64,6 +54,7 @@ void Pycron::StartGameLoop() { if (IsKeyPressed(KEY_F)) { m_graphics->toggleFullScreen(); } + m_graphics->updateVMMouse(m_vm); m_graphics->draw(this->m_stateManager); } } diff --git a/src/Pycron.h b/src/Pycron.h index 9ea7c79..7b2ec44 100644 --- a/src/Pycron.h +++ b/src/Pycron.h @@ -8,16 +8,21 @@ class Graphics; class StateManager; class Pycron { +public: + static std::string PythonDirectory; private: const int virtualScreenWidth = 360; const int virtualScreenHeight = 203; const int initialScale = 3; - Graphics* m_graphics; StateManager* m_stateManager; - pkpy::VM* m_vm; public: + + pkpy::VM* m_vm; + Graphics* m_graphics; + + Pycron(); ~Pycron(); diff --git a/src/StateManager.cpp b/src/StateManager.cpp index 745b345..7ced653 100644 --- a/src/StateManager.cpp +++ b/src/StateManager.cpp @@ -5,7 +5,7 @@ #include "StateManager.h" StateManager::StateManager(Pycron *pycron) : m_pycron(pycron){ - m_gameState = new GameState(this); + m_gameState = new GameState(this, m_pycron->m_vm); RequestStateChange(GAME); } @@ -19,9 +19,15 @@ void StateManager::RequestStateChange(StateManager::StateType state) { } if(m_currentState){ - m_currentState->OnEnter(); + // Game state needs ability to draw during code loading + if(state == GAME){ + m_pycron->m_graphics->beginDraw(); + m_currentState->OnEnter(); + m_pycron->m_graphics->endDraw(); + }else{ + m_currentState->OnEnter(); + } } - } void StateManager::Draw(Graphics *graphics) { diff --git a/src/States/GameState.cpp b/src/States/GameState.cpp index 99c07a3..266b852 100644 --- a/src/States/GameState.cpp +++ b/src/States/GameState.cpp @@ -3,18 +3,28 @@ // #include "GameState.h" +#include +#include -GameState::GameState(StateManager *stateManager) : State(stateManager) { - +GameState::GameState(StateManager *stateManager, pkpy::VM* vm) : State(stateManager), m_vm(vm){ + m_updateFunction = nullptr; + m_previousError = ""; } - void GameState::Draw(Graphics *graphics) { - graphics->Text("Test", 10, 10, 10); + try{ + if(m_updateFunction != nullptr){ + m_vm->call(m_updateFunction); + } + } catch(pkpy::Exception e){ + m_previousError = e.summary(); + std::cout << e.summary() << "\n"; + m_errorThrown = true; + } } void GameState::OnEnter() { - //TODO: Python preprocess scripts + PreProcessScripts(); } void GameState::OnExit() { @@ -25,3 +35,63 @@ void GameState::onKeyPressed(int key) { } +void GameState::PreProcessScripts() { + m_previousError = ""; + m_errorThrown = false; + std::unordered_map pythonSources = readPythonFiles(Pycron::PythonDirectory); + loadPythonModules(pythonSources); + std::string main = pythonSources[MAIN_FILE]; + + pkpy::CodeObject_ code = m_vm->compile(main, MAIN_FILE, pkpy::EXEC_MODE, false); + try{ + m_vm->_exec(code, m_vm->_main); + m_updateFunction = m_vm->eval("update"); +// if(m_updateFunction == nullptr){ +// m_previousError = ""; +// } + }catch(pkpy::Exception e){ + m_previousError = e.summary(); + std::cout << e.summary() << "\n"; + m_errorThrown = true; + } +} + +std::unordered_map GameState::readPythonFiles(const std::string &dir) { + std::unordered_map fileContents; + + for (const auto& entry : std::filesystem::directory_iterator(dir)) { + if (entry.is_regular_file()) { + std::ifstream file(entry.path()); + std::filesystem::path path(entry.path()); + std::string fileName = path.filename().string(); + if (file.is_open()) { + std::stringstream buffer; + buffer << file.rdbuf(); + fileContents[fileName] = buffer.str(); + file.close(); + } else { + std::cerr << "Error opening file: " << entry.path() << std::endl; + } + } + } + + return fileContents; +} + +void GameState::loadPythonModules(std::unordered_map &fileContents) { + for(const auto& pair : fileContents){ + try{ + if(pair.first != MAIN_FILE){ + size_t pos = pair.first.find_last_of("."); + if(pos == std::string::npos || pos == 0){ + throw pkpy::Exception("Invalid file name"); + } + std::string moduleName = pair.first.substr(0, pos); + m_vm->_lazy_modules[moduleName.c_str()] = pair.second; + } + }catch(pkpy::Exception e){ + std::cout << e.summary() << std::endl; + } + } +} + diff --git a/src/States/GameState.h b/src/States/GameState.h index d046add..332d78b 100644 --- a/src/States/GameState.h +++ b/src/States/GameState.h @@ -3,11 +3,27 @@ class GameState : public State { public: - GameState(StateManager* stateManager); + + const std::string MAIN_FILE = "main.py"; + std::string m_previousError; + bool m_errorThrown = false; + + GameState(StateManager* stateManager, pkpy::VM* vm); void Draw(Graphics* graphics) override; void OnEnter() override; void OnExit() override; void onKeyPressed(int key) override; + +private: + + pkpy::VM* m_vm; + pkpy::PyObject* m_updateFunction; + + + std::unordered_map readPythonFiles(const std::string& dir); + void loadPythonModules(std::unordered_map& fileContents); + void PreProcessScripts(); + };