基于SDL的超级简单的飞机大战小游戏

This commit is contained in:
2025-05-27 19:46:49 +08:00
commit 752c53f36c
319 changed files with 95291 additions and 0 deletions

383
src/Game.cpp Normal file
View File

@ -0,0 +1,383 @@
//
// Created by sfd on 2025/4/9.
//
#include "Game.h"
#include <fstream>
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_mixer.h>
#include <SDL_ttf.h>
#include "Log.h"
#include "Scence/ScenceMain.h"
#include "Scence/ScenceTitle.h"
SDL_Point Game::renderTextCenter(std::string text, float posY, bool isTitle, SDL_Color color)
{
SDL_Surface *surface;
if (isTitle)
{
surface = TTF_RenderUTF8_Solid(titleFont, text.c_str(), color);
}else
{
surface = TTF_RenderUTF8_Solid(messageFont, text.c_str(), color);
}
SDL_Texture *textTexture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_Rect rect = {(getWindowWidth() - surface->w)/2, static_cast<int>((getWindowHeight() - surface->h) * posY), surface->w, surface->h};
SDL_RenderCopy(renderer, textTexture, nullptr, &rect);
SDL_FreeSurface(surface);
SDL_DestroyTexture(textTexture);
return {rect.x + rect.w, rect.y};
}
void Game::renderTextPos(std::string text, SDL_Point p, bool isLeft, SDL_Color color)
{
SDL_Surface* surface = TTF_RenderUTF8_Solid(messageFont, text.c_str(), color);
SDL_Texture *textTexture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_Rect rect;
if (isLeft)
{
rect = {p.x, p.y, surface->w, surface->h};
}else
{
rect = {getWindowWidth() - p.x - surface->w, p.y, surface->w, surface->h};
}
SDL_RenderCopy(renderer, textTexture, nullptr, &rect);
SDL_FreeSurface(surface);
SDL_DestroyTexture(textTexture);
}
void Game::saveData()
{
SYSLOG_INFO("saveing data into save.dat...");
std::ofstream file("save.dat");
if (!file.is_open())
{
SDL_Log("Failed to open save.dat file!");
return;
}
for (auto& entity : scoreBoard)
{
file << entity.first << " " << entity.second << std::endl;
}
file.close();
}
void Game::loadData()
{
SYSLOG_INFO("loading data from save.dat...");
std::ifstream file("save.dat");
if (!file.is_open())
{
SYSLOG_WARN("Failed to open save.dat file! Try to Create one...");
return;
}
scoreBoard.clear();
int score = 0;
std::string name;
while (file >> score >> name)
{
scoreBoard.insert({score, name});
}
file.close();
}
Game::Game()
{
SYSLOG_INFO("init game...");
}
void Game::insertIntoScoreBoard(int score, std::string name)
{
scoreBoard.insert({score, name});
if (scoreBoard.size() > 7)
{
scoreBoard.erase(--scoreBoard.end());
}
}
Game& Game::getInstance()
{
static Game instance;
return instance;
}
Game::~Game()
{
saveData();
clean();
}
void Game::init()
{
this->frameTime = 1000.f/FPS;
// Init SDL
SYSLOG_INFO("init SDL...");
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
SYSLOG_ERROR("SDL_Init Error: {}", SDL_GetError());
// SYSLOG_ERROR("SDL_Init Error: {}", SDL_GetError());
this->running = false;
}
SYSLOG_INFO("SDL_Init OK");
// Init window
SYSLOG_INFO("creating window...");
this->window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, this->windowWidth, this->windowHeight, SDL_WINDOW_SHOWN);
if (this->window == nullptr)
{
SYSLOG_ERROR("SDL_CreateWindow error: {}", SDL_GetError());
// SYSLOG_ERROR("SDL_CreateWindow error: {}", SDL_GetError());
this->running = false;
}
SYSLOG_INFO("window created");
// Init Renderer
SYSLOG_INFO("creating renderer...");
renderer = SDL_CreateRenderer(this->window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == nullptr)
{
SYSLOG_ERROR("SDL_CreateRenderer error: {}", SDL_GetError());
// SYSLOG_ERROR("SDL_CreateRenderer error: {}", SDL_GetError());
this->running = false;
}
SYSLOG_INFO("renderer created");
// setting logical dpi
SDL_RenderSetLogicalSize(renderer, this->windowWidth, this->windowHeight);
SYSLOG_INFO("init IMG_Init...");
if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG)
{
SYSLOG_ERROR("IMG_Init Error: {}", IMG_GetError());
// SYSLOG_ERROR("IMG_Init Error: {}", IMG_GetError());
this->running = false;
}
SYSLOG_INFO("SDL imgs init OK");
// ttf
SYSLOG_INFO("init SDL_ttf...");
if (TTF_Init() == -1)
{
SYSLOG_ERROR("TTF_Init: {}", TTF_GetError());
// SYSLOG_ERROR("TTF_Init: {}", TTF_GetError());
this->running = false;
}
SYSLOG_INFO("SDL_ttf init OK");
SYSLOG_INFO("init Mix_Init...");
// Init Audio
if (Mix_Init(MIX_INIT_MP3 | MIX_INIT_OGG) != (MIX_INIT_MP3 | MIX_INIT_OGG))
{
SYSLOG_ERROR("Mix_Init Error: {}", Mix_GetError());
// SYSLOG_ERROR("Mix_Init Error: {}", Mix_GetError());
this->running = false;
}
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
SYSLOG_ERROR("Mix_OpenAudio error: {}", Mix_GetError());
// SYSLOG_ERROR("Mix_OpenAudio error: {}", Mix_GetError());
}
SYSLOG_INFO("Audio Init OK");
Mix_AllocateChannels(32);
Mix_VolumeMusic(MIX_MAX_VOLUME / 4);
Mix_Volume(-1, MIX_MAX_VOLUME / 8);
SYSLOG_INFO("loading assets....");
nearBackground.texture = IMG_LoadTexture(renderer, "assets/Stars-A.png");
SDL_QueryTexture(nearBackground.texture, NULL, NULL, &nearBackground.width, &nearBackground.height);
nearBackground.width /= 2;
nearBackground.height /= 2;
nearBackground.speed = 30;
farBackground.texture = IMG_LoadTexture(renderer, "assets/Stars-B.png");
SDL_QueryTexture(farBackground.texture, NULL, NULL, &farBackground.width, &farBackground.height);
farBackground.width /= 2;
farBackground.height /= 2;
farBackground.speed = 20;
titleFont = TTF_OpenFont("assets/Minecraft.ttf", 64);
messageFont = TTF_OpenFont("assets/Minecraft.ttf", 32);
if (titleFont == nullptr | messageFont == nullptr)
{
SYSLOG_ERROR("TTF_OpenFont: {}", TTF_GetError());
this->running = false;
}
loadData();
SYSLOG_INFO("Game Init OK");
currentScence = new ScenceTitle();
currentScence->init();
}
void Game::clean()
{
SYSLOG_INFO("Clean resources...");
if (currentScence != nullptr)
{
currentScence->clean();
delete currentScence;
}
if (nearBackground.texture != nullptr)
{
SDL_DestroyTexture(nearBackground.texture);
}
if (farBackground.texture != nullptr)
{
SDL_DestroyTexture(farBackground.texture);
}
// Audio
Mix_CloseAudio();
Mix_Quit();
// Img
IMG_Quit();
// TTF
if (titleFont != nullptr)
{
TTF_CloseFont(titleFont);
}
if (messageFont != nullptr)
{
TTF_CloseFont(messageFont);
}
TTF_Quit();
SDL_DestroyRenderer(this->renderer);
SDL_DestroyWindow(this->window);
SDL_Quit();
SYSLOG_INFO("clean resource OK");
}
void Game::run()
{
while (running)
{
const auto startTime = SDL_GetTicks();
SDL_Event event;
handleEvents(event);
update(deltaTime);
render();
const auto endTime = SDL_GetTicks();
deltaTime = endTime - startTime;
auto diff = endTime - startTime;
if (diff < frameTime)
{
SDL_Delay(frameTime - diff);
deltaTime = frameTime / 1000.f;
}else
{
deltaTime = diff / 1000.f;
}
}
}
void Game::changeScence(Scence* scence)
{
if (currentScence != nullptr)
{
currentScence->clean();
delete currentScence;
}
currentScence = scence;
scence->init();
GAMELOG_TRACE("changing scence: {}", scence->name());
}
void Game::handleEvents(SDL_Event &e)
{
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
{
running = false;
}
if (e.type == SDL_KEYDOWN)
{
if (e.key.keysym.scancode == SDL_SCANCODE_F4)
{
isFullScreen = isFullScreen ? SDL_FALSE : SDL_TRUE;
SDL_SetWindowFullscreen(window, isFullScreen);
}
}
currentScence->handleEvents(e);
}
}
void Game::update(float deltatime)
{
bgUpdate(deltatime);
currentScence->update(deltatime);
}
void Game::render()
{
// clear Screen
SDL_RenderClear(renderer);
// render bg
renderBackground();
currentScence->render();
SDL_RenderPresent(renderer);
}
void Game::bgUpdate(float deltatime)
{
nearBackground.offset += nearBackground.speed * deltatime;
if (nearBackground.offset >= 0)
{
nearBackground.offset -= nearBackground.height;
}
farBackground.offset += farBackground.speed * deltatime;
if (farBackground.offset >= 1)
{
farBackground.offset -= farBackground.height;
}
}
void Game::renderBackground()
{
for (int posY = static_cast<int>(farBackground.offset);posY < getWindowHeight(); posY+= farBackground.height)
{
for (int posX = 0;posX < getWindowWidth(); posX += farBackground.width)
{
SDL_Rect rect = {posX, posY, farBackground.width, farBackground.height};
SDL_RenderCopy(renderer, farBackground.texture, nullptr, &rect);
}
}
for (int posY = static_cast<int>(nearBackground.offset);posY < getWindowHeight(); posY+= nearBackground.height)
{
for (int posX = 0;posX < getWindowWidth(); posX+= nearBackground.width)
{
SDL_Rect rect = {posX, posY, nearBackground.width, nearBackground.height};
SDL_RenderCopy(renderer, nearBackground.texture, nullptr, &rect);
}
}
}

