feat: implemented game
parent
ed3e91aead
commit
5e4f6e70b6
Binary file not shown.
|
@ -8,12 +8,12 @@ AssetDB::AssetDB() {
|
|||
this->index_assets();
|
||||
}
|
||||
|
||||
void AssetDB::clean() {
|
||||
void AssetDB::clean(bool force) {
|
||||
for(size_t i{0}; i < this->loaded.size();) {
|
||||
std::string asset_name{this->loaded.at(i)};
|
||||
std::shared_ptr<Asset> asset{this->assets.at(asset_name)};
|
||||
// usecount=1 means the asset is unused (other than the local use and the use in this->assets)
|
||||
if(asset.use_count() == 2) {
|
||||
// usecount of 2 means the asset is unused (other than the local use and the use in this->assets)
|
||||
if(force || asset.use_count() <= 2) {
|
||||
asset->unload();
|
||||
this->loaded.erase(this->loaded.begin() + i);
|
||||
} else ++i; // don't iterate when the asset is unloaded
|
||||
|
|
|
@ -15,7 +15,7 @@ public:
|
|||
AssetDB();
|
||||
~AssetDB() = default;
|
||||
template <class AssetType> std::optional<std::shared_ptr<AssetType>> get_asset(std::string const &name);
|
||||
void clean();
|
||||
void clean(bool force);
|
||||
private:
|
||||
void index_assets();
|
||||
void load(std::string asset_name);
|
||||
|
@ -25,8 +25,10 @@ template <class AssetType> std::optional<std::shared_ptr<AssetType>> AssetDB::ge
|
|||
if(!this->assets.contains(name)) return std::nullopt;
|
||||
std::shared_ptr<AssetType> found{std::dynamic_pointer_cast<AssetType>(this->assets.at(name))};
|
||||
if(found == nullptr) return std::nullopt;
|
||||
if(!found->is_loaded())
|
||||
if(!found->is_loaded()) {
|
||||
found->load();
|
||||
this->loaded.push_back(name);
|
||||
}
|
||||
return std::make_optional(found);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "asset_wrapper.h"
|
||||
#include "core/canvas_engine.hpp"
|
||||
#include <SDL2/SDL_image.h>
|
||||
#include <SDL2/SDL_log.h>
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include <filesystem>
|
||||
|
@ -15,15 +16,18 @@ Texture::~Texture() {
|
|||
this->unload();
|
||||
}
|
||||
|
||||
void Texture::unload() {
|
||||
SDL_DestroyTexture(this->texture);
|
||||
this->texture = nullptr;
|
||||
}
|
||||
|
||||
void Texture::load() {
|
||||
this->texture = IMG_LoadTexture(CanvasEngine::get_singleton()->get_render(), this->path.c_str());
|
||||
}
|
||||
|
||||
void Texture::unload() {
|
||||
if(this->is_loaded()) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "unloading font %s", this->path.c_str());
|
||||
SDL_DestroyTexture(this->texture);
|
||||
this->texture = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::is_loaded() const {
|
||||
return this->texture != nullptr;
|
||||
}
|
||||
|
@ -39,15 +43,21 @@ Font::~Font() {
|
|||
}
|
||||
|
||||
void Font::load() {
|
||||
this->font = TTF_OpenFont(this->path.c_str(), 32);
|
||||
this->font = TTF_OpenFont(this->path.c_str(), this->PT_SIZE);
|
||||
}
|
||||
|
||||
void Font::unload() {
|
||||
if(this->is_loaded()) {
|
||||
TTF_CloseFont(this->font);
|
||||
this->font = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Font::is_loaded() const {
|
||||
return this->font != nullptr;
|
||||
}
|
||||
|
||||
TTF_Font *Font::get() {
|
||||
return this->font;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ public:
|
|||
|
||||
class Font : public Asset {
|
||||
TTF_Font *font{nullptr};
|
||||
public:
|
||||
static const int PT_SIZE{64};
|
||||
public:
|
||||
Font(std::filesystem::path const &path);
|
||||
~Font();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "canvas_engine.hpp"
|
||||
#include "core/level.hpp"
|
||||
#include <SDL2/SDL_pixels.h>
|
||||
#include <SDL2/SDL_timer.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include <SDL2/SDL_video.h>
|
||||
#include <cassert>
|
||||
#include <SDL2/SDL_error.h>
|
||||
|
@ -22,18 +24,29 @@ CanvasEngine *CanvasEngine::singleton_instance{nullptr};
|
|||
CanvasEngine::CanvasEngine() {
|
||||
if(SDL_Init(SDL_INIT_EVERYTHING) != 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL, SDL error: %s", SDL_GetError());
|
||||
this->deinit_handled = true;
|
||||
return;
|
||||
}
|
||||
if(IMG_Init(IMG_INIT_PNG | IMG_INIT_JXL) == 0) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL_image, error: %s", IMG_GetError());
|
||||
SDL_Quit();
|
||||
this->deinit_handled = true;
|
||||
return;
|
||||
}
|
||||
if(TTF_Init() == -1) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to initialize SDL_ttf, error: %s", TTF_GetError());
|
||||
IMG_Quit();
|
||||
SDL_Quit();
|
||||
this->deinit_handled = true;
|
||||
return;
|
||||
}
|
||||
this->window = SDL_CreateWindow(PROJECTNAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 800, SDL_WINDOW_RESIZABLE | SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
if(this->window == nullptr) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create window, SDL error: %s", SDL_GetError());
|
||||
IMG_Quit();
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
this->deinit_handled = true;
|
||||
return;
|
||||
}
|
||||
this->render = SDL_CreateRenderer(this->window, -1, SDL_RENDERER_ACCELERATED);
|
||||
|
@ -41,7 +54,9 @@ CanvasEngine::CanvasEngine() {
|
|||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to initialize renderer, SDL error: %s", SDL_GetError());
|
||||
SDL_DestroyWindow(this->window);
|
||||
IMG_Quit();
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
this->deinit_handled = true;
|
||||
return;
|
||||
}
|
||||
this->render_target = SDL_CreateTexture(this->render, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
|
||||
|
@ -50,12 +65,22 @@ CanvasEngine::CanvasEngine() {
|
|||
}
|
||||
|
||||
CanvasEngine::~CanvasEngine() {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CANVAS: Shutting down");
|
||||
this->level.reset();
|
||||
this->assets.clean(true);
|
||||
if(!this->deinit_handled) {
|
||||
SDL_DestroyRenderer(this->render);
|
||||
SDL_DestroyWindow(this->window);
|
||||
IMG_Quit();
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
}
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "CANVAS: Done shutting down");
|
||||
}
|
||||
|
||||
void CanvasEngine::run(std::unique_ptr<Level> &level) {
|
||||
if(!stay_open)
|
||||
return;
|
||||
assert(CanvasEngine::singleton_instance == nullptr && "Engine singleton instance already assigned, starting another instance is invalid");
|
||||
// register as singleton
|
||||
CanvasEngine::singleton_instance = this;
|
||||
|
@ -82,6 +107,10 @@ void CanvasEngine::run(std::unique_ptr<Level> &level) {
|
|||
} else {
|
||||
SDL_Delay(2);
|
||||
}
|
||||
if(this->next_level) {
|
||||
this->level = std::move(this->next_level);
|
||||
this->level->instantiate();
|
||||
}
|
||||
}
|
||||
assert(CanvasEngine::singleton_instance == this && "Engine singleton instance changed while game was running");
|
||||
CanvasEngine::singleton_instance = nullptr;
|
||||
|
@ -95,6 +124,10 @@ void CanvasEngine::set_target_delta_time(double target) {
|
|||
this->target_delta_time = target;
|
||||
}
|
||||
|
||||
void CanvasEngine::change_level(std::unique_ptr<Level> &level) {
|
||||
this->next_level = std::move(level);
|
||||
}
|
||||
|
||||
AssetDB &CanvasEngine::get_assets() {
|
||||
return this->assets;
|
||||
}
|
||||
|
|
|
@ -25,11 +25,13 @@ private:
|
|||
CollisionWorld collision_world{};
|
||||
InputMap input_map{}; //!< map of inputs to input callback objects
|
||||
std::unique_ptr<Level> level;
|
||||
std::unique_ptr<Level> next_level;
|
||||
Uint64 last_frame_start_time{}; //!< time at start of last frame
|
||||
Uint64 frame_start_time{}; //!< time at start of this frame
|
||||
double delta_time{0.f}; //!< measured delta time
|
||||
double target_delta_time{}; //!< delta time target
|
||||
bool stay_open{false}; //!< application loop will continue so long as this is true
|
||||
bool deinit_handled{false};
|
||||
public:
|
||||
CanvasEngine();
|
||||
~CanvasEngine();
|
||||
|
@ -41,6 +43,7 @@ public:
|
|||
void run(std::unique_ptr<Level> &level);
|
||||
void request_close();
|
||||
void set_target_delta_time(double target);
|
||||
void change_level(std::unique_ptr<Level> &level);
|
||||
AssetDB &get_assets();
|
||||
CollisionWorld &get_collision_world();
|
||||
InputMap &get_input_map();
|
||||
|
|
|
@ -5,14 +5,22 @@
|
|||
#include <cmath>
|
||||
|
||||
namespace ce {
|
||||
Level::~Level() {
|
||||
this->deinstantiate();
|
||||
}
|
||||
void Level::instantiate() {
|
||||
std::unique_ptr<Node> constructed{this->construct()};
|
||||
constructed->set_is_inside_tree(true);
|
||||
this->root = std::move(constructed);
|
||||
this->root->set_is_inside_tree(true);
|
||||
this->root->set_level(this);
|
||||
this->root->propagate_added();
|
||||
}
|
||||
|
||||
void Level::deinstantiate() {
|
||||
if(this->root) {
|
||||
this->root->propagate_removed();
|
||||
this->root.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Level::propagate_tick(double delta_time) {
|
||||
|
@ -24,20 +32,17 @@ void Level::propagate_draw(SDL_Renderer *render) {
|
|||
int w, h;
|
||||
SDL_Window *window{SDL_RenderGetWindow(render)};
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
ce::Transform const screen_transform{
|
||||
.position = ce::Vecf::ZERO,
|
||||
Transform const screen_transform{
|
||||
.position = Vecf::ZERO,
|
||||
.rotation = 0.f,
|
||||
.scale = ce::Vecf::ONE * float(w),
|
||||
.scale = Vecf::ONE * float(w),
|
||||
};
|
||||
float const ratio{float(h)/float(w)};
|
||||
this->root->propagate_draw(render, Transform().translated({
|
||||
0.5f / this->view_transform.scale.x,
|
||||
0.5f / this->view_transform.scale.y * ratio
|
||||
}) * this->view_transform * screen_transform);
|
||||
this->root->propagate_draw_ui(render, Transform().translated({
|
||||
0.5f / this->view_transform.scale.x,
|
||||
0.5f / this->view_transform.scale.y * ratio
|
||||
}) * screen_transform);
|
||||
}) * view_transform * screen_transform);
|
||||
this->root->propagate_draw_ui(render, screen_transform);
|
||||
}
|
||||
|
||||
Node *Level::get_root() {
|
||||
|
|
|
@ -14,7 +14,7 @@ protected:
|
|||
.scale = {1.f/10.f, 1.f/10.f},
|
||||
};
|
||||
public:
|
||||
virtual ~Level() = default;
|
||||
virtual ~Level();
|
||||
void instantiate();
|
||||
virtual Node::OwnedPtr construct() = 0;
|
||||
void deinstantiate();
|
||||
|
|
|
@ -19,6 +19,7 @@ void Node::add_child(Node::OwnedPtr &child) {
|
|||
this->children.push_back({child->get_name(), std::move(child)});
|
||||
added->parent = this;
|
||||
added->set_is_inside_tree(this->inside_tree);
|
||||
added->set_level(this->level);
|
||||
this->child_added.invoke(added);
|
||||
added->propagate_added();
|
||||
}
|
||||
|
|
|
@ -22,14 +22,12 @@ void Sprite::_draw(SDL_Renderer *render, ce::Transform const &view_transform) {
|
|||
}
|
||||
int w, h;
|
||||
SDL_QueryTexture(this->texture->get(), NULL, NULL, &w, &h);
|
||||
Transform transform{this->get_global_transform() * view_transform};
|
||||
assert(transform.scale.x != 0 && transform.scale.y != 0); // !!!
|
||||
ce::Vecf size{this->get_size()};
|
||||
size.scale(view_transform.scale);
|
||||
Transform const transform{this->get_global_transform() * view_transform};
|
||||
ce::Vecf const size{this->get_size().scaled(view_transform.scale)};
|
||||
assert(size.x != 0.f && size.y != 0.f);
|
||||
//float fw(w), fh(h);
|
||||
SDL_Rect src{.x=0, .y=0, .w=w, .h=h};
|
||||
SDL_FRect dst{.x=transform.position.x - size.x/2.f, .y=transform.position.y - size.y/2.f, .w=size.x, .h=size.y};
|
||||
SDL_Rect const src{.x=0, .y=0, .w=w, .h=h};
|
||||
SDL_FRect const dst{.x=transform.position.x - size.x/2.f, .y=transform.position.y - size.y/2.f, .w=size.x, .h=size.y};
|
||||
SDL_RenderCopyExF(render, this->texture->get(),
|
||||
&src, &dst,
|
||||
transform.rotation * 57.2958f,NULL,
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
#include "ui_text.hpp"
|
||||
#include "core/assets/asset_db.hpp"
|
||||
#include "core/canvas_engine.hpp"
|
||||
#include <SDL2/SDL_render.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace ce {
|
||||
UiText::UiText(std::string const &name, std::string text, std::string font, SDL_Color color)
|
||||
: Node2D(name)
|
||||
, font{}
|
||||
, text{text}
|
||||
, color{color} {
|
||||
std::optional<std::shared_ptr<Font>> font_asset{CanvasEngine::get_singleton()->get_assets().get_asset<Font>(font)};
|
||||
if(font_asset.has_value())
|
||||
this->font = font_asset.value();
|
||||
}
|
||||
|
||||
UiText::~UiText() {
|
||||
if(this->cached)
|
||||
SDL_DestroyTexture(this->cached);
|
||||
}
|
||||
|
||||
void UiText::_draw_ui(SDL_Renderer *render, Transform const &ui_transform) {
|
||||
if(dirty) {
|
||||
this->render();
|
||||
}
|
||||
if(this->cached == nullptr) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "No texture assigned");
|
||||
this->set_visible(false);
|
||||
return;
|
||||
}
|
||||
int w, h; SDL_QueryTexture(this->cached, NULL, NULL, &w, &h);
|
||||
Transform const transform{this->get_global_transform() * ui_transform};
|
||||
Vecf const size{Vecf{float(w) / float(h), 1.f}.scaled(transform.scale)};
|
||||
assert(size.x != 0.f && size.y != 0.f);
|
||||
SDL_Rect const src{.x=0, .y=0, .w=w, .h=h};
|
||||
SDL_FRect const dst{.x=transform.position.x, .y=transform.position.y, .w=size.x, .h=size.y};
|
||||
SDL_RenderCopyExF(render, this->cached,
|
||||
&src, &dst,
|
||||
transform.rotation * 57.2958f,NULL,
|
||||
SDL_FLIP_NONE);
|
||||
}
|
||||
|
||||
void UiText::set_text(std::string text) {
|
||||
this->text = text;
|
||||
this->render();
|
||||
}
|
||||
|
||||
std::string const &UiText::get_text() const {
|
||||
return this->text;
|
||||
}
|
||||
|
||||
void UiText::render() {
|
||||
if(this->cached != nullptr)
|
||||
SDL_DestroyTexture(this->cached);
|
||||
SDL_Renderer *render{CanvasEngine::get_singleton()->get_render()};
|
||||
if(render == nullptr) {
|
||||
this->dirty = true; // can't render right now, defer for later
|
||||
return;
|
||||
}
|
||||
SDL_Surface *surf{TTF_RenderText_Blended(this->font->get(), this->text.c_str(), this->color)};
|
||||
assert(surf != nullptr && "Failed to render text");
|
||||
this->cached = SDL_CreateTextureFromSurface(render, surf);
|
||||
assert(this->cached != nullptr && "Failed to pass rendered text to the GPU");
|
||||
SDL_FreeSurface(surf);
|
||||
this->dirty = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef CORE_UI_TEXT_HPP
|
||||
#define CORE_UI_TEXT_HPP
|
||||
|
||||
#include "core/assets/asset_wrapper.h"
|
||||
#include "core/math/transform.hpp"
|
||||
#include "core/node2d.hpp"
|
||||
|
||||
namespace ce {
|
||||
class UiText : public Node2D {
|
||||
std::shared_ptr<Font> font{nullptr};
|
||||
std::string text{};
|
||||
SDL_Color color{255, 255, 255, 255};
|
||||
SDL_Texture *cached{nullptr};
|
||||
bool dirty{true};
|
||||
public:
|
||||
UiText(std::string const &name, std::string text, std::string font, SDL_Color color);
|
||||
~UiText();
|
||||
virtual void _draw_ui(SDL_Renderer *render, Transform const &view_transform) override;
|
||||
|
||||
void set_text(std::string text);
|
||||
std::string const &get_text() const;
|
||||
protected:
|
||||
void render();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // !CORE_UI_TEXT_HPP
|
|
@ -0,0 +1,15 @@
|
|||
#include "end_screen.hpp"
|
||||
#include "core/math/transform.hpp"
|
||||
#include "core/node2d.hpp"
|
||||
#include "core/ui_text.hpp"
|
||||
#include <format>
|
||||
|
||||
EndScreen::EndScreen(unsigned score)
|
||||
: score{score} {}
|
||||
|
||||
ce::Node::OwnedPtr EndScreen::construct() {
|
||||
ce::Node::OwnedPtr root{new ce::Node2D("root")};
|
||||
root->create_child<ce::UiText>("score", std::format("Score: {}", this->score), "inter", SDL_Color{255, 255, 255, 255})
|
||||
->set_global_transform(ce::Transform().translated({0.3f, 0.3f}).scaled({0.1f, 0.1f}));
|
||||
return std::move(root);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef END_SCREEN_HPP
|
||||
#define END_SCREEN_HPP
|
||||
|
||||
#include "core/level.hpp"
|
||||
|
||||
class EndScreen : public ce::Level {
|
||||
unsigned score;
|
||||
public:
|
||||
EndScreen(unsigned score);
|
||||
virtual ce::Node::OwnedPtr construct() override;
|
||||
};
|
||||
|
||||
#endif // !END_SCREEN_HPP
|
|
@ -1,9 +1,16 @@
|
|||
#include "level_1.hpp"
|
||||
#include "core/canvas_engine.hpp"
|
||||
#include "core/level.hpp"
|
||||
#include "core/node.hpp"
|
||||
#include "core/node2d.hpp"
|
||||
#include "core/ui_text.hpp"
|
||||
#include "end_screen.hpp"
|
||||
#include "life_display.hpp"
|
||||
#include "player.hpp"
|
||||
#include "score_display.hpp"
|
||||
#include "scrolling_ground.hpp"
|
||||
#include "truck.hpp"
|
||||
#include "spawner.hpp"
|
||||
#include <SDL2/SDL_log.h>
|
||||
|
||||
ce::Node::OwnedPtr Level1::construct() {
|
||||
ce::Node::OwnedPtr root{new ce::Node2D("root")};
|
||||
|
@ -13,6 +20,23 @@ ce::Node::OwnedPtr Level1::construct() {
|
|||
.scale = ce::Vecf::ONE
|
||||
});
|
||||
root->create_child<Player>();
|
||||
root->create_child<Truck>(true);
|
||||
root->create_child<Spawner>();
|
||||
root->create_child<ScoreDisplay>()
|
||||
->set_global_transform(ce::Transform().scaled({.05f, .05f}).translated({0.01f, 0.f}));
|
||||
root->create_child<LifeDisplay>()
|
||||
->set_global_transform(ce::Transform().scaled({.05f, .05f}).translated({.9f, 0.f}));
|
||||
return std::move(root);
|
||||
}
|
||||
|
||||
void Level1::add_score() {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "add_score");
|
||||
this->score_added.invoke(++this->score);
|
||||
}
|
||||
|
||||
void Level1::lose_life() {
|
||||
this->life_lost.invoke(--this->lives);
|
||||
if(this->lives == 0) {
|
||||
std::unique_ptr<ce::Level> next{ce::Level::make<EndScreen>(this->score)};
|
||||
ce::CanvasEngine::get_singleton()->change_level(next);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,17 @@
|
|||
#define LEVEL_1_HPP
|
||||
|
||||
#include "core/level.hpp"
|
||||
#include "core/signal.hpp"
|
||||
|
||||
class Level1 : public ce::Level {
|
||||
public:
|
||||
unsigned score{0};
|
||||
unsigned lives{3};
|
||||
ce::Signal<unsigned> score_added{};
|
||||
ce::Signal<unsigned> life_lost{};
|
||||
public:
|
||||
void add_score();
|
||||
void lose_life();
|
||||
virtual ce::Node::OwnedPtr construct() override;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#include "life_display.hpp"
|
||||
#include "core/ui_text.hpp"
|
||||
#include "level_1.hpp"
|
||||
#include <format>
|
||||
|
||||
LifeDisplay::LifeDisplay()
|
||||
: ce::UiText("score_display", "0", "inter", {255, 255, 255, 255}) {}
|
||||
|
||||
void LifeDisplay::_added() {
|
||||
if(Level1 *level{dynamic_cast<Level1*>(this->get_level())}) {
|
||||
level->life_lost.connect(ce::Callable<void, unsigned>::make(this, &LifeDisplay::_lives_changed));
|
||||
this->_lives_changed(level->lives);
|
||||
}
|
||||
}
|
||||
|
||||
void LifeDisplay::_lives_changed(unsigned value) {
|
||||
this->set_text(std::format("{}", value));
|
||||
this->render();
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef LIFE_DISPLAY_HPP
|
||||
#define LIFE_DISPLAY_HPP
|
||||
|
||||
#include "core/ui_text.hpp"
|
||||
|
||||
class LifeDisplay : public ce::UiText {
|
||||
public:
|
||||
LifeDisplay();
|
||||
virtual void _added() override;
|
||||
void _lives_changed(unsigned new_value);
|
||||
};
|
||||
|
||||
#endif // !LIFE_DISPLAY_HPP
|
|
@ -1,4 +1,5 @@
|
|||
#include "core/canvas_engine.hpp"
|
||||
#include "core/level.hpp"
|
||||
#include "level_1.hpp"
|
||||
#include <SDL2/SDL_log.h>
|
||||
|
||||
|
@ -6,6 +7,6 @@ ce::CanvasEngine engine{};
|
|||
|
||||
int main(int argc [[maybe_unused]], char* argv [[maybe_unused]][]) {
|
||||
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);
|
||||
std::unique_ptr<ce::Level> level{std::make_unique<Level1>()};
|
||||
std::unique_ptr<ce::Level> level{ce::Level::make<Level1>()};
|
||||
engine.run(level);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "player.hpp"
|
||||
#include "level_1.hpp"
|
||||
#include "truck.hpp"
|
||||
#include "core/callable.hpp"
|
||||
#include "core/canvas_engine.hpp"
|
||||
|
@ -72,6 +73,10 @@ void Player::_input_vertical_movement(ce::InputValue value) {
|
|||
void Player::_on_overlap_enter(ce::CollisionShape *, ce::CollidableNode *other, ce::CollisionShape *shape) {
|
||||
if(this->invincibility > 0.f)
|
||||
return;
|
||||
if(Truck *truck{dynamic_cast<Truck*>(other)})
|
||||
if(Truck *truck{dynamic_cast<Truck*>(other)}) {
|
||||
this->invincibility = 2.f;
|
||||
if(Level1 *level{dynamic_cast<Level1*>(this->get_level())}) {
|
||||
level->lose_life();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#include "score_display.hpp"
|
||||
#include "core/ui_text.hpp"
|
||||
#include "level_1.hpp"
|
||||
#include <format>
|
||||
|
||||
ScoreDisplay::ScoreDisplay()
|
||||
: ce::UiText("score_display", "0", "inter", {255, 255, 255, 255}) {}
|
||||
|
||||
void ScoreDisplay::_added() {
|
||||
if(Level1 *level{dynamic_cast<Level1*>(this->get_level())}) {
|
||||
level->score_added.connect(ce::Callable<void, unsigned>::make(this, &ScoreDisplay::_score_changed));
|
||||
}
|
||||
}
|
||||
|
||||
void ScoreDisplay::_score_changed(unsigned value) {
|
||||
this->set_text(std::format("{}", value));
|
||||
this->render();
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef SCORE_DISPLAY_HPP
|
||||
#define SCORE_DISPLAY_HPP
|
||||
|
||||
#include "core/ui_text.hpp"
|
||||
|
||||
class ScoreDisplay : public ce::UiText {
|
||||
public:
|
||||
ScoreDisplay();
|
||||
virtual void _added() override;
|
||||
void _score_changed(unsigned new_value);
|
||||
};
|
||||
|
||||
#endif // !SCORE_DISPLAY_HPP
|
|
@ -2,6 +2,7 @@
|
|||
#include "core/math/transform.hpp"
|
||||
#include "core/sprite.hpp"
|
||||
#include "core/collision_shape.hpp"
|
||||
#include "level_1.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <SDL2/SDL_log.h>
|
||||
|
@ -29,6 +30,11 @@ void Truck::_tick(double delta) {
|
|||
trans.position.x = std::clamp(trans.position.x, -LIMITS.x, LIMITS.x);
|
||||
trans.position.y = std::max(trans.position.y, -LIMITS.y);
|
||||
this->set_global_transform(trans);
|
||||
if(transform.position.y > 4.5f)
|
||||
if(transform.position.y > 4.5f) {
|
||||
this->flag_for_deletion();
|
||||
if(Level1 *level{dynamic_cast<Level1*>(this->get_level())}) {
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "adding score");
|
||||
level->add_score();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue