GameState loads python scripts, anything but main.py is loaded as module

This commit is contained in:
Bobby Lucero 2024-04-26 12:45:07 -04:00
parent 581ed013f3
commit 12ed3d3cb1
9 changed files with 173 additions and 101 deletions

36
python/ball.py Normal file
View File

@ -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)

View File

@ -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)

View File

@ -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()));
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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) {

View File

@ -3,18 +3,28 @@
//
#include "GameState.h"
#include <fstream>
#include <sstream>
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<std::string, std::string> 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<std::string, std::string> GameState::readPythonFiles(const std::string &dir) {
std::unordered_map<std::string, std::string> 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<std::string, std::string> &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;
}
}
}

View File

@ -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<std::string, std::string> readPythonFiles(const std::string& dir);
void loadPythonModules(std::unordered_map<std::string, std::string>& fileContents);
void PreProcessScripts();
};