89
src/Game.h Normal file
View File

@ -0,0 +1,89 @@
//
// Created by sfd on 2025/4/9.
//
#ifndef GAME_H
#define GAME_H
#include <map>
#include <SDL_render.h>
#include <SDL_ttf.h>
#include <string>
#include "Object.h"
#include "Scence/Scence.h"
class Game {
public:
static Game& getInstance();
~Game();
void init();
void run();
void changeScence(Scence* scence);
void handleEvents(SDL_Event &e);
void update(float deltatime);
void render();
void bgUpdate(float deltatime);
void renderBackground();
SDL_Window* getWindow() const { return window; }
SDL_Renderer* getRenderer() const { return renderer; }
int getWindowWidth() const { return windowWidth; }
int getWindowHeight() const { return windowHeight; }
//renderText
SDL_Point renderTextCenter(std::string text, float posY, bool isTitle = false, SDL_Color color = {255, 255, 255, 255});
void renderTextPos(std::string text, SDL_Point p, bool isLeft = true, SDL_Color color = {255, 255, 255, 255});
void insertIntoScoreBoard(int score, std::string name);
//getter
int getFinalScore() const { return finalScore; }
std::multimap<int, std::string, std::greater<int>> &getScoreBoard() { return scoreBoard; };
//setter
void setFinalScore(int finalScore) { this->finalScore = finalScore; }
void saveData();
void loadData();
private:
Game();
Game(const Game&) = delete;
Game& operator=(const Game&) = delete;
void clean();
bool running = true;
SDL_bool isFullScreen = SDL_FALSE;
Scence* currentScence = nullptr;
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
int windowWidth = 800;
int windowHeight = 600;
int finalScore = 0;
TTF_Font *titleFont = nullptr;
TTF_Font *messageFont = nullptr;
int FPS = 60;
float frameTime;
float deltaTime = 0; // s
Background nearBackground;
Background farBackground;
std::multimap<int, std::string, std::greater<int>> scoreBoard;
};
#endif //GAME_H

