#include "SDL3/SDL_events.h" #include "SDL3/SDL_render.h" #include "SDL3/SDL_video.h" #include "SDL3/SDL.h" #include "SDL3/SDL_main.h" #include "SDL3_ttf/SDL_ttf.h" #include "Snake.h" #include "Apple.h" #include /* Compile through this command g++ main.cpp Snake.cpp Apple.cpp -o Game.exe -I./SDL3-3.3.6/x86_64-w64-mingw32/include -I./SDL3_ttf-3.2.2/x86_64-w64-mingw32/include -L. -lSDL3 -lSDL3_ttf */ #define winW 800 #define winH 600 using namespace std; const Uint64 frameDelay = 1000 / 60; // 60 FPS for Rendering //For Different States enum GameState { STATE_MENU, STATE_PLAYING, STATE_GAMEOVER }; GameState currentState = STATE_MENU; Uint64 initialInterval = 150; Uint64 moveInterval = initialInterval; const Uint64 minInterval = 70; // Hardest speed int score = 0; void RenderText(SDL_Renderer* renderer, TTF_Font* font, const char* text, int x, int y, SDL_Color color, bool center = false) { if (!font || !text || text[0] == '\0') return; SDL_Surface* surface = TTF_RenderText_Blended(font, text, 0, color); if (!surface) return; SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); if (texture) { float w = (float)surface->w; float h = (float)surface->h; float renderX = (float)x; if (center) { renderX = (float)(x - w / 2); } SDL_FRect dstRect = { renderX, (float)y, w, h }; SDL_RenderTexture(renderer, texture, nullptr, &dstRect); SDL_DestroyTexture(texture); } SDL_DestroySurface(surface); } // DrawMenu void DrawMenu(SDL_Renderer* renderer, TTF_Font* font, TTF_Font* smallfont, int _winW, int _winH) { SDL_Color white = {255, 255, 255, 255}; RenderText(renderer, font, "贪吃蛇大作战", _winW / 2, _winH / 2 - 50, white, true); SDL_Color gray = {200, 200, 200, 255}; RenderText(renderer, smallfont, "按任意键开始...", _winW / 2, _winH / 2 + 20, gray, true); } void DrawGameOver(SDL_Renderer* renderer, TTF_Font* font, TTF_Font* smallfont, int _winW, int _winH, int _score) { SDL_Color red = {255, 50, 50, 255}; RenderText(renderer, font, "游戏结束", _winW / 2, _winH / 2 - 80, red, true); char scoreBuf[64]; sprintf(scoreBuf, "最终得分: %d", _score); SDL_Color white = {255, 255, 255, 255}; RenderText(renderer, font, scoreBuf, _winW / 2, _winH / 2, white, true); SDL_Color gray = {200, 200, 200, 255}; RenderText(renderer, smallfont, "按R键重新开始...", _winW / 2, _winH / 2 + 120, gray, true); } void DrawScore(SDL_Renderer* renderer, TTF_Font* font, int _winW, int _winH, int _score){ char scoreBuf[64]; sprintf(scoreBuf, "得分: %d", _score); SDL_Color white = {255, 255, 255, 255}; RenderText(renderer, font, scoreBuf, 60, 0, white, true); } int main(int argc, char* argv[]) { bool x = SDL_Init(SDL_INIT_VIDEO); if(!x){ SDL_Log("SDL Init Error: %s\n", SDL_GetError()); return -1; } if (!TTF_Init()) { SDL_Log("TTF Init Error: %s", SDL_GetError()); return -1; } TTF_Font* font = TTF_OpenFont("./assets/fonts/NotoSerifCJKsc-Regular.otf", 48); TTF_Font* smallFont = TTF_OpenFont("./assets/fonts/NotoSerifCJKsc-Regular.otf", 24); if (!font || !smallFont) { SDL_Log("Font Load Error: %s", SDL_GetError()); } SDL_Window* mainWindow = SDL_CreateWindow( "Blockade: Snake Game", winW, winH, SDL_WINDOW_RESIZABLE ); if(!mainWindow){ SDL_Log("SDL Init Window Error: %s\n", SDL_GetError()); return -1; } SDL_Renderer* mainRender = SDL_CreateRenderer(mainWindow, nullptr); if(!mainRender){ SDL_Log("SDL Init Renderer Error: %s\n", SDL_GetError()); return -1; } Snake snake(winW, winH, 25); Apple apple(winW, winH, 25); Uint64 lastUpdateTime = SDL_GetTicks(); SDL_Event event; int running = 1; while(running){ Uint64 frameStart = SDL_GetTicks(); while(SDL_PollEvent(&event)){ if(event.type == SDL_EVENT_QUIT) running = 0; if (event.type == SDL_EVENT_KEY_DOWN) { if (event.key.key == SDLK_ESCAPE) running = false; if (currentState == STATE_MENU) { currentState = STATE_PLAYING; } else if (currentState == STATE_GAMEOVER) { if (event.key.key == SDLK_R) { snake.Reset(winW, winH); apple.Spawn(); score = 0; moveInterval = initialInterval; currentState = STATE_PLAYING; } } else if (currentState == STATE_PLAYING) { snake.HandleInput(event.key.key); } } } if(currentState == STATE_PLAYING){ Uint64 currentTime = SDL_GetTicks(); if (currentTime - lastUpdateTime >= moveInterval) { snake.Update(); SDL_Point head = snake.GetHeadPos(); SDL_Point applePos = apple.GetPos(); if (snake.CheckCollision(winW, winH)) { SDL_Log("Game Over! Restarting..."); currentState = STATE_GAMEOVER; } if(head.x == applePos.x && head.y == applePos.y){ snake.Grow(); apple.Spawn(); score += 10; if(moveInterval > minInterval){ moveInterval -= 5; //Everytime add Speeeeeeeeeeeeeed } //Check if Apple is inside the snake bool collision; do { collision = false; apple.Spawn(); SDL_Point newApplePos = apple.GetPos(); const std::deque& segments = snake.GetBody(); for (const auto& segment : segments) { if (newApplePos.x == segment.x && newApplePos.y == segment.y) { collision = true; break; } } } while (collision); } lastUpdateTime = currentTime; } } // Rendering // Clear screen to Black SDL_SetRenderDrawColor(mainRender, 0, 0, 0, 255); SDL_RenderClear(mainRender); if(currentState == STATE_MENU) { DrawMenu(mainRender, font, smallFont, winW, winH); } else if(currentState == STATE_PLAYING) { // Draw Snake snake.Render(mainRender); apple.Render(mainRender); DrawScore(mainRender, smallFont, winW, winH, score); } else if(currentState == STATE_GAMEOVER) { DrawGameOver(mainRender, font, smallFont, winW, winH, score); } // Present the backbuffer SDL_RenderPresent(mainRender); // Control FPS (Cap at 60 FPS) Uint64 frameTime = SDL_GetTicks() - frameStart; if (frameTime < frameDelay) { SDL_Delay((Uint32)(frameDelay - frameTime)); } } // Cleanup SDL_DestroyRenderer(mainRender); SDL_DestroyWindow(mainWindow); SDL_Quit(); return 0; }