230 lines
7.4 KiB
C++
230 lines
7.4 KiB
C++
#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 <cstring>
|
|
|
|
/*
|
|
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<SDL_Point>& 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;
|
|
}
|