51
src/Log.cpp Normal file
View File

@ -0,0 +1,51 @@
//
// Created by sfd on 2025/4/15.
//
#include "Log.h"
#include <spdlog/sinks/stdout_color_sinks.h>
std::shared_ptr<spdlog::logger> Log::sys_logger = nullptr;
std::shared_ptr<spdlog::logger> Log::game_logger = nullptr;
void Log::init()
{
if (sys_logger == nullptr)
{
sys_logger = spdlog::stdout_color_mt("sys");
sys_logger->set_level(spdlog::level::level_enum::trace);
sys_logger->flush_on(spdlog::level::level_enum::trace);
sys_logger->set_pattern("%^[%n][%H:%M:%S]%v%$");
}
if (game_logger == nullptr)
{
game_logger = spdlog::stdout_color_mt("game");
game_logger->set_level(spdlog::level::level_enum::trace);
game_logger->flush_on(spdlog::level::level_enum::trace);
game_logger->set_pattern("%^[%n][%H:%M:%S]%v%$");
}
SYSLOG_INFO("Log system init complete!");
}
std::shared_ptr<spdlog::logger>& Log::GetSysLOG()
{
if (sys_logger == nullptr) {
throw std::runtime_error("Logger not initialized. Call GlobalLogger::Init() first.");
}
return sys_logger;
}
std::shared_ptr<spdlog::logger>& Log::GetGameLOG()
{
if (game_logger == nullptr)
{
throw std::runtime_error("Logger not initialized. Call GlobalLogger::Init() first.");
}
return game_logger;
}

41
src/Log.h Normal file
View File

@ -0,0 +1,41 @@
//
// Created by sfd on 2025/4/15.
//
#ifndef LOG_H
#define LOG_H
#include <memory>
#include <spdlog/logger.h>
#include <spdlog/sinks/stdout_color_sinks.h>
class Log {
public:
static void init();
static std::shared_ptr<spdlog::logger> &GetSysLOG();
static std::shared_ptr<spdlog::logger> &GetGameLOG();
private:
static std::shared_ptr<spdlog::logger> sys_logger;
static std::shared_ptr<spdlog::logger> game_logger;
};
#define SYSLOG_TRACE(format, ...) Log::GetSysLOG()->trace(FMT_STRING("{}" format), ":", ##__VA_ARGS__)
#define SYSLOG_DEBUG(format, ...) Log::GetSysLOG()->debug(FMT_STRING("{}" format), ":", ##__VA_ARGS__)
#define SYSLOG_INFO(format, ...) Log::GetSysLOG()->info(FMT_STRING("{}" format), ":", ##__VA_ARGS__)
#define SYSLOG_WARN(format, ...) Log::GetSysLOG()->warn(FMT_STRING("[{}:{}]:" format), __FILE__, __LINE__, ##__VA_ARGS__)
#define SYSLOG_ERROR(format, ...) Log::GetSysLOG()->error(FMT_STRING("[{}:{}]:" format), __FILE__, __LINE__, ##__VA_ARGS__)
#define SYSLOG_CRITICAL(format, ...) Log::GetSysLOG()->critical(FMT_STRING("[{}:{}]:" format), __FILE__, __LINE__, ##__VA_ARGS__)
#define GAMELOG_TRACE(format, ...) Log::GetGameLOG()->trace(FMT_STRING("{}" format), ":", ##__VA_ARGS__)
#define GAMELOG_DEBUG(format, ...) Log::GetGameLOG()->debug(FMT_STRING("{}" format), ":", ##__VA_ARGS__)
#define GAMELOG_INFO(format, ...) Log::GetGameLOG()->info(FMT_STRING("{}" format), ":", ##__VA_ARGS__)
#define GAMELOG_WARN(format, ...) Log::GetGameLOG()->warn(FMT_STRING("{}" format), ":", ##__VA_ARGS__)
#define GAMELOG_ERROR(format, ...) Log::GetGameLOG()->error(FMT_STRING("{}" format), ":", ##__VA_ARGS__)
#define GAMELOG_CRITICAL(format, ...) Log::GetGameLOG()->critical(FMT_STRING("{}" format), ":", ##__VA_ARGS__)
#endif //LOG_H

109
src/Object.h Normal file
View File

@ -0,0 +1,109 @@
//
// Created by sfd on 2025/4/9.
//
#ifndef OBJECT_H
#define OBJECT_H
enum class ItemType
{
Life,
Shield,
Time
};
struct Item
{
SDL_Texture* texture = nullptr;
SDL_FPoint position = {0, 0};
SDL_FPoint direction = { 0, 0};
int width = 0;
int height = 0;
int speed = 200;
int bounce = 3;
ItemType type = ItemType::Life;
};
struct Player
{
SDL_Texture* texture = nullptr;
SDL_FPoint position = { 0, 0 };
int width = 0;
int height = 0;
int speed = 300;
int health = 3;
Uint32 coolDown = 200;
Uint32 lastShootTime = 0;
};
struct Enemy
{
SDL_Texture* texture = nullptr;
SDL_FPoint position = { 0, 0 };
int width = 0;
int height = 0;
int speed = 200;
int health = 2;
Uint32 coolDown = 3000;
Uint32 lastShootTime = 0;
};
struct BulletPlayer
{
SDL_Texture* texture = nullptr;
SDL_FPoint position = { 0, 0 };
int width = 0;
int height = 0;
int speed = 400;
int damage = 1;
};
struct BulletEnemy
{
SDL_Texture* texture = nullptr;
SDL_FPoint position = { 0, 0 };
SDL_FPoint direction = { 0, 0 };
int width = 0;
int height = 0;
int speed = 300;
int damage = 1;
};
struct Explosion
{
SDL_Texture* texture = nullptr;
SDL_FPoint position = { 0, 0 };
int width = 0;
int height = 0;
int currentFrame = 0;
int totalFrames = 0;
Uint32 stratTime = 0;
Uint32 FPS = 10;
};
struct Background
{
SDL_Texture* texture = nullptr;
SDL_FPoint position = { 0, 0 };
float offset = 0;
int width = 0;
int height = 0;
int speed = 30;
};
#endif //OBJECT_H

