Compare commits
4 Commits
4d3300e111
...
83ed7e5190
| Author | SHA1 | Date | |
|---|---|---|---|
| 83ed7e5190 | |||
| 61390e37c0 | |||
| d2718ca045 | |||
| c5ea2d8993 |
4
Makefile
4
Makefile
@ -21,8 +21,8 @@ else
|
||||
LINKER_FLAGS += -fsanitize=address,undefined -g -Og -rdynamic
|
||||
endif
|
||||
|
||||
CXX_FLAGS += $(shell pkg-config sdl2 SDL2_image --cflags)
|
||||
LINKER_FLAGS += $(shell pkg-config sdl2 SDL2_image --libs)
|
||||
CXX_FLAGS += $(shell pkg-config sdl2 SDL2_image SDL2_ttf --cflags)
|
||||
LINKER_FLAGS += $(shell pkg-config sdl2 SDL2_image SDL2_ttf --libs)
|
||||
|
||||
build/%.o: %.cpp $(wildcard *.hpp)
|
||||
@mkdir -p `dirname $@`
|
||||
|
||||
@ -1,25 +1,25 @@
|
||||
#ifndef ARROW_HPP
|
||||
#define ARROW_HPP
|
||||
|
||||
#include <memory>
|
||||
#include "GameRenderer.hpp"
|
||||
#include "Sprite.hpp"
|
||||
|
||||
class Arrow
|
||||
{
|
||||
private:
|
||||
public:
|
||||
double x;
|
||||
double y;
|
||||
double angle;
|
||||
|
||||
static constexpr int speed = 5;
|
||||
|
||||
public:
|
||||
Arrow(double x, double y, double angle) : x(x), y(y), angle(angle) {}
|
||||
|
||||
void draw(const GameRenderer *renderer, const Sprite &sprite, double player_x, double player_y) const;
|
||||
|
||||
void update();
|
||||
|
||||
bool operator==(const Arrow &other) const = default;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
28
src/Color.hpp
Normal file
28
src/Color.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef COLOR_HPP
|
||||
#define COLOR_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
typedef SDL_Color Color;
|
||||
|
||||
inline Color rgb(uint8_t red, uint8_t green, uint8_t blue)
|
||||
{
|
||||
SDL_Color color;
|
||||
color.r = red;
|
||||
color.g = green;
|
||||
color.b = blue;
|
||||
color.a = 255;
|
||||
return color;
|
||||
}
|
||||
|
||||
inline Color rgba(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
|
||||
{
|
||||
SDL_Color color;
|
||||
color.r = red;
|
||||
color.g = green;
|
||||
color.b = blue;
|
||||
color.a = alpha;
|
||||
return color;
|
||||
}
|
||||
|
||||
#endif
|
||||
48
src/Game.cpp
48
src/Game.cpp
@ -1,8 +1,10 @@
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include "GameRenderer.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Game.hpp"
|
||||
#include "Color.hpp"
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
@ -10,6 +12,9 @@ Game::Game() : renderer("Zombo Shooter", 800, 450), player(&renderer), map(&rend
|
||||
{
|
||||
arrow_sprite = renderer.load_sprite("./assets/arrow.png", 22, 8);
|
||||
zombo_sprite = renderer.load_sprite("./assets/zombo.png", 40, 40);
|
||||
gold_sprite = renderer.load_sprite("./assets/gold.png", 30, 20);
|
||||
|
||||
font = renderer.load_font("./assets/Jersey20-Regular.ttf", 26);
|
||||
}
|
||||
|
||||
void Game::update(std::stop_token stop_token)
|
||||
@ -25,8 +30,40 @@ void Game::update(std::stop_token stop_token)
|
||||
arrow.update();
|
||||
}
|
||||
|
||||
// Remove arrows that are out of bounds
|
||||
std::erase_if(arrows, [this](const Arrow &arrow) {
|
||||
if (arrow.x < map.tile_offset_x * map.tile_size || arrow.y < map.tile_offset_x * map.tile_size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
arrow.x > (double)map.tiles.size() * map.tile_size + map.tile_offset_x ||
|
||||
arrow.y > (double)map.tiles[0].size() * map.tile_size + map.tile_offset_y
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
for (Zombo &zombo : zombos) {
|
||||
zombo.update(player.x, player.y);
|
||||
|
||||
// Check collision between zombos and arrows
|
||||
for (Arrow &arrow : arrows) {
|
||||
if (
|
||||
arrow.x > zombo.x && arrow.x < zombo.x + zombo_sprite->width &&
|
||||
arrow.y > zombo.y && arrow.y < zombo.y + zombo_sprite->height
|
||||
) {
|
||||
std::erase(arrows, arrow);
|
||||
|
||||
zombo.hp--;
|
||||
if (zombo.hp == 0) {
|
||||
std::erase(zombos, zombo);
|
||||
player.gold += 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ticks % 100 == 0 && zombos.size() < 20) {
|
||||
@ -47,7 +84,7 @@ void Game::draw()
|
||||
{
|
||||
const std::lock_guard lock(game_mutex);
|
||||
|
||||
renderer.clear_screen(0x80, 0x40, 0xFF, 0xFF);
|
||||
renderer.clear_screen(rgb(0x80, 0x40, 0xFF));
|
||||
|
||||
map.draw(player.x, player.y);
|
||||
|
||||
@ -61,6 +98,12 @@ void Game::draw()
|
||||
|
||||
player.draw();
|
||||
|
||||
renderer.draw_sprite(*gold_sprite, renderer.screen_width - 50, 10);
|
||||
|
||||
int text_width = renderer.get_text_width(font, std::to_string(player.gold));
|
||||
renderer.draw_text(font, std::to_string(player.gold), renderer.screen_width - gold_sprite->width - text_width - 28, 10, rgb(0, 0, 0));
|
||||
renderer.draw_text(font, std::to_string(player.gold), renderer.screen_width - gold_sprite->width - text_width - 30, 8, rgb(255, 255, 255));
|
||||
|
||||
renderer.flush();
|
||||
}
|
||||
|
||||
@ -114,8 +157,9 @@ void Game::run()
|
||||
}
|
||||
}
|
||||
|
||||
if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == 1) {
|
||||
if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == 1 && player.shooting_cooldown == 0) {
|
||||
arrows.emplace_back(player.x, player.y, player.angle);
|
||||
player.shooting_cooldown = 40;
|
||||
}
|
||||
|
||||
int mouse_x, mouse_y;
|
||||
|
||||
@ -17,7 +17,8 @@ private:
|
||||
Map map;
|
||||
std::vector<Arrow> arrows;
|
||||
std::vector<Zombo> zombos;
|
||||
std::unique_ptr<Sprite> arrow_sprite, zombo_sprite;
|
||||
std::unique_ptr<Sprite> arrow_sprite, zombo_sprite, gold_sprite;
|
||||
Font *font;
|
||||
|
||||
std::mutex game_mutex;
|
||||
unsigned int ticks = 0;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include <SDL2/SDL_image.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include "Sprite.hpp"
|
||||
@ -12,6 +13,11 @@ GameRenderer::GameRenderer(const std::string &title, const int screen_width, con
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (TTF_Init() < 0) {
|
||||
std::cerr << "Unable to initialize SDL_ttf" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
|
||||
|
||||
window = SDL_CreateWindow(
|
||||
@ -40,6 +46,26 @@ GameRenderer::~GameRenderer()
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
|
||||
Font *GameRenderer::load_font(const std::string &file, int font_size)
|
||||
{
|
||||
return TTF_OpenFont(file.c_str(), font_size);
|
||||
}
|
||||
|
||||
int GameRenderer::get_text_width(Font *font, const std::string &text) const
|
||||
{
|
||||
SDL_Surface *surface = TTF_RenderText_Solid(font, text.c_str(), rgb(0, 0, 0));
|
||||
return surface->w;
|
||||
}
|
||||
|
||||
void GameRenderer::draw_text(Font *font, const std::string &text, int x, int y, Color color) const
|
||||
{
|
||||
SDL_Surface *surface = TTF_RenderText_Solid(font, text.c_str(), color);
|
||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
|
||||
SDL_Rect textRect = { .x = x, .y = y, .w = surface->w, .h = surface->h };
|
||||
SDL_RenderCopy(renderer, texture, nullptr, &textRect);
|
||||
}
|
||||
|
||||
std::unique_ptr<Sprite> GameRenderer::load_sprite(const std::string &file, const int width, const int height) const
|
||||
{
|
||||
return std::make_unique<Sprite>(renderer, file, width, height);
|
||||
@ -63,9 +89,9 @@ void GameRenderer::draw_sprite_rotated(const Sprite &sprite, const int x, const
|
||||
SDL_RenderCopyEx(renderer, sprite.texture, nullptr, &rect, angle, nullptr, SDL_FLIP_NONE);
|
||||
}
|
||||
|
||||
void GameRenderer::clear_screen(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) const
|
||||
void GameRenderer::clear_screen(Color color) const
|
||||
{
|
||||
SDL_SetRenderDrawColor(renderer, r, g, b, a);
|
||||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
SDL_RenderClear(renderer);
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,11 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include "Sprite.hpp"
|
||||
#include "Color.hpp"
|
||||
|
||||
typedef TTF_Font Font;
|
||||
|
||||
class GameRenderer
|
||||
{
|
||||
@ -22,7 +26,13 @@ public:
|
||||
|
||||
~GameRenderer();
|
||||
|
||||
std::unique_ptr<Sprite> load_sprite(const std::string &file, int width, int height) const;
|
||||
[[nodiscard]] Font *load_font(const std::string &file, int font_size);
|
||||
|
||||
int get_text_width(Font *font, const std::string &text) const;
|
||||
|
||||
void draw_text(Font *font, const std::string &text, int x, int y, Color color) const;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Sprite> load_sprite(const std::string &file, int width, int height) const;
|
||||
|
||||
void draw_sprite(const Sprite &sprite, int x, int y) const;
|
||||
|
||||
@ -30,7 +40,7 @@ public:
|
||||
|
||||
void draw_sprite_rotated(const Sprite &sprite, int x, int y, double angle) const;
|
||||
|
||||
void clear_screen(uint8_t r, uint8_t g, uint8_t b, uint8_t a) const;
|
||||
void clear_screen(Color color) const;
|
||||
|
||||
void flush() const;
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@ class Map
|
||||
{
|
||||
private:
|
||||
GameRenderer *renderer;
|
||||
std::vector<std::vector<Tile>> tiles;
|
||||
|
||||
std::unique_ptr<Sprite> grass_sprite, path_sprite;
|
||||
|
||||
@ -22,6 +21,8 @@ public:
|
||||
double tile_offset_y;
|
||||
int tile_size;
|
||||
|
||||
std::vector<std::vector<Tile>> tiles;
|
||||
|
||||
Map(GameRenderer *renderer, int tile_size);
|
||||
|
||||
~Map() = default;
|
||||
|
||||
@ -5,7 +5,8 @@
|
||||
Player::Player(GameRenderer *renderer) : renderer(renderer)
|
||||
{
|
||||
hero_sprite = renderer->load_sprite("./assets/hero_front.png", 40, 40);
|
||||
bow_sprite = renderer->load_sprite("./assets/bow_arrow.png", 22, 32);
|
||||
bow_arrow_sprite = renderer->load_sprite("./assets/bow_arrow.png", 22, 32);
|
||||
bow_sprite = renderer->load_sprite("./assets/bow.png", 22, 32);
|
||||
}
|
||||
|
||||
void Player::draw() const
|
||||
@ -17,7 +18,7 @@ void Player::draw() const
|
||||
);
|
||||
|
||||
renderer->draw_sprite_rotated(
|
||||
*bow_sprite,
|
||||
shooting_cooldown > 0 ? *bow_sprite : *bow_arrow_sprite,
|
||||
(int)(renderer->screen_width / 2 - bow_sprite->width / 2 + std::cos(angle) * 30),
|
||||
(int)(renderer->screen_height / 2 - bow_sprite->height / 2 + std::sin(angle) * 30),
|
||||
angle * 180 / std::numbers::pi
|
||||
@ -28,4 +29,6 @@ void Player::update()
|
||||
{
|
||||
x += x_vel;
|
||||
y += y_vel;
|
||||
|
||||
if (shooting_cooldown > 0) shooting_cooldown--;
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ class Player
|
||||
{
|
||||
private:
|
||||
GameRenderer *renderer;
|
||||
std::unique_ptr<Sprite> hero_sprite, bow_sprite;
|
||||
std::unique_ptr<Sprite> hero_sprite, bow_sprite, bow_arrow_sprite;
|
||||
|
||||
public:
|
||||
double x = 0;
|
||||
@ -19,6 +19,10 @@ public:
|
||||
|
||||
double angle = 0.0;
|
||||
|
||||
int shooting_cooldown = 0;
|
||||
|
||||
int gold = 0;
|
||||
|
||||
static constexpr double speed = 1.5;
|
||||
|
||||
Player(GameRenderer *renderer);
|
||||
|
||||
@ -6,18 +6,20 @@
|
||||
|
||||
class Zombo
|
||||
{
|
||||
private:
|
||||
public:
|
||||
double x;
|
||||
double y;
|
||||
int hp = 3;
|
||||
|
||||
static constexpr double speed = 0.5;
|
||||
|
||||
public:
|
||||
Zombo(const GameRenderer *renderer, const Sprite &sprite, double player_x, double player_y);
|
||||
|
||||
void update(double player_x, double player_y);
|
||||
|
||||
void draw(const GameRenderer *renderer, const Sprite &sprite, double player_x, double player_y) const;
|
||||
|
||||
bool operator==(const Zombo &other) const = default;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue
Block a user