/************************************************************** * SNAKE SDL + OpenGL Test Game to be ported to UWP * How to build (mingw mysys): * g++ -shared snake_ported.cpp -o snake_ported.dll \ -lmingw32 -lSDL2main -lSDL2 -lopengl32 -mwindows * ---------------------------------------------------------- **************************************************************/ #include #include #include #include #include #include #ifdef _WIN32 #define DLL_EXPORT __declspec(dllexport) #else #define DLL_EXPORT #endif static const int GRID_WIDTH = 20; static const int GRID_HEIGHT = 15; static const int WINDOW_SCALE = 32; struct Segment { int x, y; }; enum Direction { UP, DOWN, LEFT, RIGHT }; extern "C" { struct ControllerInput { float left_x; float left_y; float right_x; float right_y; bool button_a; bool button_b; }; static float g_left_x = 0.0f; static float g_left_y = 0.0f; static Direction g_dir = RIGHT; static bool g_running = false; // Update controller thumbstick values, map them to direction DLL_EXPORT void update_controller_input(ControllerInput input) { g_left_x = input.left_x; g_left_y = input.left_y; } // Forward declaration so external_entry can call it static int snake_main(); // This is the exported entry point that the host app calls // It calls our internal snake_main() DLL_EXPORT int external_entry() { return snake_main(); } } // The core Snake game logic static int snake_main() { //SDL_GL_GetCurrentContext() will be your best friend. if (!SDL_GL_GetCurrentContext()) { std::cerr << "No active GL context found. Exiting.\n"; return -1; } glViewport(0, 0, GRID_WIDTH * WINDOW_SCALE, GRID_HEIGHT * WINDOW_SCALE); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, (double)(GRID_WIDTH * WINDOW_SCALE), (double)(GRID_HEIGHT * WINDOW_SCALE), 0.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.f, 0.f, 0.f, 1.f); std::srand((unsigned int)std::time(nullptr)); std::vector snake; snake.push_back({GRID_WIDTH / 2, GRID_HEIGHT / 2}); g_dir = RIGHT; int foodX = std::rand() % GRID_WIDTH; int foodY = std::rand() % GRID_HEIGHT; const float MOVE_INTERVAL = 0.15f; float timer = 0.0f; g_running = true; Uint32 lastTicks = SDL_GetTicks(); int score = 0; while (g_running) { SDL_Event e; while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) { g_running = false; } else if (e.type == SDL_KEYDOWN) { switch (e.key.keysym.sym) { case SDLK_ESCAPE: g_running = false; break; case SDLK_UP: if (g_dir != DOWN) g_dir = UP; break; case SDLK_DOWN: if (g_dir != UP) g_dir = DOWN; break; case SDLK_LEFT: if (g_dir != RIGHT) g_dir = LEFT; break; case SDLK_RIGHT: if (g_dir != LEFT) g_dir = RIGHT; break; default: break; } } } // handle controller direction via left thumbstick { float deadzone = 0.5f; Direction nextDir = g_dir; if (g_left_y > deadzone && g_dir != DOWN) nextDir = UP; if (g_left_y < -deadzone && g_dir != UP) nextDir = DOWN; if (g_left_x > deadzone && g_dir != LEFT) nextDir = RIGHT; if (g_left_x < -deadzone && g_dir != RIGHT) nextDir = LEFT; g_dir = nextDir; } Uint32 currentTicks = SDL_GetTicks(); float deltaTime = (currentTicks - lastTicks) / 1000.0f; lastTicks = currentTicks; timer += deltaTime; if (timer >= MOVE_INTERVAL) { timer -= MOVE_INTERVAL; Segment head = snake.front(); switch (g_dir) { case UP: head.y -= 1; break; case DOWN: head.y += 1; break; case LEFT: head.x -= 1; break; case RIGHT: head.x += 1; break; } if (head.x < 0) head.x = GRID_WIDTH - 1; else if (head.x >= GRID_WIDTH) head.x = 0; if (head.y < 0) head.y = GRID_HEIGHT - 1; else if (head.y >= GRID_HEIGHT) head.y = 0; for (size_t i = 0; i < snake.size(); i++) { if (snake[i].x == head.x && snake[i].y == head.y) { std::cout << "Game Over! Final score: " << score << std::endl; g_running = false; break; } } if (g_running) { snake.insert(snake.begin(), head); if (head.x == foodX && head.y == foodY) { score++; foodX = std::rand() % GRID_WIDTH; foodY = std::rand() % GRID_HEIGHT; } else { snake.pop_back(); } } } if (!g_running) break; glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glColor3f(1.0f, 0.0f, 0.0f); glBegin(GL_QUADS); float fx = (float)foodX * WINDOW_SCALE; float fy = (float)foodY * WINDOW_SCALE; glVertex2f(fx, fy); glVertex2f(fx + WINDOW_SCALE, fy); glVertex2f(fx + WINDOW_SCALE, fy + WINDOW_SCALE); glVertex2f(fx, fy + WINDOW_SCALE); glEnd(); glColor3f(0.0f, 1.0f, 0.0f); glBegin(GL_QUADS); for (auto &seg : snake) { float sx = (float)seg.x * WINDOW_SCALE; float sy = (float)seg.y * WINDOW_SCALE; glVertex2f(sx, sy); glVertex2f(sx + WINDOW_SCALE, sy); glVertex2f(sx + WINDOW_SCALE, sy + WINDOW_SCALE); glVertex2f(sx, sy + WINDOW_SCALE); } glEnd(); // Since the host created the window & context, we swap via the current window SDL_GL_SwapWindow(SDL_GL_GetCurrentWindow()); } return 0; }