11
src/Scence/Scence.cpp Normal file
View File

@ -0,0 +1,11 @@
//
// Created by sfd on 2025/4/9.
//
#include "Scence.h"
#include "../Game.h"
Scence::Scence(): game(Game::getInstance())
{
}

34
src/Scence/Scence.h Normal file
View File

@ -0,0 +1,34 @@
//
// Created by sfd on 2025/4/9.
//
#ifndef SCENCE_H
#define SCENCE_H
#include <SDL_events.h>
#include <string>
class Game;
class Scence {
public:
Scence();
virtual ~Scence() = default;
virtual void init() = 0;
virtual void update(float deltatime) = 0;
virtual void render() = 0;
virtual void clean() = 0;
virtual void handleEvents(SDL_Event &e) = 0;
public:
std::string name() { return this->thisName; }
protected:
Game& game;
std::string thisName;
};
#endif //SCENCE_H

162
src/Scence/ScenceEnd.cpp Normal file
View File

@ -0,0 +1,162 @@
//
// Created by sfd on 2025/4/14.
//
#include "ScenceEnd.h"
#include <string>
#include "ScenceMain.h"
#include "../Game.h"
#include "../Log.h"
void ScenceEnd::init()
{
thisName = "SenceEnd";
GAMELOG_TRACE("ScenceEnd Initializing...");
if (!SDL_IsTextInputActive())
{
SDL_StartTextInput();
}
if (!SDL_IsTextInputActive())
{
SYSLOG_ERROR("failed to start text input: {}", SDL_GetError());
}
}
void ScenceEnd::update(float deltatime)
{
blinkTimer += deltatime;
if (blinkTimer > blinkFrequency)
{
blinkTimer -= blinkFrequency;
}
}
void ScenceEnd::render()
{
if (isTyping)
{
renderPhase1();
}else
{
renderPhase2();
}
}
void ScenceEnd::clean()
{
}
void ScenceEnd::backspaceString()
{
if (name.empty())
return;
auto lastchar = name.back();
if ((lastchar & 0b10000000) == 0b10000000)
{
name.pop_back();
while ((name.back() & 0b11000000) != 0b11000000)
{
name.pop_back();
}
}
name.pop_back();
}
void ScenceEnd::handleEvents(SDL_Event& e)
{
if (isTyping)
{
if (e.type == SDL_TEXTINPUT)
{
name += e.text.text;
}
if (e.type == SDL_KEYDOWN)
{
if (e.key.keysym.scancode == SDL_SCANCODE_RETURN)
{
isTyping = false;
SDL_StopTextInput();
if (name.empty())
{
name = "匿名";
}
game.insertIntoScoreBoard(game.getFinalScore(), name);
}
if (e.key.keysym.scancode == SDL_SCANCODE_BACKSPACE)
{
if (name.length() > 0)
{
backspaceString();
}
}
}
}else
{
if (e.type == SDL_KEYDOWN)
{
if (e.key.keysym.scancode == SDL_SCANCODE_J)
{
GAMELOG_TRACE("restart game");
auto scenceMain = new ScenceMain();
game.changeScence(scenceMain);
}
}
}
}
void ScenceEnd::renderPhase1()
{
auto score = game.getFinalScore();
std::string scoretext = "你的得分是: " + std::to_string(score);
std::string gameOverText = "Game Over";
std::string typeInNameText = "请输入名字, 回车确认:";
game.renderTextCenter(gameOverText, 0.3f, true);
game.renderTextCenter(scoretext, 0.5f );
game.renderTextCenter(typeInNameText, 0.7f);
if (name != "")
{
SDL_Point p = game.renderTextCenter(name, 0.8f);
if (blinkTimer < 0.5f)
{
game.renderTextPos("_", p);
}
}else
{
if (blinkTimer < 0.5f)
{
game.renderTextCenter("_", 0.8f);
}
}
}
void ScenceEnd::renderPhase2()
{
game.renderTextCenter("得分榜", 0.1f, true);
auto posY = 0.25f * game.getWindowHeight();
int i = 1;
for (auto item : game.getScoreBoard())
{
std::string name =std::to_string(i++) + ". " + item.second;
std::string score= std::to_string(item.first);
game.renderTextPos(name, {100, static_cast<int>(posY)});
game.renderTextPos(score, {100, static_cast<int>(posY)}, false);
posY += 50 ;
}
if (blinkTimer < 0.5f)
{
game.renderTextCenter("Press J to Restart Game!", 0.9f);
}
}

36
src/Scence/ScenceEnd.h Normal file
View File

@ -0,0 +1,36 @@
//
// Created by sfd on 2025/4/14.
//
#ifndef SCENCEEND_H
#define SCENCEEND_H
#include <string>
#include "Scence.h"
class ScenceEnd : public Scence{
public:
void init() override;
void update(float deltatime) override;
void render() override;
void clean() override;
void backspaceString();
void handleEvents(SDL_Event& e) override;
private:
void renderPhase1();
void renderPhase2();
private:
bool isTyping = true;
float blinkFrequency = 1.0f;
float blinkTimer = 0;
std::string name = "";
};
#endif //SCENCEEND_H

708
src/Scence/ScenceMain.cpp Normal file
View File

