aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig2
-rw-r--r--Doxyfile10
-rw-r--r--asset/texture/ERROR.pngbin0 -> 3581 bytes
-rw-r--r--contributing.md23
-rw-r--r--makefile3
-rw-r--r--src/crepe/Component.h5
-rw-r--r--src/crepe/api/Color.cpp37
-rw-r--r--src/crepe/api/Color.h49
-rw-r--r--src/crepe/api/Sprite.h3
-rw-r--r--src/crepe/api/Texture.cpp2
-rw-r--r--src/crepe/facade/DB.cpp4
-rw-r--r--src/crepe/facade/DB.h4
-rw-r--r--src/crepe/facade/SDLContext.cpp87
-rw-r--r--src/crepe/facade/SDLContext.h41
-rw-r--r--src/crepe/system/RenderSystem.cpp88
-rw-r--r--src/crepe/system/RenderSystem.h53
-rw-r--r--src/crepe/util/Proxy.h2
-rw-r--r--src/crepe/util/Proxy.hpp6
-rw-r--r--src/doc/feature/script.dox62
-rw-r--r--src/doc/features.dox10
-rw-r--r--src/doc/index.dox10
-rw-r--r--src/doc/installing.dox9
-rw-r--r--src/doc/layout.xml252
-rw-r--r--src/doc/style.css4
-rw-r--r--src/example/CMakeLists.txt7
-rw-r--r--src/example/audio_internal.cpp56
-rw-r--r--src/example/components_internal.cpp51
-rw-r--r--src/example/db.cpp30
-rw-r--r--src/example/ecs.cpp53
-rw-r--r--src/example/log.cpp28
-rw-r--r--src/example/particles.cpp43
-rw-r--r--src/example/physics.cpp24
-rw-r--r--src/example/proxy.cpp45
-rw-r--r--src/example/rendering.cpp55
-rw-r--r--src/example/rendering_particle.cpp71
-rw-r--r--src/example/scene_manager.cpp79
-rw-r--r--src/example/script.cpp49
-rw-r--r--src/makefile7
-rw-r--r--src/test/CMakeLists.txt4
-rw-r--r--src/test/DBTest.cpp29
-rw-r--r--src/test/ECSTest.cpp236
-rw-r--r--src/test/ParticleTest.cpp2
-rw-r--r--src/test/RenderSystemTest.cpp174
-rw-r--r--src/test/SceneManagerTest.cpp122
-rw-r--r--src/test/ValueBrokerTest.cpp64
45 files changed, 1328 insertions, 667 deletions
diff --git a/.editorconfig b/.editorconfig
index 191871f..65f5034 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,5 +10,5 @@ max_line_length = 95
indent_style = space
indent_size = 2
-[*.md]
+[*.{md,dox}]
max_line_length = 80
diff --git a/Doxyfile b/Doxyfile
index 8ba90fc..e0a31df 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -14,12 +14,20 @@ RECURSIVE = YES
GENERATE_LATEX = NO
+LAYOUT_FILE = src/doc/layout.xml
+TAB_SIZE = 2
+
+HTML_INDEX_NUM_ENTRIES = 2
+HTML_EXTRA_STYLESHEET = src/doc/style.css
+
USE_MDFILE_AS_MAINPAGE = ./readme.md
-HTML_INDEX_NUM_ENTRIES = 1 # collapse trees by default
REPEAT_BRIEF = NO
INTERNAL_DOCS = YES
+EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
+HIDE_UNDOC_NAMESPACES = YES
+HIDE_UNDOC_CLASSES = YES
QUIET = YES
diff --git a/asset/texture/ERROR.png b/asset/texture/ERROR.png
new file mode 100644
index 0000000..2af3548
--- /dev/null
+++ b/asset/texture/ERROR.png
Binary files differ
diff --git a/contributing.md b/contributing.md
index 5b0c79d..9c95851 100644
--- a/contributing.md
+++ b/contributing.md
@@ -20,7 +20,7 @@ that you can click on to open them.
# Code style
- Formatting nitty-gritty is handled by clang-format/clang-tidy (run `make
- format` in the root folder of this repository to format all sources files)
+ format` or `make lint`)
- <details><summary>
ASCII only
</summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
@@ -798,6 +798,27 @@ that you can click on to open them.
resolving merge conflicts when multiple sources were added by different
people to the same CMakeLists.txt easier.
+## GoogleTest-specific
+
+- Unit tests are not *required* to follow all code standards
+- <details><summary>
+ Private/protected members may be accessed using preprocessor tricks
+ </summary>
+
+ ```cpp
+ // include unrelated headers before
+
+ #define private public
+ #define protected public
+
+ // headers included after *will* be affected
+ ```
+ </details>
+- Each test source file defines tests within a single test suite (first
+ parameter of `TEST()` / `TEST_F()` macro)
+- Test source files match their suite name (or test fixture name in the case of
+ tests that use a fixture)
+
# Structure
- Files are placed in the appropriate directory:
diff --git a/makefile b/makefile
index dd7c587..c46e8a5 100644
--- a/makefile
+++ b/makefile
@@ -6,5 +6,6 @@ doxygen: Doxyfile FORCE
FMT += $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp')
format: FORCE
clang-format -i $(FMT)
- $(MAKE) -C src $@
+lint: FORCE
+ $(MAKE) -C src $@
diff --git a/src/crepe/Component.h b/src/crepe/Component.h
index 5279fb3..dc17721 100644
--- a/src/crepe/Component.h
+++ b/src/crepe/Component.h
@@ -27,6 +27,11 @@ protected:
//! Only the ComponentManager can create components
friend class ComponentManager;
+ Component(const Component &) = delete;
+ Component(Component &&) = delete;
+ virtual Component & operator=(const Component &) = delete;
+ virtual Component & operator=(Component &&) = delete;
+
public:
virtual ~Component() = default;
diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp
index 9e5f187..29bd77a 100644
--- a/src/crepe/api/Color.cpp
+++ b/src/crepe/api/Color.cpp
@@ -2,32 +2,11 @@
using namespace crepe;
-Color Color::white = Color(255, 255, 255, 0);
-Color Color::red = Color(255, 0, 0, 0);
-Color Color::green = Color(0, 255, 0, 0);
-Color Color::blue = Color(0, 0, 255, 0);
-Color Color::black = Color(0, 0, 0, 0);
-Color Color::cyan = Color(0, 255, 255, 0);
-Color Color::yellow = Color(255, 255, 0, 0);
-Color Color::magenta = Color(255, 0, 255, 0);
-
-Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {
- this->a = alpha;
- this->r = red;
- this->g = green;
- this->b = blue;
-};
-
-const Color & Color::get_white() { return Color::white; };
-
-const Color & Color::get_red() { return Color::red; };
-const Color & Color::get_green() { return Color::green; };
-const Color & Color::get_blue() { return Color::blue; };
-
-const Color & Color::get_black() { return Color::black; };
-
-const Color & Color::get_cyan() { return Color::cyan; };
-
-const Color & Color::get_yellow() { return Color::yellow; };
-
-const Color & Color::get_magenta() { return Color::magenta; };
+const Color Color::WHITE{0xff, 0xff, 0xff};
+const Color Color::RED{0xff, 0x00, 0x00};
+const Color Color::GREEN{0x00, 0xff, 0x00};
+const Color Color::BLUE{0x00, 0x00, 0xff};
+const Color Color::BLACK{0x00, 0x00, 0x00};
+const Color Color::CYAN{0x00, 0xff, 0xff};
+const Color Color::YELLOW{0xff, 0xff, 0x00};
+const Color Color::MAGENTA{0xff, 0x00, 0xff};
diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h
index aa47bf4..84edb5c 100644
--- a/src/crepe/api/Color.h
+++ b/src/crepe/api/Color.h
@@ -4,41 +4,20 @@
namespace crepe {
-// TODO: make Color a struct w/o constructors/destructors
-class Color {
-
- // FIXME: can't these colors be defined as a `static constexpr const Color`
- // instead?
-
-public:
- Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha);
- static const Color & get_white();
- static const Color & get_red();
- static const Color & get_green();
- static const Color & get_blue();
- static const Color & get_cyan();
- static const Color & get_magenta();
- static const Color & get_yellow();
- static const Color & get_black();
-
-private:
- // TODO: why are these private!?
- uint8_t r;
- uint8_t g;
- uint8_t b;
- uint8_t a;
-
- static Color white;
- static Color red;
- static Color green;
- static Color blue;
- static Color cyan;
- static Color magenta;
- static Color yellow;
- static Color black;
-
-private:
- friend class SDLContext;
+struct Color {
+ uint8_t r = 0x00;
+ uint8_t g = 0x00;
+ uint8_t b = 0x00;
+ uint8_t a = 0xff;
+
+ static const Color WHITE;
+ static const Color RED;
+ static const Color GREEN;
+ static const Color BLUE;
+ static const Color CYAN;
+ static const Color MAGENTA;
+ static const Color YELLOW;
+ static const Color BLACK;
};
} // namespace crepe
diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h
index 0192793..74a55d4 100644
--- a/src/crepe/api/Sprite.h
+++ b/src/crepe/api/Sprite.h
@@ -2,8 +2,9 @@
#include <memory>
+#include "../Component.h"
+
#include "Color.h"
-#include "Component.h"
#include "Texture.h"
namespace crepe {
diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp
index de0d0ea..734a5bb 100644
--- a/src/crepe/api/Texture.cpp
+++ b/src/crepe/api/Texture.cpp
@@ -35,5 +35,5 @@ int Texture::get_width() const {
}
int Texture::get_height() const {
if (this->texture == nullptr) return 0;
- return SDLContext::get_instance().get_width(*this);
+ return SDLContext::get_instance().get_height(*this);
}
diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp
index d5d19dc..95cf606 100644
--- a/src/crepe/facade/DB.cpp
+++ b/src/crepe/facade/DB.cpp
@@ -18,8 +18,8 @@ DB::DB(const string & path) {
this->db = {db, [](libdb::DB * db) { db->close(db, 0); }};
// load or create database file
- ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL, libdb::DB_BTREE, DB_CREATE,
- 0);
+ const char * file = path.empty() ? NULL : path.c_str();
+ ret = this->db->open(this->db.get(), NULL, file, NULL, libdb::DB_BTREE, DB_CREATE, 0);
if (ret != 0) throw runtime_error(format("db->open: {}", libdb::db_strerror(ret)));
// create cursor
diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h
index 629b0eb..115c0f1 100644
--- a/src/crepe/facade/DB.h
+++ b/src/crepe/facade/DB.h
@@ -22,8 +22,10 @@ class DB {
public:
/**
* \param path The path of the database (created if nonexistant)
+ *
+ * \note If \p path is empty, the database is entirely in-memory
*/
- DB(const std::string & path);
+ DB(const std::string & path = "");
virtual ~DB() = default;
public:
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 9464c31..b8b2bda 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -1,5 +1,6 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
+#include <SDL2/SDL_keycode.h>
#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_surface.h>
@@ -7,13 +8,15 @@
#include <cmath>
#include <cstddef>
#include <functional>
-#include <iostream>
#include <memory>
+#include <stdexcept>
#include <string>
+#include "../api/Camera.h"
#include "../api/Sprite.h"
#include "../api/Texture.h"
#include "../api/Transform.h"
+#include "../api/Vector2.h"
#include "../util/Log.h"
#include "SDLContext.h"
@@ -30,29 +33,21 @@ SDLContext::SDLContext() {
dbg_trace();
// FIXME: read window defaults from config manager
- if (SDL_Init(SDL_INIT_VIDEO) < 0) {
- // FIXME: throw exception
- std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
- return;
+ if (SDL_Init(SDL_INIT_VIDEO) != 0) {
+ throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError()));
}
SDL_Window * tmp_window
= SDL_CreateWindow("Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
this->viewport.w, this->viewport.h, 0);
if (!tmp_window) {
- // FIXME: throw exception
- std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
- return;
+ throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError()));
}
this->game_window = {tmp_window, [](SDL_Window * window) { SDL_DestroyWindow(window); }};
SDL_Renderer * tmp_renderer
= SDL_CreateRenderer(this->game_window.get(), -1, SDL_RENDERER_ACCELERATED);
if (!tmp_renderer) {
- // FIXME: throw exception
- std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError()
- << std::endl;
- SDL_DestroyWindow(this->game_window.get());
- return;
+ throw runtime_error(format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError()));
}
this->game_renderer
@@ -60,9 +55,7 @@ SDLContext::SDLContext() {
int img_flags = IMG_INIT_PNG;
if (!(IMG_Init(img_flags) & img_flags)) {
- // FIXME: throw exception
- std::cout << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError()
- << std::endl;
+ throw runtime_error("SDLContext: SDL_image could not initialize!");
}
}
@@ -102,40 +95,63 @@ void SDLContext::handle_events(bool & running) {
void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer.get()); }
void SDLContext::present_screen() { SDL_RenderPresent(this->game_renderer.get()); }
-void SDLContext::draw(const Sprite & sprite, const Transform & transform, const Camera & cam) {
-
- SDL_RendererFlip render_flip
- = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
- | (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
-
- double adjusted_x = (transform.position.x - cam.x) * cam.zoom;
- double adjusted_y = (transform.position.y - cam.y) * cam.zoom;
- double adjusted_w = sprite.sprite_rect.w * transform.scale * cam.zoom;
- double adjusted_h = sprite.sprite_rect.h * transform.scale * cam.zoom;
-
- SDL_Rect srcrect = {
+SDL_Rect SDLContext::get_src_rect(const Sprite & sprite) const {
+ return SDL_Rect{
.x = sprite.sprite_rect.x,
.y = sprite.sprite_rect.y,
.w = sprite.sprite_rect.w,
.h = sprite.sprite_rect.h,
};
+}
+SDL_Rect SDLContext::get_dst_rect(const Sprite & sprite, const Vector2 & pos,
+ const double & scale, const Camera & cam) const {
+
+ double adjusted_x = (pos.x - cam.x) * cam.zoom;
+ double adjusted_y = (pos.y - cam.y) * cam.zoom;
+ double adjusted_w = sprite.sprite_rect.w * scale * cam.zoom;
+ double adjusted_h = sprite.sprite_rect.h * scale * cam.zoom;
- SDL_Rect dstrect = {
+ return SDL_Rect{
.x = static_cast<int>(adjusted_x),
.y = static_cast<int>(adjusted_y),
.w = static_cast<int>(adjusted_w),
.h = static_cast<int>(adjusted_h),
};
+}
+
+void SDLContext::draw_particle(const Sprite & sprite, const Vector2 & pos,
+ const double & angle, const double & scale,
+ const Camera & camera) {
+
+ SDL_RendererFlip render_flip
+ = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
+ | (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
+
+ SDL_Rect srcrect = this->get_src_rect(sprite);
+ SDL_Rect dstrect = this->get_dst_rect(sprite, pos, scale, camera);
+
+ SDL_RenderCopyEx(this->game_renderer.get(), sprite.sprite_image->texture.get(), &srcrect,
+ &dstrect, angle, NULL, render_flip);
+}
+
+void SDLContext::draw(const Sprite & sprite, const Transform & transform, const Camera & cam) {
+
+ SDL_RendererFlip render_flip
+ = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
+ | (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
+
+ SDL_Rect srcrect = this->get_src_rect(sprite);
+ SDL_Rect dstrect = this->get_dst_rect(sprite, transform.position, transform.scale, cam);
SDL_RenderCopyEx(this->game_renderer.get(), sprite.sprite_image->texture.get(), &srcrect,
&dstrect, transform.rotation, NULL, render_flip);
}
-void SDLContext::camera(const Camera & cam) {
+void SDLContext::set_camera(const Camera & cam) {
this->viewport.w = static_cast<int>(cam.aspect_width);
this->viewport.h = static_cast<int>(cam.aspect_height);
- this->viewport.x = static_cast<int>(cam.x) - (SCREEN_WIDTH / 2);
- this->viewport.y = static_cast<int>(cam.y) - (SCREEN_HEIGHT / 2);
+ this->viewport.x = static_cast<int>(cam.x) - (this->viewport.w / 2);
+ this->viewport.y = static_cast<int>(cam.y) - (this->viewport.h / 2);
SDL_SetRenderDrawColor(this->game_renderer.get(), cam.bg_color.r, cam.bg_color.g,
cam.bg_color.b, cam.bg_color.a);
@@ -147,9 +163,8 @@ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>
SDLContext::texture_from_path(const std::string & path) {
SDL_Surface * tmp = IMG_Load(path.c_str());
- if (tmp == nullptr) {
- tmp = IMG_Load("../asset/texture/ERROR.png");
- }
+ if (tmp == nullptr)
+ throw runtime_error(format("SDLContext: IMG_Load error: {}", SDL_GetError()));
std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> img_surface;
img_surface = {tmp, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }};
@@ -158,7 +173,7 @@ SDLContext::texture_from_path(const std::string & path) {
= SDL_CreateTextureFromSurface(this->game_renderer.get(), img_surface.get());
if (tmp_texture == nullptr) {
- throw runtime_error(format("Texture cannot be load from {}", path));
+ throw runtime_error(format("SDLContext: Texture cannot be load from {}", path));
}
std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> img_texture;
diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h
index 007092b..841ffc9 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -1,8 +1,10 @@
#pragma once
#include <SDL2/SDL_keycode.h>
+#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h>
+#include <cmath>
#include <functional>
#include <memory>
#include <string>
@@ -10,10 +12,7 @@
#include "../api/Sprite.h"
#include "../api/Transform.h"
#include "api/Camera.h"
-
-// FIXME: this needs to be removed
-const int SCREEN_WIDTH = 640;
-const int SCREEN_HEIGHT = 480;
+#include "api/Vector2.h"
namespace crepe {
@@ -21,9 +20,6 @@ namespace crepe {
// typedef is unusable when crepe is packaged. Wouter will fix this later.
typedef SDL_Keycode CREPE_KEYCODES;
-class Texture;
-class LoopManager;
-
/**
* \class SDLContext
* \brief Facade for the SDL library
@@ -91,9 +87,6 @@ private:
//! Will use the funtions: texture_from_path, get_width,get_height.
friend class Texture;
- //! Will use the funtions: texture_from_path, get_width,get_height.
- friend class Animator;
-
/**
* \brief Loads a texture from a file path.
* \param path Path to the image file.
@@ -127,6 +120,9 @@ private:
*/
void draw(const Sprite & sprite, const Transform & transform, const Camera & camera);
+ void draw_particle(const Sprite & sprite, const Vector2 & pos, const double & angle,
+ const double & scale, const Camera & camera);
+
//! Clears the screen, preparing for a new frame.
void clear_screen();
@@ -134,10 +130,31 @@ private:
void present_screen();
/**
- * \brief Sets the current camera for rendering.
+ * \brief sets the background of the camera (will be adjusted in future PR)
* \param camera Reference to the Camera object.
*/
- void camera(const Camera & camera);
+ void set_camera(const Camera & camera);
+
+private:
+ /**
+ * \brief calculates the sqaure size of the image
+ *
+ * \param sprite Reference to the sprite to calculate the rectangle
+ * \return sdl rectangle to draw a src image
+ */
+ SDL_Rect get_src_rect(const Sprite & sprite) const;
+ /**
+ * \brief calculates the sqaure size of the image for an destination
+ *
+ * \param sprite Reference to the sprite to calculate the rectangle
+ * \param pos the pos in pixel positions
+ * \param scale the multiplier to increase of decrease for the specified sprite
+ * \param cam Reference to the current camera in the scene to calculate the position based
+ * on the camera
+ * \return sdl rectangle to draw a dst image to draw on the screen
+ */
+ SDL_Rect get_dst_rect(const Sprite & sprite, const Vector2 & pos, const double & scale,
+ const Camera & cam) const;
private:
//! sdl Window
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index fa3d0de..e379771 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -1,44 +1,104 @@
+#include <algorithm>
+#include <cassert>
+#include <cmath>
#include <functional>
+#include <iostream>
+#include <stdexcept>
#include <vector>
#include "../ComponentManager.h"
+#include "../api/ParticleEmitter.h"
#include "../api/Sprite.h"
#include "../api/Transform.h"
+#include "../api/Vector2.h"
#include "../facade/SDLContext.h"
-#include "../util/Log.h"
#include "RenderSystem.h"
using namespace crepe;
+using namespace std;
-void RenderSystem::clear_screen() const { SDLContext::get_instance().clear_screen(); }
+void RenderSystem::clear_screen() { this->context.clear_screen(); }
-void RenderSystem::present_screen() const { SDLContext::get_instance().present_screen(); }
+void RenderSystem::present_screen() { this->context.present_screen(); }
void RenderSystem::update_camera() {
ComponentManager & mgr = this->component_manager;
std::vector<std::reference_wrapper<Camera>> cameras = mgr.get_components_by_type<Camera>();
+ if (cameras.size() == 0) throw std::runtime_error("No cameras in current scene");
+
for (Camera & cam : cameras) {
- SDLContext::get_instance().camera(cam);
- this->curr_cam = &cam;
+ if (!cam.active) continue;
+ this->context.set_camera(cam);
+ this->curr_cam_ref = &cam;
}
}
-void RenderSystem::render_sprites() const {
- ComponentManager & mgr = this->component_manager;
- std::vector<std::reference_wrapper<Sprite>> sprites = mgr.get_components_by_type<Sprite>();
+bool sorting_comparison(const Sprite & a, const Sprite & b) {
+ if (a.sorting_in_layer < b.sorting_in_layer) return true;
+ if (a.sorting_in_layer == b.sorting_in_layer) return a.order_in_layer < b.order_in_layer;
- SDLContext & render = SDLContext::get_instance();
- for (const Sprite & sprite : sprites) {
- auto transforms = mgr.get_components_by_id<Transform>(sprite.game_object_id);
- render.draw(sprite, transforms[0], *curr_cam);
- }
+ return false;
+}
+
+std::vector<std::reference_wrapper<Sprite>>
+RenderSystem::sort(std::vector<std::reference_wrapper<Sprite>> & objs) const {
+
+ std::vector<std::reference_wrapper<Sprite>> sorted_objs(objs);
+ std::sort(sorted_objs.begin(), sorted_objs.end(), sorting_comparison);
+
+ return sorted_objs;
}
void RenderSystem::update() {
this->clear_screen();
this->update_camera();
- this->render_sprites();
+ this->render();
this->present_screen();
}
+
+bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) {
+
+ ComponentManager & mgr = this->component_manager;
+
+ vector<reference_wrapper<ParticleEmitter>> emitters
+ = mgr.get_components_by_id<ParticleEmitter>(sprite.game_object_id);
+
+ bool rendering_particles = false;
+
+ for (const ParticleEmitter & em : emitters) {
+ if (!(&em.data.sprite == &sprite)) continue;
+ rendering_particles = true;
+ if (!em.active) continue;
+
+ for (const Particle & p : em.data.particles) {
+ if (!p.active) continue;
+ this->context.draw_particle(sprite, p.position, p.angle, scale,
+ *this->curr_cam_ref);
+ }
+ }
+ return rendering_particles;
+}
+void RenderSystem::render_normal(const Sprite & sprite, const Transform & tm) {
+ this->context.draw(sprite, tm, *this->curr_cam_ref);
+}
+
+void RenderSystem::render() {
+
+ ComponentManager & mgr = this->component_manager;
+ vector<reference_wrapper<Sprite>> sprites = mgr.get_components_by_type<Sprite>();
+ vector<reference_wrapper<Sprite>> sorted_sprites = this->sort(sprites);
+
+ for (const Sprite & sprite : sorted_sprites) {
+ if (!sprite.active) continue;
+ const Transform & transform
+ = mgr.get_components_by_id<Transform>(sprite.game_object_id).front().get();
+
+ bool rendered_particles = this->render_particle(sprite, transform.scale);
+
+ if (rendered_particles) continue;
+
+ this->render_normal(sprite, transform);
+ }
+}
diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h
index 87ec494..d25a6e3 100644
--- a/src/crepe/system/RenderSystem.h
+++ b/src/crepe/system/RenderSystem.h
@@ -1,18 +1,24 @@
#pragma once
-#include "api/Camera.h"
+#include <functional>
+#include <vector>
+
+#include "facade/SDLContext.h"
#include "System.h"
+#include <cmath>
namespace crepe {
+class Camera;
+class Sprite;
+
/**
* \class RenderSystem
* \brief Manages rendering operations for all game objects.
*
- * RenderSystem is responsible for rendering sprites, clearing and presenting the screen, and
- * managing the active camera. It functions as a singleton, providing centralized rendering
- * services for the application.
+ * RenderSystem is responsible for rendering, clearing and presenting the screen, and
+ * managing the active camera.
*/
class RenderSystem : public System {
public:
@@ -25,20 +31,45 @@ public:
private:
//! Clears the screen in preparation for rendering.
- void clear_screen() const;
+ void clear_screen();
//! Presents the rendered frame to the display.
- void present_screen() const;
+ void present_screen();
//! Updates the active camera used for rendering.
void update_camera();
- //! Renders all active sprites to the screen.
- void render_sprites() const;
+ //! Renders the whole screen
+ void render();
+
+ /**
+ * \brief Renders all the particles on the screen from a given sprite.
+ *
+ * \param sprite renders the particles with given texture
+ * \param tm the Transform component for scale
+ * \return true if particles have been rendered
+ */
+ bool render_particle(const Sprite & sprite, const double & scale);
+
+ /**
+ * \brief renders a sprite with a Transform component on the screen
+ *
+ * \param sprite the sprite component that holds all the data
+ * \param tm the Transform component that holds the position,rotation and scale
+ */
+ void render_normal(const Sprite & sprite, const Transform & tm);
+
+ /**
+ * \brief sort a vector sprite objects with
+ *
+ * \param objs the vector that will do a sorting algorithm on
+ * \return returns a sorted reference vector
+ */
+ std::vector<std::reference_wrapper<Sprite>>
+ sort(std::vector<std::reference_wrapper<Sprite>> & objs) const;
/**
* \todo Include color handling for sprites.
- * \todo Implement particle emitter rendering with sprites.
* \todo Add text rendering using SDL_ttf for text components.
* \todo Implement a text component and a button component.
* \todo Ensure each sprite is checked for active status before rendering.
@@ -48,8 +79,10 @@ private:
private:
//! Pointer to the current active camera for rendering
- Camera * curr_cam = nullptr;
+ Camera * curr_cam_ref = nullptr;
// TODO: needs a better solution
+
+ SDLContext & context = SDLContext::get_instance();
};
} // namespace crepe
diff --git a/src/crepe/util/Proxy.h b/src/crepe/util/Proxy.h
index b34f7c6..789144a 100644
--- a/src/crepe/util/Proxy.h
+++ b/src/crepe/util/Proxy.h
@@ -16,6 +16,8 @@ template <typename T>
class Proxy {
public:
//! Set operator
+ Proxy & operator=(Proxy &);
+ //! Set operator
Proxy & operator=(const T &);
//! Get operator
operator const T &();
diff --git a/src/crepe/util/Proxy.hpp b/src/crepe/util/Proxy.hpp
index b9923db..ef2b69f 100644
--- a/src/crepe/util/Proxy.hpp
+++ b/src/crepe/util/Proxy.hpp
@@ -14,6 +14,12 @@ Proxy<T> & Proxy<T>::operator=(const T & val) {
}
template <typename T>
+Proxy<T> & Proxy<T>::operator=(Proxy & proxy) {
+ this->broker.set(T(proxy));
+ return *this;
+}
+
+template <typename T>
Proxy<T>::operator const T &() {
return this->broker.get();
}
diff --git a/src/doc/feature/script.dox b/src/doc/feature/script.dox
new file mode 100644
index 0000000..d25a63b
--- /dev/null
+++ b/src/doc/feature/script.dox
@@ -0,0 +1,62 @@
+// vim:ft=doxygen
+namespace crepe {
+/**
+
+\defgroup feature_script Scripting
+\ingroup feature
+\brief User-defined scripts for game objects
+
+Scripts can be used to implement game behavior, and allow arbitrary code to run
+as part of the game loop. Scripts are implemented as derivative classes of
+Script, which are added to game objects using the BehaviorScript \ref Component
+"component".
+
+\todo This section is incomplete:
+- Utility functions to get components/events/etc inside script
+- How to listen for events
+- Extensions of script (keylistener)
+
+\see Script
+\see BehaviorScript
+\see GameObject
+
+\par Example
+
+First, define a class that inherits from Script. This class acts as an
+interface, and has two functions (\ref Script::init "\c init()" and \ref
+Script::update "\c update()"), which may be implemented (they are empty by
+default). From now on, this derivative class will be referred to as a *concrete
+script*.
+
+```cpp
+#include <crepe/api/Script.h>
+#include <crepe/api/BehaviorScript.h>
+
+class MyScript : public crepe::Script {
+ void init() {
+ // called once
+ }
+ void update() {
+ // called on fixed update
+ }
+};
+```
+
+Concrete scripts can be instantiated and attached to \ref GameObject
+"game objects" using the BehaviorScript \ref Component "component".
+
+```cpp
+using namespace crepe;
+GameObject obj = component_manager.new_object("name");
+
+// create BehaviorScript instance
+BehaviorScript & behavior_script = obj.add_component<BehaviorScript>();
+// attach (and instantiate) MyScript to behavior_script
+behavior_script.set_script<MyScript>();
+
+// the above can also be done in a single call for convenience:
+obj.add_component<BehaviorScript>().set_script<MyScript>();
+```
+
+*/
+}
diff --git a/src/doc/features.dox b/src/doc/features.dox
new file mode 100644
index 0000000..4786bed
--- /dev/null
+++ b/src/doc/features.dox
@@ -0,0 +1,10 @@
+// vim:ft=doxygen
+/**
+
+\defgroup feature Features
+\brief Engine components
+
+This page lists engine features and contains usage instructions for each
+feature.
+
+*/
diff --git a/src/doc/index.dox b/src/doc/index.dox
new file mode 100644
index 0000000..5ec7889
--- /dev/null
+++ b/src/doc/index.dox
@@ -0,0 +1,10 @@
+// vim:ft=doxygen
+/**
+
+\mainpage crêpe game engine
+
+Welcome to the documentation for the crêpe game engine.
+
+\see feature
+
+*/
diff --git a/src/doc/installing.dox b/src/doc/installing.dox
new file mode 100644
index 0000000..48b27d7
--- /dev/null
+++ b/src/doc/installing.dox
@@ -0,0 +1,9 @@
+// vim:ft=doxygen
+/**
+
+\defgroup install Installation
+\brief Engine installation instructions
+
+\todo This entire page
+
+*/
diff --git a/src/doc/layout.xml b/src/doc/layout.xml
new file mode 100644
index 0000000..2244fa7
--- /dev/null
+++ b/src/doc/layout.xml
@@ -0,0 +1,252 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<doxygenlayout version="1.0">
+ <navindex>
+ <tab type="mainpage" visible="yes" title=""/>
+ <tab type="pages" visible="no" title="" intro=""/>
+ <tab type="topics" visible="yes" title="" intro=""/>
+ <tab type="modules" visible="yes" title="" intro="">
+ <tab type="modulelist" visible="yes" title="" intro=""/>
+ <tab type="modulemembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="namespaces" visible="no" title="">
+ <tab type="namespacelist" visible="yes" title="" intro=""/>
+ <tab type="namespacemembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="concepts" visible="yes" title="">
+ </tab>
+ <tab type="interfaces" visible="yes" title="">
+ <tab type="interfacelist" visible="yes" title="" intro=""/>
+ <tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
+ <tab type="interfacehierarchy" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="classes" visible="yes" title="">
+ <tab type="classlist" visible="yes" title="" intro=""/>
+ <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
+ <tab type="hierarchy" visible="yes" title="" intro=""/>
+ <tab type="classmembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="structs" visible="yes" title="">
+ <tab type="structlist" visible="yes" title="" intro=""/>
+ <tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
+ </tab>
+ <tab type="exceptions" visible="yes" title="">
+ <tab type="exceptionlist" visible="yes" title="" intro=""/>
+ <tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
+ <tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="files" visible="yes" title="">
+ <tab type="filelist" visible="yes" title="" intro=""/>
+ <tab type="globals" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="examples" visible="yes" title="" intro=""/>
+ </navindex>
+ <class>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_HEADERFILE"/>
+ <inheritancegraph visible="yes"/>
+ <collaborationgraph visible="yes"/>
+ <memberdecl>
+ <nestedclasses visible="yes" title=""/>
+ <publictypes title=""/>
+ <services title=""/>
+ <interfaces title=""/>
+ <publicslots title=""/>
+ <signals title=""/>
+ <publicmethods title=""/>
+ <publicstaticmethods title=""/>
+ <publicattributes title=""/>
+ <publicstaticattributes title=""/>
+ <protectedtypes title=""/>
+ <protectedslots title=""/>
+ <protectedmethods title=""/>
+ <protectedstaticmethods title=""/>
+ <protectedattributes title=""/>
+ <protectedstaticattributes title=""/>
+ <packagetypes title=""/>
+ <packagemethods title=""/>
+ <packagestaticmethods title=""/>
+ <packageattributes title=""/>
+ <packagestaticattributes title=""/>
+ <properties title=""/>
+ <events title=""/>
+ <privatetypes title=""/>
+ <privateslots title=""/>
+ <privatemethods title=""/>
+ <privatestaticmethods title=""/>
+ <privateattributes title=""/>
+ <privatestaticattributes title=""/>
+ <friends title=""/>
+ <related title="" subtitle=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <services title=""/>
+ <interfaces title=""/>
+ <constructors title=""/>
+ <functions title=""/>
+ <related title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ <events title=""/>
+ </memberdef>
+ <allmemberslink visible="yes"/>
+ <usedfiles visible="$SHOW_USED_FILES"/>
+ <authorsection visible="yes"/>
+ </class>
+ <namespace>
+ <briefdescription visible="yes"/>
+ <memberdecl>
+ <nestednamespaces visible="yes" title=""/>
+ <constantgroups visible="yes" title=""/>
+ <interfaces visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <concepts visible="yes" title=""/>
+ <structs visible="yes" title=""/>
+ <exceptions visible="yes" title=""/>
+ <typedefs title=""/>
+ <sequences title=""/>
+ <dictionaries title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <sequences title=""/>
+ <dictionaries title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </namespace>
+ <concept>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_HEADERFILE"/>
+ <definition visible="yes" title=""/>
+ <detaileddescription title=""/>
+ <authorsection visible="yes"/>
+ </concept>
+ <file>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <includegraph visible="yes"/>
+ <includedbygraph visible="yes"/>
+ <sourcelink visible="yes"/>
+ <memberdecl>
+ <interfaces visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <structs visible="yes" title=""/>
+ <exceptions visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <concepts visible="yes" title=""/>
+ <constantgroups visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <sequences title=""/>
+ <dictionaries title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <sequences title=""/>
+ <dictionaries title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ </memberdef>
+ <authorsection/>
+ </file>
+ <group>
+ <detaileddescription title=""/>
+ <groupgraph visible="yes"/>
+ <memberdecl>
+ <nestedgroups visible="yes" title=""/>
+ <modules visible="yes" title=""/>
+ <dirs visible="yes" title=""/>
+ <files visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <concepts visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <sequences title=""/>
+ <dictionaries title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <memberdef>
+ <pagedocs/>
+ <inlineclasses title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <sequences title=""/>
+ <dictionaries title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </group>
+ <module>
+ <briefdescription visible="yes"/>
+ <exportedmodules visible="yes"/>
+ <memberdecl>
+ <concepts visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <enums title=""/>
+ <typedefs title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups title=""/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdecl>
+ <files visible="yes"/>
+ </memberdecl>
+ </module>
+ <directory>
+ <briefdescription visible="yes"/>
+ <directorygraph visible="yes"/>
+ <memberdecl>
+ <dirs visible="yes"/>
+ <files visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ </directory>
+</doxygenlayout>
diff --git a/src/doc/style.css b/src/doc/style.css
new file mode 100644
index 0000000..08bc9f5
--- /dev/null
+++ b/src/doc/style.css
@@ -0,0 +1,4 @@
+#titlearea,
+address {
+ display: none;
+}
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index ddb0262..7b4cc43 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -16,13 +16,7 @@ function(add_example target_name)
add_dependencies(examples ${target_name})
endfunction()
-add_example(audio_internal)
-# add_example(components_internal)
-add_example(script)
-add_example(log)
-add_example(rendering)
add_example(asset_manager)
-add_example(physics)
add_example(savemgr)
add_example(proxy)
add_example(db)
@@ -30,5 +24,6 @@ add_example(ecs)
add_example(scene_manager)
add_example(events)
add_example(particles)
+add_example(rendering_particle)
add_example(gameloop)
diff --git a/src/example/audio_internal.cpp b/src/example/audio_internal.cpp
deleted file mode 100644
index 661161a..0000000
--- a/src/example/audio_internal.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the internal \c Sound class.
- */
-
-#include <crepe/api/Config.h>
-#include <crepe/facade/Sound.h>
-#include <crepe/util/Log.h>
-
-#include <thread>
-
-using namespace crepe;
-using namespace std;
-using namespace std::chrono_literals;
-using std::make_unique;
-
-// Unrelated stuff that is not part of this POC
-int _ = []() {
- // Show dbg_trace() output
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::TRACE;
-
- return 0; // satisfy compiler
-}();
-
-int main() {
- // Load a background track (Ogg Vorbis)
- auto bgm = Sound("../mwe/audio/bgm.ogg");
- // Load three short samples (WAV)
- auto sfx1 = Sound("../mwe/audio/sfx1.wav");
- auto sfx2 = Sound("../mwe/audio/sfx2.wav");
- auto sfx3 = Sound("../mwe/audio/sfx3.wav");
-
- // Start the background track
- bgm.play();
-
- // Play each sample sequentially while pausing and resuming the background track
- this_thread::sleep_for(500ms);
- sfx1.play();
- this_thread::sleep_for(500ms);
- sfx2.play();
- bgm.pause();
- this_thread::sleep_for(500ms);
- sfx3.play();
- bgm.play();
- this_thread::sleep_for(500ms);
-
- // Play all samples simultaniously
- sfx1.play();
- sfx2.play();
- sfx3.play();
- this_thread::sleep_for(1000ms);
-
- // Stop all audio and exit
- return EXIT_SUCCESS;
-}
diff --git a/src/example/components_internal.cpp b/src/example/components_internal.cpp
deleted file mode 100644
index 2a232a9..0000000
--- a/src/example/components_internal.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the internal ECS
- */
-
-#include <cassert>
-#include <chrono>
-
-#include <crepe/Component.h>
-#include <crepe/ComponentManager.h>
-
-#include <crepe/api/GameObject.h>
-#include <crepe/api/Rigidbody.h>
-#include <crepe/api/Sprite.h>
-
-#include <crepe/util/Log.h>
-
-using namespace crepe;
-using namespace std;
-
-#define OBJ_COUNT 100000
-
-int main() {
- dbg_trace();
-
- ComponentManager mgr{};
-
- auto start_adding = chrono::high_resolution_clock::now();
-
- for (int i = 0; i < OBJ_COUNT; ++i) {
- GameObject obj = mgr.new_object("Name", "Tag");
- obj.add_component<Sprite>("test");
- obj.add_component<Rigidbody>(0, 0, i);
- }
-
- auto stop_adding = chrono::high_resolution_clock::now();
-
- auto sprites = mgr.get_components_by_type<Sprite>();
- for (auto sprite : sprites) {
- assert(true);
- }
-
- auto stop_looping = chrono::high_resolution_clock::now();
-
- auto add_time = chrono::duration_cast<chrono::microseconds>(stop_adding - start_adding);
- auto loop_time = chrono::duration_cast<chrono::microseconds>(stop_looping - stop_adding);
- printf("add time: %ldus\n", add_time.count());
- printf("loop time: %ldus\n", loop_time.count());
-
- return 0;
-}
diff --git a/src/example/db.cpp b/src/example/db.cpp
deleted file mode 100644
index ee4e8fc..0000000
--- a/src/example/db.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <crepe/api/Config.h>
-#include <crepe/facade/DB.h>
-#include <crepe/util/Log.h>
-
-using namespace crepe;
-using namespace std;
-
-// run before main
-static auto _ = []() {
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::TRACE;
- return 0;
-}();
-
-int main() {
- dbg_trace();
-
- DB db("file.db");
-
- const char * test_key = "test-key";
- string test_data = "Hello world!";
-
- dbg_logf("DB has key = {}", db.has(test_key));
-
- db.set(test_key, test_data);
-
- dbg_logf("key = \"{}\"", db.get(test_key));
-
- return 0;
-}
diff --git a/src/example/ecs.cpp b/src/example/ecs.cpp
deleted file mode 100644
index d5ba51b..0000000
--- a/src/example/ecs.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-#include <iostream>
-
-#include <crepe/ComponentManager.h>
-#include <crepe/api/GameObject.h>
-#include <crepe/api/Metadata.h>
-#include <crepe/api/Transform.h>
-
-using namespace crepe;
-using namespace std;
-
-int main() {
- ComponentManager mgr{};
-
- // Create a few GameObjects
- try {
- GameObject body = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
- GameObject right_leg = mgr.new_object("rightLeg", "person", Vector2{1, 1}, 0, 1);
- GameObject left_leg = mgr.new_object("leftLeg", "person", Vector2{1, 1}, 0, 1);
- GameObject right_foot = mgr.new_object("rightFoot", "person", Vector2{2, 2}, 0, 1);
- GameObject left_foot = mgr.new_object("leftFoot", "person", Vector2{2, 2}, 0, 1);
-
- // Set the parent of each GameObject
- right_foot.set_parent(right_leg);
- left_foot.set_parent(left_leg);
- right_leg.set_parent(body);
- left_leg.set_parent(body);
-
- // Adding a second Transform component is not allowed and will invoke an exception
- body.add_component<Transform>(Vector2{10, 10}, 0, 1);
- } catch (const exception & e) {
- cerr << e.what() << endl;
- }
-
- // Get the Metadata and Transform components of each GameObject
- vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>();
- vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>();
-
- // Print the Metadata and Transform components
- for (auto & m : metadata) {
- cout << "Id: " << m.get().game_object_id << " Name: " << m.get().name
- << " Tag: " << m.get().tag << " Parent: " << m.get().parent << " Children: ";
- for (auto & c : m.get().children) {
- cout << c << " ";
- }
- cout << endl;
- }
- for (auto & t : transform) {
- cout << "Id: " << t.get().game_object_id << " Position: [" << t.get().position.x
- << ", " << t.get().position.y << "]" << endl;
- }
-
- return 0;
-}
diff --git a/src/example/log.cpp b/src/example/log.cpp
deleted file mode 100644
index 5baa021..0000000
--- a/src/example/log.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the logging functions
- */
-
-#include <crepe/api/Config.h>
-#include <crepe/util/Log.h>
-
-using namespace crepe;
-
-// unrelated setup code
-int _ = []() {
- // make sure all log messages get printed
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::TRACE;
-
- return 0; // satisfy compiler
-}();
-
-int main() {
- dbg_trace();
- dbg_log("debug message");
- Log::logf("info message with variable: {}", 3);
- Log::logf(Log::Level::WARNING, "warning");
- Log::logf(Log::Level::ERROR, "error");
-
- return 0;
-}
diff --git a/src/example/particles.cpp b/src/example/particles.cpp
deleted file mode 100644
index 3d5f676..0000000
--- a/src/example/particles.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <crepe/ComponentManager.h>
-#include <crepe/api/AssetManager.h>
-
-#include <crepe/Component.h>
-#include <crepe/api/Color.h>
-#include <crepe/api/GameObject.h>
-#include <crepe/api/ParticleEmitter.h>
-#include <crepe/api/Rigidbody.h>
-#include <crepe/api/Sprite.h>
-#include <crepe/api/Texture.h>
-#include <crepe/api/Transform.h>
-
-using namespace crepe;
-using namespace std;
-
-int main(int argc, char * argv[]) {
- ComponentManager mgr{};
- GameObject game_object = mgr.new_object("", "", Vector2{0, 0}, 0, 0);
- Color color(0, 0, 0, 0);
- Sprite test_sprite = game_object.add_component<Sprite>(
- make_shared<Texture>("../asset/texture/img.png"), color, FlipSettings{true, true});
- game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{
- .position = {0, 0},
- .max_particles = 100,
- .emission_rate = 0,
- .min_speed = 0,
- .max_speed = 0,
- .min_angle = 0,
- .max_angle = 0,
- .begin_lifespan = 0,
- .end_lifespan = 0,
- .force_over_time = Vector2{0, 0},
- .boundary{
- .width = 0,
- .height = 0,
- .offset = Vector2{0, 0},
- .reset_on_exit = false,
- },
- .sprite = test_sprite,
- });
-
- return 0;
-}
diff --git a/src/example/physics.cpp b/src/example/physics.cpp
deleted file mode 100644
index ad663a0..0000000
--- a/src/example/physics.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <crepe/Component.h>
-#include <crepe/ComponentManager.h>
-#include <crepe/api/GameObject.h>
-#include <crepe/api/Rigidbody.h>
-#include <crepe/api/Transform.h>
-#include <crepe/system/PhysicsSystem.h>
-
-using namespace crepe;
-using namespace std;
-
-int main(int argc, char * argv[]) {
- ComponentManager mgr{};
-
- GameObject game_object = mgr.new_object("Name", "Tag", Vector2{0, 0}, 0, 0);
- game_object.add_component<Rigidbody>(Rigidbody::Data{
- .mass = 1,
- .gravity_scale = 1,
- .body_type = Rigidbody::BodyType::DYNAMIC,
- .constraints = {0, 0, 0},
- .use_gravity = true,
- .bounce = false,
- });
- return 0;
-}
diff --git a/src/example/proxy.cpp b/src/example/proxy.cpp
deleted file mode 100644
index 69451f8..0000000
--- a/src/example/proxy.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the proxy type
- */
-
-#include <crepe/ValueBroker.h>
-#include <crepe/api/Config.h>
-#include <crepe/util/Log.h>
-#include <crepe/util/Proxy.h>
-
-using namespace std;
-using namespace crepe;
-
-void test_ro_ref(const int & val) {}
-void test_rw_ref(int & val) {}
-void test_ro_val(int val) {}
-
-int main() {
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::DEBUG;
-
- int real_value = 0;
-
- ValueBroker<int> broker{
- [&real_value](const int & target) {
- dbg_logf("set {} to {}", real_value, target);
- real_value = target;
- },
- [&real_value]() -> const int & {
- dbg_logf("get {}", real_value);
- return real_value;
- },
- };
-
- Proxy<int> proxy{broker};
-
- broker.set(54);
- proxy = 84;
-
- test_ro_ref(proxy); // this is allowed
- // test_rw_ref(proxy); // this should throw a compile error
- test_ro_val(proxy);
-
- return 0;
-}
diff --git a/src/example/rendering.cpp b/src/example/rendering.cpp
deleted file mode 100644
index c9e62f1..0000000
--- a/src/example/rendering.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#include "api/Camera.h"
-#include <crepe/ComponentManager.h>
-#include <crepe/api/GameObject.h>
-#include <crepe/system/RenderSystem.h>
-#include <crepe/util/Log.h>
-
-#include <crepe/api/AssetManager.h>
-#include <crepe/api/Color.h>
-#include <crepe/api/Sprite.h>
-#include <crepe/api/Texture.h>
-#include <crepe/api/Transform.h>
-#include <crepe/api/Vector2.h>
-
-#include <chrono>
-#include <memory>
-
-using namespace std;
-using namespace crepe;
-
-int main() {
- dbg_trace();
-
- ComponentManager mgr{};
- RenderSystem sys{mgr};
-
- GameObject obj = mgr.new_object("name", "tag", Vector2{0, 0}, 1, 1);
- GameObject obj1 = mgr.new_object("name", "tag", Vector2{500, 0}, 1, 0.1);
- GameObject obj2 = mgr.new_object("name", "tag", Vector2{800, 0}, 1, 0.1);
-
- // Normal adding components
- {
- Color color(0, 0, 0, 0);
- obj.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"), color,
- FlipSettings{false, false});
- obj.add_component<Camera>(Color::get_red());
- }
- {
- Color color(0, 0, 0, 0);
- obj1.add_component<Sprite>(make_shared<Texture>("../asset/texture/second.png"), color,
- FlipSettings{true, true});
- }
-
- /*
- {
- Color color(0, 0, 0, 0);
- auto img = mgr.cache<Texture>("../asset/texture/second.png");
- obj2.add_component<Sprite>(img, color, FlipSettings{true, true});
- }
- */
-
- auto start = std::chrono::steady_clock::now();
- while (std::chrono::steady_clock::now() - start < std::chrono::seconds(5)) {
- sys.update();
- }
-}
diff --git a/src/example/rendering_particle.cpp b/src/example/rendering_particle.cpp
new file mode 100644
index 0000000..4571afb
--- /dev/null
+++ b/src/example/rendering_particle.cpp
@@ -0,0 +1,71 @@
+#include "api/Camera.h"
+#include "system/ParticleSystem.h"
+#include <SDL2/SDL_timer.h>
+#include <crepe/ComponentManager.h>
+
+#include <crepe/Component.h>
+#include <crepe/api/Color.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Texture.h>
+#include <crepe/api/Transform.h>
+#include <crepe/api/Vector2.h>
+#include <crepe/system/RenderSystem.h>
+
+#include <chrono>
+#include <iostream>
+#include <memory>
+
+using namespace crepe;
+using namespace std;
+
+int main(int argc, char * argv[]) {
+ ComponentManager mgr;
+ GameObject game_object = mgr.new_object("", "", Vector2{100, 100}, 0, 0.1);
+ RenderSystem sys{mgr};
+ ParticleSystem psys{mgr};
+
+ Color color(255, 255, 255, 255);
+
+ Sprite & test_sprite = game_object.add_component<Sprite>(
+ make_shared<Texture>("../asset/texture/img.png"), color, FlipSettings{false, false});
+ test_sprite.order_in_layer = 5;
+
+ auto & test = game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{
+ .position = {0, 0},
+ .max_particles = 10,
+ .emission_rate = 0.1,
+ .min_speed = 6,
+ .max_speed = 20,
+ .min_angle = -20,
+ .max_angle = 20,
+ .begin_lifespan = 0,
+ .end_lifespan = 60,
+ .force_over_time = Vector2{0, 0},
+ .boundary{
+ .width = 1000,
+ .height = 1000,
+ .offset = Vector2{0, 0},
+ .reset_on_exit = false,
+ },
+ .sprite = test_sprite,
+ });
+ game_object.add_component<Camera>(Color::WHITE);
+
+ game_object
+ .add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"), color,
+ FlipSettings{false, false})
+ .order_in_layer
+ = 6;
+
+ auto start = std::chrono::steady_clock::now();
+ while (std::chrono::steady_clock::now() - start < std::chrono::seconds(5)) {
+ psys.update();
+ sys.update();
+ SDL_Delay(10);
+ }
+
+ return 0;
+}
diff --git a/src/example/scene_manager.cpp b/src/example/scene_manager.cpp
deleted file mode 100644
index accec7d..0000000
--- a/src/example/scene_manager.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-#include <iostream>
-
-#include <crepe/ComponentManager.h>
-#include <crepe/api/GameObject.h>
-#include <crepe/api/Metadata.h>
-#include <crepe/api/Scene.h>
-#include <crepe/api/SceneManager.h>
-#include <crepe/api/Vector2.h>
-
-using namespace crepe;
-using namespace std;
-
-class ConcreteScene1 : public Scene {
-public:
- using Scene::Scene;
-
- void load_scene() {
- auto & mgr = this->component_manager;
- GameObject object1 = mgr.new_object("scene_1", "tag_scene_1", Vector2{0, 0}, 0, 1);
- GameObject object2 = mgr.new_object("scene_1", "tag_scene_1", Vector2{1, 0}, 0, 1);
- GameObject object3 = mgr.new_object("scene_1", "tag_scene_1", Vector2{2, 0}, 0, 1);
- }
-};
-
-class ConcreteScene2 : public Scene {
-public:
- using Scene::Scene;
-
- void load_scene() {
- auto & mgr = this->component_manager;
- GameObject object1 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 0}, 0, 1);
- GameObject object2 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 1}, 0, 1);
- GameObject object3 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 2}, 0, 1);
- GameObject object4 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 3}, 0, 1);
- }
-};
-
-int main() {
- ComponentManager component_mgr{};
- SceneManager scene_mgr{component_mgr};
-
- // Add the scenes to the scene manager
- scene_mgr.add_scene<ConcreteScene1>("scene1");
- scene_mgr.add_scene<ConcreteScene2>("scene2");
-
- // There is no need to call set_next_scene() at the beginnen, because the first scene will be
- // automatically set as the next scene
-
- // Load scene1 (the first scene added)
- scene_mgr.load_next_scene();
-
- // Get the Metadata components of each GameObject of Scene1
- vector<reference_wrapper<Metadata>> metadata
- = component_mgr.get_components_by_type<Metadata>();
-
- cout << "Metadata components of Scene1:" << endl;
- // Print the Metadata
- for (auto & m : metadata) {
- cout << "Id: " << m.get().game_object_id << " Name: " << m.get().name
- << " Tag: " << m.get().tag << endl;
- }
-
- // Set scene2 as the next scene
- scene_mgr.set_next_scene("scene2");
- // Load scene2
- scene_mgr.load_next_scene();
-
- // Get the Metadata components of each GameObject of Scene2
- metadata = component_mgr.get_components_by_type<Metadata>();
-
- cout << "Metadata components of Scene2:" << endl;
- // Print the Metadata
- for (auto & m : metadata) {
- cout << "Id: " << m.get().game_object_id << " Name: " << m.get().name
- << " Tag: " << m.get().tag << endl;
- }
-
- return 0;
-}
diff --git a/src/example/script.cpp b/src/example/script.cpp
deleted file mode 100644
index a23295b..0000000
--- a/src/example/script.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/** \file
- *
- * Standalone example for usage of the script component and system
- */
-
-#include <crepe/ComponentManager.h>
-#include <crepe/system/ScriptSystem.h>
-#include <crepe/util/Log.h>
-
-#include <crepe/api/BehaviorScript.h>
-#include <crepe/api/Config.h>
-#include <crepe/api/GameObject.h>
-#include <crepe/api/Script.h>
-#include <crepe/api/Transform.h>
-
-using namespace crepe;
-using namespace std;
-
-// Unrelated stuff that is not part of this POC
-int _ = []() {
- // Show dbg_trace() output
- auto & cfg = Config::get_instance();
- cfg.log.level = Log::Level::TRACE;
-
- return 0; // satisfy compiler
-}();
-
-// User-defined script:
-class MyScript : public Script {
- void update() {
- // Retrieve component from the same GameObject this script is on
- Transform & test = get_component<Transform>();
- dbg_logf("Transform({:.2f}, {:.2f})", test.position.x, test.position.y);
- }
-};
-
-int main() {
- ComponentManager component_manager{};
- ScriptSystem system{component_manager};
-
- // Create game object with Transform and BehaviorScript components
- GameObject obj = component_manager.new_object("name");
- obj.add_component<BehaviorScript>().set_script<MyScript>();
-
- // Update all scripts. This should result in MyScript::update being called
- system.update();
-
- return EXIT_SUCCESS;
-}
diff --git a/src/makefile b/src/makefile
index 5f80204..a0e8f02 100644
--- a/src/makefile
+++ b/src/makefile
@@ -1,6 +1,9 @@
.PHONY: FORCE
-FMT := $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp')
format: FORCE
- clang-tidy -p build/compile_commands.json --fix-errors $(FMT)
+ $(MAKE) -C .. $@
+
+LINT := $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp')
+lint: FORCE
+ clang-tidy -p build/compile_commands.json --fix-errors $(LINT)
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 6f6ad79..dc985a3 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -4,5 +4,9 @@ target_sources(test_main PUBLIC
ScriptTest.cpp
ParticleTest.cpp
EventTest.cpp
+ ECSTest.cpp
+ SceneManagerTest.cpp
+ ValueBrokerTest.cpp
+ DBTest.cpp
)
diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp
new file mode 100644
index 0000000..b57eba9
--- /dev/null
+++ b/src/test/DBTest.cpp
@@ -0,0 +1,29 @@
+#include <gtest/gtest.h>
+#include <crepe/facade/DB.h>
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+class DBTest : public Test {
+public:
+ DB db;
+};
+
+TEST_F(DBTest, ReadWrite) {
+ db.set("foo", "bar");
+ EXPECT_EQ(db.get("foo"), "bar");
+}
+
+TEST_F(DBTest, Nonexistant) {
+ EXPECT_THROW(db.get("foo"), std::out_of_range);
+ db.set("foo", "bar");
+ EXPECT_NO_THROW(db.get("foo"));
+}
+
+TEST_F(DBTest, Has) {
+ EXPECT_EQ(db.has("foo"), false);
+ db.set("foo", "bar");
+ EXPECT_EQ(db.has("foo"), true);
+}
+
diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp
new file mode 100644
index 0000000..d5a5826
--- /dev/null
+++ b/src/test/ECSTest.cpp
@@ -0,0 +1,236 @@
+#include <gtest/gtest.h>
+
+#define protected public
+
+#include <crepe/ComponentManager.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Metadata.h>
+#include <crepe/api/Transform.h>
+#include <crepe/api/Vector2.h>
+
+using namespace std;
+using namespace crepe;
+
+class ECSTest : public ::testing::Test {
+public:
+ ComponentManager mgr{};
+};
+
+TEST_F(ECSTest, createGameObject) {
+ GameObject obj = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+
+ vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>();
+ vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>();
+
+ EXPECT_EQ(metadata.size(), 1);
+ EXPECT_EQ(transform.size(), 1);
+
+ EXPECT_EQ(metadata[0].get().name, "body");
+ EXPECT_EQ(metadata[0].get().tag, "person");
+ EXPECT_EQ(metadata[0].get().parent, -1);
+ EXPECT_EQ(metadata[0].get().children.size(), 0);
+
+ EXPECT_EQ(transform[0].get().position.x, 0);
+ EXPECT_EQ(transform[0].get().position.y, 0);
+ EXPECT_EQ(transform[0].get().rotation, 0);
+ EXPECT_EQ(transform[0].get().scale, 1);
+}
+
+TEST_F(ECSTest, deleteAllGameObjects) {
+ GameObject obj0 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+ GameObject obj1 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+
+ mgr.delete_all_components();
+
+ vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>();
+ vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>();
+
+ EXPECT_EQ(metadata.size(), 0);
+ EXPECT_EQ(transform.size(), 0);
+
+ GameObject obj2 = mgr.new_object("body2", "person2", Vector2{1, 0}, 5, 1);
+
+ metadata = mgr.get_components_by_type<Metadata>();
+ transform = mgr.get_components_by_type<Transform>();
+
+ EXPECT_EQ(metadata.size(), 1);
+ EXPECT_EQ(transform.size(), 1);
+
+ EXPECT_EQ(metadata[0].get().game_object_id, 0);
+ EXPECT_EQ(metadata[0].get().name, "body2");
+ EXPECT_EQ(metadata[0].get().tag, "person2");
+ EXPECT_EQ(metadata[0].get().parent, -1);
+ EXPECT_EQ(metadata[0].get().children.size(), 0);
+
+ EXPECT_EQ(transform[0].get().game_object_id, 0);
+ EXPECT_EQ(transform[0].get().position.x, 1);
+ EXPECT_EQ(transform[0].get().position.y, 0);
+ EXPECT_EQ(transform[0].get().rotation, 5);
+ EXPECT_EQ(transform[0].get().scale, 1);
+}
+
+TEST_F(ECSTest, deleteGameObject) {
+ GameObject obj0 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+ GameObject obj1 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+
+ mgr.delete_all_components_of_id(0);
+
+ vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>();
+ vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>();
+
+ EXPECT_EQ(metadata.size(), 1);
+ EXPECT_EQ(transform.size(), 1);
+
+ EXPECT_EQ(metadata[0].get().game_object_id, 1);
+ EXPECT_EQ(metadata[0].get().name, "body");
+ EXPECT_EQ(metadata[0].get().tag, "person");
+ EXPECT_EQ(metadata[0].get().parent, -1);
+ EXPECT_EQ(metadata[0].get().children.size(), 0);
+
+ EXPECT_EQ(transform[0].get().game_object_id, 1);
+ EXPECT_EQ(transform[0].get().position.x, 0);
+ EXPECT_EQ(transform[0].get().position.y, 0);
+ EXPECT_EQ(transform[0].get().rotation, 0);
+ EXPECT_EQ(transform[0].get().scale, 1);
+}
+
+TEST_F(ECSTest, manyGameObjects) {
+ for (int i = 0; i < 5000; i++) {
+ GameObject obj = mgr.new_object("body", "person", Vector2{0, 0}, 0, i);
+ }
+
+ vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>();
+ vector<reference_wrapper<Transform>> transform = mgr.get_components_by_type<Transform>();
+
+ EXPECT_EQ(metadata.size(), 5000);
+ EXPECT_EQ(transform.size(), 5000);
+ for (int i = 0; i < 5000; i++) {
+ EXPECT_EQ(metadata[i].get().game_object_id, i);
+ EXPECT_EQ(metadata[i].get().name, "body");
+ EXPECT_EQ(metadata[i].get().tag, "person");
+ EXPECT_EQ(metadata[i].get().parent, -1);
+ EXPECT_EQ(metadata[i].get().children.size(), 0);
+
+ EXPECT_EQ(transform[i].get().game_object_id, i);
+ EXPECT_EQ(transform[i].get().position.x, 0);
+ EXPECT_EQ(transform[i].get().position.y, 0);
+ EXPECT_EQ(transform[i].get().rotation, 0);
+ EXPECT_EQ(transform[i].get().scale, i);
+ }
+
+ mgr.delete_components<Metadata>();
+
+ metadata = mgr.get_components_by_type<Metadata>();
+ transform = mgr.get_components_by_type<Transform>();
+
+ EXPECT_EQ(metadata.size(), 0);
+ EXPECT_EQ(transform.size(), 5000);
+
+ for (int i = 0; i < 10000 - 5000; i++) {
+ string tag = "person" + to_string(i);
+ GameObject obj = mgr.new_object("body", tag, Vector2{0, 0}, i, 0);
+ }
+
+ metadata = mgr.get_components_by_type<Metadata>();
+ transform = mgr.get_components_by_type<Transform>();
+
+ EXPECT_EQ(metadata.size(), 10000 - 5000);
+ EXPECT_EQ(transform.size(), 10000);
+}
+
+TEST_F(ECSTest, getComponentsByID) {
+ GameObject obj0 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+ GameObject obj1 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+
+ vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_id<Metadata>(0);
+ vector<reference_wrapper<Transform>> transform = mgr.get_components_by_id<Transform>(1);
+
+ EXPECT_EQ(metadata.size(), 1);
+ EXPECT_EQ(transform.size(), 1);
+
+ EXPECT_EQ(metadata[0].get().game_object_id, 0);
+ EXPECT_EQ(metadata[0].get().name, "body");
+ EXPECT_EQ(metadata[0].get().tag, "person");
+ EXPECT_EQ(metadata[0].get().parent, -1);
+ EXPECT_EQ(metadata[0].get().children.size(), 0);
+
+ EXPECT_EQ(transform[0].get().game_object_id, 1);
+ EXPECT_EQ(transform[0].get().position.x, 0);
+ EXPECT_EQ(transform[0].get().position.y, 0);
+ EXPECT_EQ(transform[0].get().rotation, 0);
+ EXPECT_EQ(transform[0].get().scale, 1);
+}
+
+TEST_F(ECSTest, tooMuchComponents) {
+ try {
+ GameObject obj0 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+ obj0.add_component<Transform>(Vector2{10, 10}, 0, 1);
+ } catch (const exception & e) {
+ EXPECT_EQ(e.what(),
+ string("Exceeded maximum number of instances for this component type"));
+ }
+
+ try {
+ GameObject obj1 = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+ obj1.add_component<Metadata>("body", "person");
+ } catch (const exception & e) {
+ EXPECT_EQ(e.what(),
+ string("Exceeded maximum number of instances for this component type"));
+ }
+
+ vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>();
+
+ EXPECT_EQ(metadata.size(), 2);
+ EXPECT_EQ(metadata[0].get().name, "body");
+ EXPECT_EQ(metadata[1].get().name, "body");
+}
+
+TEST_F(ECSTest, partentChild) {
+ {
+ GameObject body = mgr.new_object("body", "person", Vector2{0, 0}, 0, 1);
+ GameObject right_leg = mgr.new_object("rightLeg", "person", Vector2{1, 1}, 0, 1);
+ GameObject left_leg = mgr.new_object("leftLeg", "person", Vector2{1, 1}, 0, 1);
+ GameObject right_foot = mgr.new_object("rightFoot", "person", Vector2{2, 2}, 0, 1);
+ GameObject left_foot = mgr.new_object("leftFoot", "person", Vector2{2, 2}, 0, 1);
+
+ // Set the parent of each GameObject
+ right_foot.set_parent(right_leg);
+ left_foot.set_parent(left_leg);
+ right_leg.set_parent(body);
+ left_leg.set_parent(body);
+ }
+
+ // Get the Metadata and Transform components of each GameObject
+ vector<reference_wrapper<Metadata>> metadata = mgr.get_components_by_type<Metadata>();
+
+ // Check IDs
+ EXPECT_EQ(metadata[0].get().game_object_id, 0);
+ EXPECT_EQ(metadata[1].get().game_object_id, 1);
+ EXPECT_EQ(metadata[2].get().game_object_id, 2);
+ EXPECT_EQ(metadata[3].get().game_object_id, 3);
+ EXPECT_EQ(metadata[4].get().game_object_id, 4);
+
+ // Check the parent-child relationships
+ EXPECT_EQ(metadata[0].get().name, "body");
+ EXPECT_EQ(metadata[1].get().name, "rightLeg");
+ EXPECT_EQ(metadata[2].get().name, "leftLeg");
+ EXPECT_EQ(metadata[3].get().name, "rightFoot");
+ EXPECT_EQ(metadata[4].get().name, "leftFoot");
+
+ EXPECT_EQ(metadata[0].get().parent, -1);
+ EXPECT_EQ(metadata[1].get().parent, 0);
+ EXPECT_EQ(metadata[2].get().parent, 0);
+ EXPECT_EQ(metadata[3].get().parent, 1);
+ EXPECT_EQ(metadata[4].get().parent, 2);
+
+ EXPECT_EQ(metadata[0].get().children.size(), 2);
+ EXPECT_EQ(metadata[1].get().children.size(), 1);
+ EXPECT_EQ(metadata[2].get().children.size(), 1);
+ EXPECT_EQ(metadata[3].get().children.size(), 0);
+ EXPECT_EQ(metadata[4].get().children.size(), 0);
+
+ EXPECT_EQ(metadata[0].get().children[0], 1);
+ EXPECT_EQ(metadata[0].get().children[1], 2);
+ EXPECT_EQ(metadata[1].get().children[0], 3);
+ EXPECT_EQ(metadata[2].get().children[0], 4);
+}
diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp
index 4e655a9..d9bbba0 100644
--- a/src/test/ParticleTest.cpp
+++ b/src/test/ParticleTest.cpp
@@ -28,7 +28,7 @@ public:
GameObject game_object = mgr.new_object("", "", Vector2{0, 0}, 0, 0);
Color color(0, 0, 0, 0);
- Sprite test_sprite = game_object.add_component<Sprite>(
+ Sprite & test_sprite = game_object.add_component<Sprite>(
make_shared<Texture>("../asset/texture/img.png"), color,
FlipSettings{true, true});
diff --git a/src/test/RenderSystemTest.cpp b/src/test/RenderSystemTest.cpp
new file mode 100644
index 0000000..ac479d3
--- /dev/null
+++ b/src/test/RenderSystemTest.cpp
@@ -0,0 +1,174 @@
+#include "api/Camera.h"
+#include <functional>
+#include <gtest/gtest.h>
+#include <memory>
+#include <vector>
+
+#define private public
+#define protected public
+
+#include <crepe/ComponentManager.h>
+#include <crepe/api/Color.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Texture.h>
+
+#include <crepe/system/RenderSystem.h>
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+class RenderSystemTest : public Test {
+public:
+ ComponentManager mgr{};
+ RenderSystem sys{mgr};
+ GameObject entity1 = this->mgr.new_object("name");
+ GameObject entity2 = this->mgr.new_object("name");
+ GameObject entity3 = this->mgr.new_object("name");
+ GameObject entity4 = this->mgr.new_object("name");
+
+ void SetUp() override {
+ auto & sprite1
+ = entity1.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"),
+ Color(0, 0, 0, 0), FlipSettings{false, false});
+ ASSERT_NE(sprite1.sprite_image.get(), nullptr);
+ sprite1.order_in_layer = 5;
+ sprite1.sorting_in_layer = 5;
+ EXPECT_EQ(sprite1.order_in_layer, 5);
+ EXPECT_EQ(sprite1.sorting_in_layer, 5);
+ auto & sprite2
+ = entity2.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"),
+ Color(0, 0, 0, 0), FlipSettings{false, false});
+ ASSERT_NE(sprite2.sprite_image.get(), nullptr);
+ sprite2.sorting_in_layer = 2;
+ sprite2.order_in_layer = 1;
+
+ EXPECT_EQ(sprite2.sorting_in_layer, 2);
+ EXPECT_EQ(sprite2.order_in_layer, 1);
+
+ auto & sprite3
+ = entity3.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"),
+ Color(0, 0, 0, 0), FlipSettings{false, false});
+ ASSERT_NE(sprite3.sprite_image.get(), nullptr);
+ sprite3.sorting_in_layer = 1;
+ sprite3.order_in_layer = 2;
+
+ EXPECT_EQ(sprite3.sorting_in_layer, 1);
+ EXPECT_EQ(sprite3.order_in_layer, 2);
+
+ auto & sprite4
+ = entity4.add_component<Sprite>(make_shared<Texture>("../asset/texture/img.png"),
+ Color(0, 0, 0, 0), FlipSettings{false, false});
+ ASSERT_NE(sprite4.sprite_image.get(), nullptr);
+ sprite4.sorting_in_layer = 1;
+ sprite4.order_in_layer = 1;
+ EXPECT_EQ(sprite4.sorting_in_layer, 1);
+ EXPECT_EQ(sprite4.order_in_layer, 1);
+ }
+};
+
+TEST_F(RenderSystemTest, expected_throws) {
+ GameObject entity1 = this->mgr.new_object("NAME");
+
+ // no texture img
+ EXPECT_ANY_THROW({
+ entity1.add_component<Sprite>(make_shared<Texture>("NO_IMAGE"), Color(0, 0, 0, 0),
+ FlipSettings{false, false});
+ });
+
+ // No camera
+ EXPECT_ANY_THROW({ this->sys.update(); });
+}
+
+TEST_F(RenderSystemTest, make_sprites) {}
+
+TEST_F(RenderSystemTest, sorting_sprites) {
+ vector<reference_wrapper<Sprite>> sprites = this->mgr.get_components_by_type<Sprite>();
+ ASSERT_EQ(sprites.size(), 4);
+
+ vector<reference_wrapper<Sprite>> sorted_sprites = this->sys.sort(sprites);
+ ASSERT_EQ(sorted_sprites.size(), 4);
+
+ // Expected order after sorting:
+ // 1. sorting_in_layer: 1, order_in_layer: 1 (entity4)
+ // 2. sorting_in_layer: 1, order_in_layer: 2 (entity3)
+ // 3. sorting_in_layer: 2, order_in_layer: 1 (entity2)
+ // 4. sorting_in_layer: 5, order_in_layer: 5 (entity1)
+
+ EXPECT_EQ(sorted_sprites[0].get().sorting_in_layer, 1);
+ EXPECT_EQ(sorted_sprites[0].get().order_in_layer, 1);
+
+ EXPECT_EQ(sorted_sprites[1].get().sorting_in_layer, 1);
+ EXPECT_EQ(sorted_sprites[1].get().order_in_layer, 2);
+
+ EXPECT_EQ(sorted_sprites[2].get().sorting_in_layer, 2);
+ EXPECT_EQ(sorted_sprites[2].get().order_in_layer, 1);
+
+ EXPECT_EQ(sorted_sprites[3].get().sorting_in_layer, 5);
+ EXPECT_EQ(sorted_sprites[3].get().order_in_layer, 5);
+
+ for (size_t i = 1; i < sorted_sprites.size(); ++i) {
+ const Sprite & prev = sorted_sprites[i - 1].get();
+ const Sprite & curr = sorted_sprites[i].get();
+
+ if (prev.sorting_in_layer == curr.sorting_in_layer) {
+ EXPECT_LE(prev.order_in_layer, curr.order_in_layer);
+ } else {
+ EXPECT_LE(prev.sorting_in_layer, curr.sorting_in_layer);
+ }
+ }
+}
+
+TEST_F(RenderSystemTest, Update) {
+ entity1.add_component<Camera>(Color::WHITE);
+ {
+ vector<reference_wrapper<Sprite>> sprites = this->mgr.get_components_by_type<Sprite>();
+ ASSERT_EQ(sprites.size(), 4);
+
+ EXPECT_EQ(sprites[0].get().game_object_id, 0);
+ EXPECT_EQ(sprites[1].get().game_object_id, 1);
+ EXPECT_EQ(sprites[2].get().game_object_id, 2);
+ EXPECT_EQ(sprites[3].get().game_object_id, 3);
+ }
+ this->sys.update();
+ {
+ vector<reference_wrapper<Sprite>> sprites = this->mgr.get_components_by_type<Sprite>();
+ ASSERT_EQ(sprites.size(), 4);
+
+ EXPECT_EQ(sprites[0].get().game_object_id, 0);
+ EXPECT_EQ(sprites[1].get().game_object_id, 1);
+ EXPECT_EQ(sprites[2].get().game_object_id, 2);
+ EXPECT_EQ(sprites[3].get().game_object_id, 3);
+ }
+}
+
+TEST_F(RenderSystemTest, Camera) {
+ {
+ auto cameras = this->mgr.get_components_by_type<Camera>();
+ EXPECT_NE(cameras.size(), 1);
+ }
+ {
+ entity1.add_component<Camera>(Color::WHITE);
+ auto cameras = this->mgr.get_components_by_type<Camera>();
+ EXPECT_EQ(cameras.size(), 1);
+ }
+
+ //TODO improve with newer version
+}
+TEST_F(RenderSystemTest, Color) {
+ entity1.add_component<Camera>(Color::WHITE);
+ auto & sprite = this->mgr.get_components_by_id<Sprite>(entity1.id).front().get();
+ ASSERT_NE(sprite.sprite_image.get(), nullptr);
+
+ sprite.color = Color::GREEN;
+ EXPECT_EQ(sprite.color.r, Color::GREEN.r);
+ EXPECT_EQ(sprite.color.g, Color::GREEN.g);
+ EXPECT_EQ(sprite.color.b, Color::GREEN.b);
+ EXPECT_EQ(sprite.color.a, Color::GREEN.a);
+ this->sys.update();
+ EXPECT_EQ(sprite.color.r, Color::GREEN.r);
+ EXPECT_EQ(sprite.color.g, Color::GREEN.g);
+ EXPECT_EQ(sprite.color.b, Color::GREEN.b);
+ EXPECT_EQ(sprite.color.a, Color::GREEN.a);
+}
diff --git a/src/test/SceneManagerTest.cpp b/src/test/SceneManagerTest.cpp
new file mode 100644
index 0000000..69e1171
--- /dev/null
+++ b/src/test/SceneManagerTest.cpp
@@ -0,0 +1,122 @@
+#include <crepe/ComponentManager.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Metadata.h>
+#include <crepe/api/Scene.h>
+#include <crepe/api/SceneManager.h>
+#include <crepe/api/Transform.h>
+#include <crepe/api/Vector2.h>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace crepe;
+
+class ConcreteScene1 : public Scene {
+public:
+ using Scene::Scene;
+
+ void load_scene() {
+ auto & mgr = this->component_manager;
+ GameObject object1 = mgr.new_object("scene_1", "tag_scene_1", Vector2{0, 0}, 0, 1);
+ GameObject object2 = mgr.new_object("scene_1", "tag_scene_1", Vector2{1, 0}, 0, 1);
+ GameObject object3 = mgr.new_object("scene_1", "tag_scene_1", Vector2{2, 0}, 0, 1);
+ }
+};
+
+class ConcreteScene2 : public Scene {
+public:
+ using Scene::Scene;
+
+ void load_scene() {
+ auto & mgr = this->component_manager;
+ GameObject object1 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 0}, 0, 1);
+ GameObject object2 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 1}, 0, 1);
+ GameObject object3 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 2}, 0, 1);
+ GameObject object4 = mgr.new_object("scene_2", "tag_scene_2", Vector2{0, 3}, 0, 1);
+ }
+};
+
+class SceneManagerTest : public ::testing::Test {
+public:
+ ComponentManager component_mgr{};
+ SceneManager scene_mgr{component_mgr};
+};
+
+TEST_F(SceneManagerTest, loadScene) {
+ scene_mgr.add_scene<ConcreteScene1>("scene1");
+ scene_mgr.add_scene<ConcreteScene2>("scene2");
+
+ scene_mgr.load_next_scene();
+
+ vector<reference_wrapper<Metadata>> metadata
+ = component_mgr.get_components_by_type<Metadata>();
+ vector<reference_wrapper<Transform>> transform
+ = component_mgr.get_components_by_type<Transform>();
+
+ EXPECT_EQ(metadata.size(), 3);
+ EXPECT_EQ(transform.size(), 3);
+
+ EXPECT_EQ(metadata[0].get().game_object_id, 0);
+ EXPECT_EQ(metadata[0].get().name, "scene_1");
+ EXPECT_EQ(metadata[0].get().tag, "tag_scene_1");
+ EXPECT_EQ(metadata[0].get().parent, -1);
+ EXPECT_EQ(metadata[0].get().children.size(), 0);
+ EXPECT_EQ(transform[0].get().position.x, 0);
+ EXPECT_EQ(transform[0].get().position.y, 0);
+
+ EXPECT_EQ(metadata[1].get().game_object_id, 1);
+ EXPECT_EQ(metadata[1].get().name, "scene_1");
+ EXPECT_EQ(metadata[1].get().tag, "tag_scene_1");
+ EXPECT_EQ(metadata[1].get().parent, -1);
+ EXPECT_EQ(metadata[1].get().children.size(), 0);
+ EXPECT_EQ(transform[1].get().position.x, 1);
+ EXPECT_EQ(transform[1].get().position.y, 0);
+
+ EXPECT_EQ(metadata[2].get().game_object_id, 2);
+ EXPECT_EQ(metadata[2].get().name, "scene_1");
+ EXPECT_EQ(metadata[2].get().tag, "tag_scene_1");
+ EXPECT_EQ(metadata[2].get().parent, -1);
+ EXPECT_EQ(metadata[2].get().children.size(), 0);
+ EXPECT_EQ(transform[2].get().position.x, 2);
+ EXPECT_EQ(transform[2].get().position.y, 0);
+
+ scene_mgr.set_next_scene("scene2");
+ scene_mgr.load_next_scene();
+
+ metadata = component_mgr.get_components_by_type<Metadata>();
+ transform = component_mgr.get_components_by_type<Transform>();
+
+ EXPECT_EQ(metadata.size(), 4);
+ EXPECT_EQ(transform.size(), 4);
+
+ EXPECT_EQ(metadata[0].get().game_object_id, 0);
+ EXPECT_EQ(metadata[0].get().name, "scene_2");
+ EXPECT_EQ(metadata[0].get().tag, "tag_scene_2");
+ EXPECT_EQ(metadata[0].get().parent, -1);
+ EXPECT_EQ(metadata[0].get().children.size(), 0);
+ EXPECT_EQ(transform[0].get().position.x, 0);
+ EXPECT_EQ(transform[0].get().position.y, 0);
+
+ EXPECT_EQ(metadata[1].get().game_object_id, 1);
+ EXPECT_EQ(metadata[1].get().name, "scene_2");
+ EXPECT_EQ(metadata[1].get().tag, "tag_scene_2");
+ EXPECT_EQ(metadata[1].get().parent, -1);
+ EXPECT_EQ(metadata[1].get().children.size(), 0);
+ EXPECT_EQ(transform[1].get().position.x, 0);
+ EXPECT_EQ(transform[1].get().position.y, 1);
+
+ EXPECT_EQ(metadata[2].get().game_object_id, 2);
+ EXPECT_EQ(metadata[2].get().name, "scene_2");
+ EXPECT_EQ(metadata[2].get().tag, "tag_scene_2");
+ EXPECT_EQ(metadata[2].get().parent, -1);
+ EXPECT_EQ(metadata[2].get().children.size(), 0);
+ EXPECT_EQ(transform[2].get().position.x, 0);
+ EXPECT_EQ(transform[2].get().position.y, 2);
+
+ EXPECT_EQ(metadata[3].get().game_object_id, 3);
+ EXPECT_EQ(metadata[3].get().name, "scene_2");
+ EXPECT_EQ(metadata[3].get().tag, "tag_scene_2");
+ EXPECT_EQ(metadata[3].get().parent, -1);
+ EXPECT_EQ(metadata[3].get().children.size(), 0);
+ EXPECT_EQ(transform[3].get().position.x, 0);
+ EXPECT_EQ(transform[3].get().position.y, 3);
+}
diff --git a/src/test/ValueBrokerTest.cpp b/src/test/ValueBrokerTest.cpp
new file mode 100644
index 0000000..10a4654
--- /dev/null
+++ b/src/test/ValueBrokerTest.cpp
@@ -0,0 +1,64 @@
+#include <gtest/gtest.h>
+
+#include <crepe/ValueBroker.h>
+#include <crepe/util/Proxy.h>
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+class ValueBrokerTest : public Test {
+public:
+ int read_count = 0;
+ int write_count = 0;
+ int value = 0;
+
+ ValueBroker<int> broker {
+ [this](const int & target) -> void {
+ this->write_count++;
+ this->value = target;
+ },
+ [this]() -> const int & {
+ this->read_count++;
+ return this->value;
+ },
+ };
+ Proxy<int> proxy{broker};
+
+ void SetUp() override {
+ ASSERT_EQ(read_count, 0);
+ ASSERT_EQ(write_count, 0);
+ }
+};
+
+TEST_F(ValueBrokerTest, BrokerWrite) {
+ broker.set(0);
+ EXPECT_EQ(read_count, 0);
+ EXPECT_EQ(write_count, 1);
+}
+
+TEST_F(ValueBrokerTest, BrokerRead) {
+ broker.get();
+ EXPECT_EQ(read_count, 1);
+ EXPECT_EQ(write_count, 0);
+}
+
+TEST_F(ValueBrokerTest, ProxyWrite) {
+ proxy = 0;
+ EXPECT_EQ(read_count, 0);
+ EXPECT_EQ(write_count, 1);
+}
+
+void dummy(int) { }
+TEST_F(ValueBrokerTest, ProxyRead) {
+ dummy(proxy);
+ EXPECT_EQ(read_count, 1);
+ EXPECT_EQ(write_count, 0);
+}
+
+TEST_F(ValueBrokerTest, ProxyReadWrite) {
+ proxy = proxy;
+ ASSERT_EQ(read_count, 1);
+ ASSERT_EQ(write_count, 1);
+}
+