@ -0,0 +1,708 @@
//
// Created by sfd on 2025/4/9.
//
#include "ScenceMain.h"
#include <SDL_image.h>
#include "ScenceEnd.h"
#include "ScenceTitle.h"
#include "../Game.h"
#include "../Log.h"
void ScenceMain::init()
{
GAMELOG_TRACE("ScenceMain Initializing...");
thisName = "ScenceMain";
// loading Music
bgm = Mix_LoadMUS("assets/Zedd,Alessia Cara - Stay.ogg");
if (bgm == nullptr)
{
SYSLOG_ERROR("Failed to load Music: {}" ,Mix_GetError());
}else
{
Mix_PlayMusic(bgm, -1);
}
// loading Font
scoreFont = TTF_OpenFont("assets/Minecraft.ttf", 24);
sounds["player_shoot"] = Mix_LoadWAV("assets/laser_shoot4.wav");
sounds["enemy_shoot"] = Mix_LoadWAV("assets/xs_laser.wav");
sounds["explosion"] = Mix_LoadWAV("assets/explosion11.wav");
sounds["hit"] = Mix_LoadWAV("assets/eff1.wav");
sounds["get_item"] = Mix_LoadWAV("assets/eff5.wav");
std::random_device rd;
gen = std::mt19937(rd());
rand = std::uniform_real_distribution<float>(0.f, 1.f);
// Player
GAMELOG_TRACE("ScenceMain loading assets...");
player.texture = IMG_LoadTexture(game.getRenderer(), "assets/SpaceShip.png");
SDL_QueryTexture(player.texture, nullptr, nullptr, &player.width, &player.height);
player.width /= 4;
player.height /= 4;
player.position.x = game.getWindowWidth() / 2 - player.width / 2;
player.position.y = game.getWindowHeight() - player.height - 10;
// Bullet Player
BulletPlayerTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/bullet.png");
SDL_QueryTexture(BulletPlayerTemplate.texture, nullptr, nullptr, &BulletPlayerTemplate.width, &BulletPlayerTemplate.height);
BulletPlayerTemplate.width /= 4;
BulletPlayerTemplate.height /= 4;
// Enemy
EnemyTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/insect-1.png");
SDL_QueryTexture(EnemyTemplate.texture, nullptr, nullptr, &EnemyTemplate.width, &EnemyTemplate.height);
EnemyTemplate.width /= 4;
EnemyTemplate.height /= 4;
// Bullet Enemy
BulletEnemyTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/bullet-1.png");
SDL_QueryTexture(BulletEnemyTemplate.texture, nullptr, nullptr, &BulletEnemyTemplate.width, &BulletEnemyTemplate.height);
BulletEnemyTemplate.width /= 4;
BulletEnemyTemplate.height /= 4;
ExplosionTexmplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/explosion.png");
SDL_QueryTexture(ExplosionTexmplate.texture, nullptr, nullptr, &ExplosionTexmplate.width, &ExplosionTexmplate.height);
ExplosionTexmplate.totalFrames = ExplosionTexmplate.width / ExplosionTexmplate.height;
ExplosionTexmplate.width = ExplosionTexmplate.height;
ItemLifeTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/bonus_life.png");
SDL_QueryTexture(ItemLifeTemplate.texture, nullptr, nullptr, &ItemLifeTemplate.width, &ItemLifeTemplate.height);
ItemLifeTemplate.width /= 4;
ItemLifeTemplate.height /= 4;
ItemLifeTemplate.type = ItemType::Life;
ItemShootSpeedTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/bonus_time.png");
SDL_QueryTexture(ItemShootSpeedTemplate.texture, nullptr, nullptr, &ItemShootSpeedTemplate.width, &ItemShootSpeedTemplate.height);
ItemShootSpeedTemplate.width /= 4;
ItemShootSpeedTemplate.height /= 4;
ItemShootSpeedTemplate.type = ItemType::Time;
uiHealth = IMG_LoadTexture(game.getRenderer(), "assets/Health UI Gold.png");
}
void ScenceMain::update(float deltatime)
{
if (!holdGame)
{
if (playerIsDead)
{
changeScenceDelay(deltatime, 3);
}else
{
keyboardControl(deltatime);
}
updatebulletPlayer(deltatime);
spawnEnemy();
updateEnemy(deltatime);
updateEnemyBullet(deltatime);
updatePlayer(deltatime);
updateExplosion(deltatime);
updateItems(deltatime);
}
}
void ScenceMain::updateExplosion(float deltatime)
{
auto currentTime = SDL_GetTicks();
for (auto it = explosions.begin(); it != explosions.end();)
{
auto explosion = *it;
explosion->currentFrame = (currentTime - explosion->stratTime) * explosion->FPS / 1000;
if (explosion->currentFrame > explosion->totalFrames)
{
delete explosion;
it = explosions.erase(it);
}else
{
++it;
}
}
}
void ScenceMain::renderbulletEnemy() const
{
for (auto bullet : bulletEnemies)
{
SDL_Rect rect = {static_cast<int>(bullet->position.x), static_cast<int>(bullet->position.y), bullet->width, bullet->height};
// SDL_RenderCopy(game.getRenderer(), bullet->texture, nullptr, &rect);
const float angle = atan2f(bullet->direction.y, bullet->direction.x) * 180.0f / M_PI - 90.0f;
SDL_RenderCopyEx(game.getRenderer(), bullet->texture, nullptr, &rect, angle, nullptr, SDL_FLIP_NONE);
}
}
void ScenceMain::render()
{
renderbulletPlayer();
renderbulletEnemy();
if (!playerIsDead)
{
const SDL_Rect rect = {static_cast<int>(player.position.x), static_cast<int>(player.position.y), player.width, player.height};
SDL_RenderCopy(game.getRenderer() ,player.texture, nullptr, &rect);
}
renderEnemy();
renderItem();
renderExplosion();
renderUI();
}
void ScenceMain::renderExplosion() const
{
for (auto explosion : explosions)
{
SDL_Rect src = {explosion->currentFrame * explosion->width, 0 , explosion->width, explosion->height};
SDL_Rect dst = {static_cast<int>(explosion->position.x), static_cast<int>(explosion->position.y), explosion->width, explosion->height};
SDL_RenderCopy(game.getRenderer(), explosion->texture, &src, &dst);
}
}
void ScenceMain::clean()
{
// Free Music;
if (bgm != nullptr)
{
Mix_HaltMusic();
Mix_FreeMusic(bgm);
}
for (auto& bullet : bulletsPlayer)
{
if (bullet != nullptr)
delete bullet;
}
bulletsPlayer.clear();
for (auto& enemy: enemies)
{
if (enemy != nullptr)
{
delete enemy;
}
}
enemies.clear();
for (auto& item: bulletEnemies)
{
if (item != nullptr)
{
delete item;
}
}
bulletEnemies.clear();
for (auto& item: explosions)
{
if (item != nullptr)
{
delete item;
}
}
explosions.clear();
for (auto& item: items)
{
if (item != nullptr)
{
delete item;
}
}
items.clear();
if (player.texture != nullptr)
{
SDL_DestroyTexture(player.texture);
player.texture = nullptr;
}
if (BulletEnemyTemplate.texture != nullptr)
{
SDL_DestroyTexture(BulletEnemyTemplate.texture);
BulletEnemyTemplate.texture = nullptr;
}
if (BulletPlayerTemplate.texture != nullptr)
{
SDL_DestroyTexture(BulletPlayerTemplate.texture);
BulletPlayerTemplate.texture = nullptr;
}
if (EnemyTemplate .texture != nullptr)
{
SDL_DestroyTexture(EnemyTemplate.texture);
EnemyTemplate.texture = nullptr;
}
if (ExplosionTexmplate.texture != nullptr)
{
SDL_DestroyTexture(ExplosionTexmplate.texture);
ExplosionTexmplate.texture = nullptr;
}
if (ItemLifeTemplate.texture != nullptr)
{
SDL_DestroyTexture(ItemLifeTemplate.texture);
}
for (auto sound : sounds)
{
if (sound.second != nullptr)
{
Mix_FreeChunk(sound.second);
}
}
sounds.clear();
if (uiHealth != nullptr)
{
SDL_DestroyTexture(uiHealth);
}
if (scoreFont != nullptr)
{
TTF_CloseFont(scoreFont);
}
}
void ScenceMain::handleEvents(SDL_Event& e)
{
if (e.type == SDL_KEYDOWN)
{
if (e.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
{
auto scenceTitle = new ScenceTitle();
game.changeScence(scenceTitle);
}
if (e.key.keysym.scancode == SDL_SCANCODE_P)
{
holdGame = !holdGame;
}
}
}
void ScenceMain::keyboardControl(const float deltatime)
{
const Uint8* state = SDL_GetKeyboardState(nullptr);
if (state[SDL_SCANCODE_W])
{
player.position.y -= deltatime * player.speed;
}
if (state[SDL_SCANCODE_S])
{
player.position.y += deltatime * player.speed;
}
if (state[SDL_SCANCODE_A])
{
player.position.x -= deltatime * player.speed;
}
if (state[SDL_SCANCODE_D])
{
player.position.x += deltatime * player.speed;
}
// Simple Check Collision
if (player.position.x < 0) { player.position.x = 0; }
if (player.position.y < 0) { player.position.y = 0; }
if (player.position.y > game.getWindowHeight() - player.height) { player.position.y = game.getWindowHeight() - player.height; }
if (player.position.x > game.getWindowWidth() - player.width) { player.position.x = game.getWindowWidth() - player.width; }
if (state[SDL_SCANCODE_J])
{
auto currentTime = SDL_GetTicks();
if (currentTime - player.coolDown > player.lastShootTime)
{
ShootPlayer();
player.lastShootTime = currentTime;
}
}
}
void ScenceMain::ShootPlayer()
{
auto bullet = new BulletPlayer(BulletPlayerTemplate);
bullet->position.x = player.position.x + player.width / 2 - bullet->width / 2;
bullet->position.y = player.position.y;
bulletsPlayer.push_back(bullet);
Mix_PlayChannel(-1, sounds["player_shoot"], 0);
}
void ScenceMain::updatebulletPlayer(float deltatime)
{
for (auto it = bulletsPlayer.begin(); it != bulletsPlayer.end(); )
{
auto bullet = *it;
bullet->position.y -= deltatime * bullet->speed;
if (bullet->position.y + bullet->height < 0)
{
delete bullet;
it = bulletsPlayer.erase(it);
}else
{
bool hit = false;
for (auto& enemy : enemies)
{
SDL_Rect enemyRect = {static_cast<int>(enemy->position.x), static_cast<int>(enemy->position.y), enemy->width, enemy->height};
SDL_Rect bulletRect = {static_cast<int>(bullet->position.x), static_cast<int>(bullet->position.y), bullet->width, bullet->height};
if (SDL_HasIntersection(&enemyRect, &bulletRect))
{
enemy->health -= bullet->damage;
Mix_PlayChannel(-1, sounds["hit"], 0);
delete bullet;
it = bulletsPlayer.erase(it);
hit = true;
break;
}
}
if (!hit)
{
++it;
}
}
}
}
void ScenceMain::renderbulletPlayer() const
{
for (auto & bullet : bulletsPlayer)
{
SDL_Rect rect = {static_cast<int>(bullet->position.x), static_cast<int>(bullet->position.y), bullet->width, bullet->height};
SDL_RenderCopy(game.getRenderer(), bullet->texture, nullptr, &rect);
}
}
void ScenceMain::spawnEnemy()
{
if (rand(gen) > 1 / 60.f)
{
return;
}
Enemy* enemy = new Enemy(EnemyTemplate);
enemy->position.x = rand(gen) * (game.getWindowWidth() - enemy->width);
enemy->position.y = - enemy->height;
enemies.push_back(enemy);
}
void ScenceMain::enemyExplode(Enemy* enemy)
{
score += 5;
auto currentTime = SDL_GetTicks();
auto explosion = new Explosion(ExplosionTexmplate);
explosion->position.x = enemy->position.x + enemy->width / 2 - explosion->width / 2;
explosion->position.y = enemy->position.y + enemy->height / 2 - explosion->height / 2;
explosion->stratTime = currentTime;
explosions.push_back(explosion);
Mix_PlayChannel(-1, sounds["explosion"], 0);
if (rand(gen) < 0.5f)
{
dropItem(enemy);
}
delete enemy;
}
void ScenceMain::updateEnemy(float deltatime)
{
for (auto it = enemies.begin(); it != enemies.end();)
{
auto currentTime = SDL_GetTicks();
auto enemy = *it;
enemy->position.y += deltatime * enemy->speed;
if (enemy->position.y > game.getWindowHeight())
{
delete enemy;
it = enemies.erase(it);
}else
{
if (enemy->health <= 0)
{
enemyExplode(enemy);
it = enemies.erase(it);
}
else
{
if (!playerIsDead)
{
if (currentTime - enemy->lastShootTime > enemy->coolDown)
{
shootEnemy(*enemy);
enemy->lastShootTime = currentTime;
}
}
++it;
}
}
}
}
void ScenceMain::renderEnemy() const
{
for (auto& enemy : enemies)
{
SDL_Rect rect = {static_cast<int>(enemy->position.x), static_cast<int>(enemy->position.y), enemy->width, enemy->height};
SDL_RenderCopy(game.getRenderer(), enemy->texture, nullptr, &rect);
}
}
void ScenceMain::shootEnemy(Enemy& enemy)
{
auto bullet = new BulletEnemy(BulletEnemyTemplate);
bullet->position.x = enemy.position.x + enemy.width / 2 - bullet->width / 2;
bullet->position.y = enemy.position.y + enemy.height / 2 - bullet->height / 2;
bullet->direction = getDirection(enemy);
bulletEnemies.push_back(bullet);
Mix_PlayChannel(-1, sounds["enemy_shoot"], 0);
}
SDL_FPoint ScenceMain::getDirection(const Enemy& enemy) const
{
auto x = (player.position.x + player.width /2) - (enemy.position.x + enemy.width / 2);
auto y = (player.position.y + player.height /2) - (enemy.position.y + enemy.height / 2);
const auto length = std::sqrt(x * x + y * y);
x /= length;
y /= length;
return SDL_FPoint{x, y};
}
void ScenceMain::updateEnemyBullet(float deltatime)
{
for (auto it = bulletEnemies.begin(); it != bulletEnemies.end(); )
{
auto bullet = *it;
bullet->position.x += bullet->speed * deltatime * bullet->direction.x;
bullet->position.y += bullet->speed * deltatime * bullet->direction.y;
if (bullet->position.y > game.getWindowHeight() ||
bullet->position.y + bullet->height < 0 ||
bullet->position.x + bullet->width < 0 ||
bullet->position.x > game.getWindowWidth())
{
delete bullet;
it = bulletEnemies.erase(it);
}else
{
SDL_Rect PlayerRect = {static_cast<int>(player.position.x), static_cast<int>(player.position.y), player.width, player.height};
SDL_Rect bulletRect = {static_cast<int>(bullet->position.x), static_cast<int>(bullet->position.y), bullet->width, bullet->height};
if (SDL_HasIntersection(&PlayerRect, &bulletRect) && !playerIsDead)
{
player.health -= bullet->damage;
Mix_PlayChannel(-1, sounds["hit"], 0);
delete bullet;
it = bulletEnemies.erase(it);
break;
}else
{
++it;
}
}
}
}
void ScenceMain::updatePlayer(float deltatime)
{
if (playerIsDead)
{
game.setFinalScore(score);
return;
}
if (player.health <= 0)
{
this->playerIsDead = true;
}
for (auto enemy : enemies)
{
SDL_Rect enemyRect = {static_cast<int>(enemy->position.x), static_cast<int>(enemy->position.y), enemy->width, enemy->height};
SDL_Rect playerRect = {static_cast<int>(player.position.x), static_cast<int>(player.position.y), player.width, player.height};
if (SDL_HasIntersection(&enemyRect, &playerRect))
{
enemy->health = 0;
player.health -= 2;
}
}
}
void ScenceMain::dropItem(Enemy *enemy)
{
auto rands = rand(gen);
Item *item;
if (rands < 0.3)
{
item = new Item(ItemLifeTemplate);
}
else if (rands < 0.7)
{
item = new Item(ItemShootSpeedTemplate);
}
else
{
item = new Item(ItemLifeTemplate);
}
item->position.x = enemy->position.x + enemy->width / 2 - item->width / 2;
item->position.y = enemy->position.y + enemy->height / 2 - item->height / 2;
float angle = rand(gen) * 2 * M_PI;
item->direction.x = cos(angle);
item->direction.y = sin(angle);
items.push_back(item);
}
void ScenceMain::updateItems(float deltatime)
{
for (auto it = items.begin(); it != items.end();)
{
auto item = *it;
item->position.x += item->direction.x * item->speed * deltatime;
item->position.y += item->direction.y * item->speed * deltatime;
if (item->bounce > 0)
{
if (item->position.x < 0 && item->direction.x < 0)
{
item->direction.x = -item->direction.x;
item->bounce--;
}
if (item->position.y < 0 && item->direction.y < 0)
{
item->direction.y = -item->direction.y;
item->bounce--;
}
if (item->position.x + item->width > game.getWindowWidth() && item->direction.x > 0)
{
item->direction.x = -item->direction.x;
item->bounce--;
}
if (item->position.y + item->height > game.getWindowHeight() && item->direction.y > 0)
{
item->direction.y = -item->direction.y;
item->bounce--;
}
}
if (item->direction.x + item->width < 0 ||
item->direction.x > game.getWindowWidth() ||
item->direction.y + item->height < 0 ||
item->direction.y > game.getWindowHeight())
{
delete item;
it = items.erase(it);
}else
{
SDL_Rect itemRect = {static_cast<int>(item->position.x), static_cast<int>(item->position.y), item->width, item->height};
SDL_Rect playerRect = {static_cast<int>(player.position.x), static_cast<int>(player.position.y), player.width, player.height};
if (SDL_HasIntersection(&itemRect, &playerRect))
{
playerGetItem(item);
delete item;
it = items.erase(it);
}else
{
++it;
}
}
}
}
void ScenceMain::playerGetItem(Item* item)
{
score += 10;
if (item->type == ItemType::Life)
{
player.health +=2;
}else if (item->type == ItemType::Time)
{
player.coolDown *= 0.9f;
}
Mix_PlayChannel(-1, sounds["get_item"], 0);
}
void ScenceMain::changeScenceDelay(float deltatime, float delay)
{
timerEnd += deltatime;
if (timerEnd >= delay)
{
auto scenceEnd = new ScenceEnd();
game.changeScence(scenceEnd);
}
}
void ScenceMain::renderItem()
{
for (auto item : items)
{
SDL_Rect rect = {static_cast<int>(item->position.x), static_cast<int>(item->position.y), item->width, item->height};
SDL_RenderCopy(game.getRenderer(), item->texture, nullptr, &rect);
}
}
void ScenceMain::renderUI()
{
int x = 10;
int y = 10;
int size = 32;
int offset = 40;
for (int i = 0; i < player.health; i++)
{
int ty = y;
if (i >= 9)
{
ty += offset * static_cast<int>(i / 9);
}
SDL_Rect rect = {x + i % 9 * offset, ty, size, size};
SDL_RenderCopy(game.getRenderer(), uiHealth, nullptr, &rect);
}
auto text = "SCORE: " + std::to_string(score);
SDL_Surface* surface = TTF_RenderUTF8_Solid(scoreFont, text.c_str(), SDL_Color{255, 255, 255, 255});
SDL_Texture* scoreTexture = SDL_CreateTextureFromSurface(game.getRenderer(), surface);
SDL_Rect rect = {game.getWindowWidth() - 180, y, surface->w, surface->h};
SDL_RenderCopy(game.getRenderer(), scoreTexture, nullptr, &rect);
SDL_DestroyTexture(scoreTexture);
SDL_FreeSurface(surface);
}

91
src/Scence/ScenceMain.h Normal file
View File

@ -0,0 +1,91 @@
//
// Created by sfd on 2025/4/9.
//
#ifndef SCENCEMAIN_H
#define SCENCEMAIN_H
#include <map>
#include <random>
#include <vector>
#include <SDL_mixer.h>
#include <SDL_ttf.h>
#include "../Object.h"
#include "Scence.h"
class ScenceMain : public Scence{
public:
void init() override;
void update(float deltatime) override;
void render() override;
void clean() override;
void handleEvents(SDL_Event &e) override;
private:
void keyboardControl(float deltatime);
void updateItems(float deltatime);
void updateEnemyBullet(float deltatime);
void updatePlayer(float deltatime);
void updateEnemy(float deltatime);
void updatebulletPlayer(float deltatime);
void updateExplosion(float deltatime);
void renderbulletEnemy() const;
void renderExplosion() const;
void renderbulletPlayer() const;
void renderEnemy() const;
void renderItem();
void renderUI();
void ShootPlayer();
void spawnEnemy();
void enemyExplode(Enemy* enemy);
void shootEnemy(Enemy& enemy);
SDL_FPoint getDirection(const Enemy& enemy) const;
void dropItem(Enemy* enemy);
void playerGetItem(Item* item);
void changeScenceDelay(float deltatime, float delay);
private:
std::mt19937 gen;
std::uniform_real_distribution<float> rand;
Mix_Music* bgm;
std::pmr::map<std::string, Mix_Chunk*> sounds;
SDL_Texture* uiHealth;
TTF_Font *scoreFont;
int score = 0;
bool holdGame = false;
Player player;
bool playerIsDead = false;
float timerEnd = 0;
BulletPlayer BulletPlayerTemplate;
Enemy EnemyTemplate;
BulletEnemy BulletEnemyTemplate;
Explosion ExplosionTexmplate;
Item ItemLifeTemplate;
Item ItemShootSpeedTemplate;
std::vector<BulletPlayer*> bulletsPlayer;
std::vector<Enemy*> enemies;
std::vector<BulletEnemy*> bulletEnemies;
std::vector<Explosion*> explosions;
std::vector<Item*> items;
};
#endif //SCENCEMAIN_H

View File

@ -0,0 +1,61 @@
//
// Created by sfd on 2025/4/13.
//
#include "ScenceTitle.h"
#include <SDL_mixer.h>
#include "ScenceMain.h"
#include "../Game.h"
#include "../Log.h"
void ScenceTitle::init()
{
GAMELOG_TRACE("ScenceTitle Initializing");
thisName = "ScenceTitle";
// 载入音乐并且播放
bgm = Mix_LoadMUS("assets/Zedd,Alessia Cara - Stay.ogg");
Mix_PlayMusic(bgm,-1);
GAMELOG_TRACE("ScenceTitle ready");
}
void ScenceTitle::update(float deltatime)
{
timer += deltatime;
if (timer > 1.5f)
{
timer -= 1.5f;
}
}
void ScenceTitle::render()
{
game.renderTextCenter("SPACE WAR", 0.35f, true, {255,0,0});
if (timer > 0.75f)
{
game.renderTextCenter("Press J to Start!", 0.65f);
}
}
void ScenceTitle::clean()
{
GAMELOG_TRACE("ScenceTitle Cleaning");
Mix_HaltMusic();
Mix_FreeMusic(bgm);
}
void ScenceTitle::handleEvents(SDL_Event& e)
{
if (e.type == SDL_KEYDOWN)
{
if (e.key.keysym.scancode == SDL_SCANCODE_J)
{
auto scenceMain = new ScenceMain();
game.changeScence(scenceMain);
}
}
}

28
src/Scence/ScenceTitle.h Normal file
View File

@ -0,0 +1,28 @@
//
// Created by sfd on 2025/4/13.
//
#ifndef SCENCETITLE_H
#define SCENCETITLE_H
#include <SDL_mixer.h>
#include "Scence.h"
class ScenceTitle : public Scence {
public:
void init() override;
void update(float deltatime) override;
void render()override;
void clean()override;
void handleEvents(SDL_Event &e)override;
private:
Mix_Music *bgm;
float timer = 0;
};
#endif //SCENCETITLE_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

BIN
src/assets/Minecraft.ttf Normal file

Binary file not shown.

BIN
src/assets/SpaceShip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
src/assets/Stars-A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
src/assets/Stars-B.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

BIN
src/assets/bonus_life.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
src/assets/bonus_shield.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
src/assets/bonus_time.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
src/assets/bullet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
src/assets/eff1.wav Normal file

Binary file not shown.

BIN
src/assets/eff5.wav Normal file

Binary file not shown.

BIN
src/assets/explosion.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/assets/explosion11.wav Normal file

Binary file not shown.

BIN
src/assets/insect-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/insect-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
src/assets/laser-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/assets/laser_shoot4.wav Normal file

Binary file not shown.

BIN
src/assets/xs_laser.wav Normal file

Binary file not shown.

16
src/main.cpp Normal file
View File

@ -0,0 +1,16 @@
#include <SDL.h>
#include "Game.h"
#include "Log.h"
int main(int ,char **)
{
Log::init();
Game &game = Game::getInstance();
game.init();
game.run();
return 0;
}