diff options
Diffstat (limited to 'src')
87 files changed, 1991 insertions, 308 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 09c60bd..b60a0cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_BUILD_TYPE Debug) project(crepe C CXX) find_package(SDL2 REQUIRED) +find_package(SDL2_image REQUIRED) find_package(SoLoud REQUIRED) find_package(GTest REQUIRED) @@ -20,7 +21,8 @@ target_include_directories(crepe target_link_libraries(crepe PRIVATE soloud - PRIVATE SDL2 + PUBLIC SDL2 + PUBLIC SDL2_image ) add_subdirectory(crepe) @@ -36,4 +38,3 @@ target_link_libraries(test_main PRIVATE gtest_main PUBLIC crepe ) - diff --git a/src/crepe/Asset.cpp b/src/crepe/Asset.cpp index 15ddc27..8a2a11c 100644 --- a/src/crepe/Asset.cpp +++ b/src/crepe/Asset.cpp @@ -5,7 +5,9 @@ using namespace crepe; Asset::Asset(const std::string & src) { - this->src = std::filesystem::canonical(src); + // FIXME: restore this + // this->src = std::filesystem::canonical(src); + this->src = src; this->file = std::ifstream(this->src, std::ios::in | std::ios::binary); } diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index d85aef0..d938eb8 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -2,30 +2,35 @@ target_sources(crepe PUBLIC Asset.cpp Sound.cpp SoundContext.cpp + Particle.cpp + ParticleSystem.cpp + SDLApp.cpp ComponentManager.cpp Component.cpp - GameObject.cpp - Collider.cpp - Rigidbody.cpp - Sprite.cpp ScriptSystem.cpp - Script.cpp + PhysicsSystem.cpp + CollisionSystem.cpp + Collider.cpp + SDLContext.cpp + + RenderSystem.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES Asset.h Sound.h SoundContext.h + SDLContext.h ComponentManager.h ComponentManager.hpp Component.h - GameObject.h - GameObject.hpp - Collider.h - Rigidbody.h - Sprite.h System.h ScriptSystem.h + PhysicsSystem.h + CollisionSystem.h + Collider.h + SDLContext.h + RenderSystem.h ) add_subdirectory(api) diff --git a/src/crepe/Collider.cpp b/src/crepe/Collider.cpp index 3f12afd..13a3f33 100644 --- a/src/crepe/Collider.cpp +++ b/src/crepe/Collider.cpp @@ -2,4 +2,4 @@ using namespace crepe; -Collider::Collider(int size) : size(size) {} +Collider::Collider(uint32_t gameObjectId) : Component(gameObjectId) {} diff --git a/src/crepe/Collider.h b/src/crepe/Collider.h index 120da05..68a7d1d 100644 --- a/src/crepe/Collider.h +++ b/src/crepe/Collider.h @@ -6,7 +6,7 @@ namespace crepe { class Collider : public Component { public: - Collider(int size); + Collider(uint32_t game_object_id); int size; }; diff --git a/src/crepe/CollisionSystem.cpp b/src/crepe/CollisionSystem.cpp new file mode 100644 index 0000000..55e0fdc --- /dev/null +++ b/src/crepe/CollisionSystem.cpp @@ -0,0 +1,7 @@ +#include "CollisionSystem.h" + +using namespace crepe; + +CollisionSystem::CollisionSystem() {} + +void CollisionSystem::update() {} diff --git a/src/crepe/CollisionSystem.h b/src/crepe/CollisionSystem.h new file mode 100644 index 0000000..1e9f1aa --- /dev/null +++ b/src/crepe/CollisionSystem.h @@ -0,0 +1,11 @@ +#pragma once + +namespace crepe { + +class CollisionSystem { +public: + CollisionSystem(); + void update(); +}; + +} // namespace crepe diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp index d14159c..358ce31 100644 --- a/src/crepe/Component.cpp +++ b/src/crepe/Component.cpp @@ -2,4 +2,4 @@ using namespace crepe; -Component::Component() : active(true) {} +Component::Component(uint32_t id) : game_object_id(id), active(true) {} diff --git a/src/crepe/Component.h b/src/crepe/Component.h index 16a4ce5..bc44865 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -1,13 +1,21 @@ #pragma once +#include <cstdint> + namespace crepe { +class ComponentManager; + class Component { +protected: + friend class crepe::ComponentManager; + Component(uint32_t id); + public: - Component(); - // TODO: shouldn't this constructor be deleted because this class will never - // directly be instantiated? + virtual ~Component() = default; +public: + uint32_t game_object_id; bool active; }; diff --git a/src/crepe/ComponentManager.cpp b/src/crepe/ComponentManager.cpp index 9a3fec7..8bde33a 100644 --- a/src/crepe/ComponentManager.cpp +++ b/src/crepe/ComponentManager.cpp @@ -1,4 +1,5 @@ #include "ComponentManager.h" +#include "util/log.h" using namespace crepe; @@ -20,5 +21,9 @@ void ComponentManager::delete_all_components_of_id(uint32_t id) { void ComponentManager::delete_all_components() { // Clear the whole unordered_map<> - components.clear(); + this->components.clear(); } + +ComponentManager::ComponentManager() { dbg_trace(); } + +ComponentManager::~ComponentManager() { dbg_trace(); } diff --git a/src/crepe/ComponentManager.h b/src/crepe/ComponentManager.h index 2ab9dc8..2b5e1df 100644 --- a/src/crepe/ComponentManager.h +++ b/src/crepe/ComponentManager.h @@ -4,7 +4,6 @@ #include <memory> #include <typeindex> #include <unordered_map> -#include <utility> #include <vector> #include "Component.h" @@ -23,7 +22,7 @@ public: public: //! Add a component of a specific type template <typename T, typename... Args> - void add_component(uint32_t id, Args &&... args); + T & add_component(uint32_t id, Args &&... args); //! Deletes all components of a specific type and id template <typename T> void delete_components_by_id(uint32_t id); @@ -44,7 +43,8 @@ public: std::vector<std::reference_wrapper<T>> get_components_by_type() const; private: - ComponentManager() = default; + ComponentManager(); + virtual ~ComponentManager(); /* * The std::unordered_map<std::type_index, std::vector<std::vector<std::unique_ptr<Component>>>> below might seem a bit strange, let me explain this structure: diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp index 084cd33..9b07f13 100644 --- a/src/crepe/ComponentManager.hpp +++ b/src/crepe/ComponentManager.hpp @@ -7,10 +7,11 @@ namespace crepe { template <class T, typename... Args> -void ComponentManager::add_component(uint32_t id, Args &&... args) { +T & ComponentManager::add_component(uint32_t id, Args &&... args) { using namespace std; - static_assert(is_base_of<Component, T>::value, "add_component must recieve a derivative class of Component"); + static_assert(is_base_of<Component, T>::value, + "add_component must recieve a derivative class of Component"); // Determine the type of T (this is used as the key of the unordered_map<>) type_index type = typeid(T); @@ -29,9 +30,11 @@ void ComponentManager::add_component(uint32_t id, Args &&... args) { // Create a new component of type T (arguments directly forwarded). The // constructor must be called by ComponentManager. - T * instance = new T(forward<Args>(args)...); + T * instance = new T(id, forward<Args>(args)...); // store its unique_ptr in the vector<> components[type][id].push_back(unique_ptr<T>(instance)); + + return *instance; } template <typename T> @@ -60,11 +63,9 @@ void ComponentManager::delete_components() { // Determine the type of T (this is used as the key of the unordered_map<>) std::type_index type = typeid(T); - // Find the type (in the unordered_map<>) - if (components.find(type) != components.end()) { - // Clear the whole vector<> of this specific type - components[type].clear(); - } + if (components.find(type) == components.end()) return; + + components[type].clear(); } template <typename T> @@ -78,30 +79,26 @@ ComponentManager::get_components_by_id(uint32_t id) const { // Create an empty vector<> vector<reference_wrapper<T>> component_vector; - // Find the type (in the unordered_map<>) - if (components.find(type) != components.end()) { - // Get the correct vector<> - const vector<vector<unique_ptr<Component>>> & component_array - = components.at(type); + if (components.find(type) == components.end()) return component_vector; - // Make sure that the id (that we are looking for) is within the boundaries of the vector<> - if (id < component_array.size()) { - // Loop trough the whole vector<> - for (const unique_ptr<Component> & component_ptr : - component_array[id]) { - // Cast the unique_ptr to a raw pointer - T * casted_component = static_cast<T *>(component_ptr.get()); - - // Ensure that the cast was successful - if (casted_component) { - // Add the dereferenced raw pointer to the vector<> - component_vector.push_back(*casted_component); - } - } - } + // Get the correct vector<> + const vector<vector<unique_ptr<Component>>> & component_array + = components.at(type); + + // Make sure that the id (that we are looking for) is within the boundaries of the vector<> + if (id >= component_array.size()) return component_vector; + + // Loop trough the whole vector<> + for (const unique_ptr<Component> & component_ptr : component_array[id]) { + // Cast the unique_ptr to a raw pointer + T * casted_component = static_cast<T *>(component_ptr.get()); + + if (casted_component == nullptr) continue; + + // Add the dereferenced raw pointer to the vector<> + component_vector.push_back(*casted_component); } - // Return the vector<> return component_vector; } @@ -119,30 +116,28 @@ ComponentManager::get_components_by_type() const { // uint32_t id = 0; // Find the type (in the unordered_map<>) - if (components.find(type) != components.end()) { + if (components.find(type) == components.end()) return component_vector; - // Get the correct vector<> - const vector<vector<unique_ptr<Component>>> & component_array - = components.at(type); - - // Loop through the whole vector<> - for (const vector<unique_ptr<Component>> & component : - component_array) { - // Loop trough the whole vector<> - for (const unique_ptr<Component> & component_ptr : component) { - // Cast the unique_ptr to a raw pointer - T * casted_component = static_cast<T *>(component_ptr.get()); - - // Ensure that the cast was successful - if (casted_component) { - // Pair the dereferenced raw pointer and the id and add it to the vector<> - component_vector.emplace_back(ref(*casted_component)); - } - } - - // Increase the id (the id will also be stored in the returned vector<>) - //++id; + // Get the correct vector<> + const vector<vector<unique_ptr<Component>>> & component_array + = components.at(type); + + // Loop through the whole vector<> + for (const vector<unique_ptr<Component>> & component : component_array) { + // Loop trough the whole vector<> + for (const unique_ptr<Component> & component_ptr : component) { + // Cast the unique_ptr to a raw pointer + T * casted_component = static_cast<T *>(component_ptr.get()); + + // Ensure that the cast was successful + if (casted_component == nullptr) continue; + + // Pair the dereferenced raw pointer and the id and add it to the vector<> + component_vector.emplace_back(ref(*casted_component)); } + + // Increase the id (the id will also be stored in the returned vector<>) + //++id; } // Return the vector<> diff --git a/src/crepe/GameObject.cpp b/src/crepe/GameObject.cpp deleted file mode 100644 index de3beb6..0000000 --- a/src/crepe/GameObject.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "GameObject.h" - -using namespace crepe; - -GameObject::GameObject(uint32_t id, std::string name, std::string tag, - int layer) - : id(id), name(name), tag(tag), active(true), layer(layer) {} diff --git a/src/crepe/GameObject.hpp b/src/crepe/GameObject.hpp deleted file mode 100644 index 5966fbf..0000000 --- a/src/crepe/GameObject.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "GameObject.h" - -#include "ComponentManager.h" - -namespace crepe { - -template <typename T, typename... Args> -void GameObject::add_component(Args &&... args) { - auto & mgr = ComponentManager::get_instance(); - mgr.add_component<T>(id, std::forward<Args>(args)...); -} - -} // namespace crepe diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp new file mode 100644 index 0000000..4810e80 --- /dev/null +++ b/src/crepe/Particle.cpp @@ -0,0 +1,20 @@ +#include "Particle.h" + +using namespace crepe; + +Particle::Particle() { this->active = false; } + +void Particle::reset(float lifespan, Position position, Position velocity) { + this->time_in_life = 0; + this->lifespan = lifespan; + this->position = position; + this->velocity = velocity; + this->active = true; +} + +void Particle::update(float deltaTime) { + time_in_life += deltaTime; + position.x += velocity.x * deltaTime; + position.y += velocity.y * deltaTime; + if (time_in_life >= lifespan) this->active = false; +} diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h new file mode 100644 index 0000000..21e691d --- /dev/null +++ b/src/crepe/Particle.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Position.h" + +namespace crepe { + +class Particle { +public: + Position position; + // FIXME: `Position` is an awkward name for a 2D vector. See FIXME comment in + // api/Transform.h for fix proposal. + Position velocity; + float lifespan; + bool active; + + Particle(); + void reset(float lifespan, Position position, Position velocity); + void update(float deltaTime); + float time_in_life; +}; + +} // namespace crepe diff --git a/src/crepe/ParticleSystem.cpp b/src/crepe/ParticleSystem.cpp new file mode 100644 index 0000000..af6c550 --- /dev/null +++ b/src/crepe/ParticleSystem.cpp @@ -0,0 +1,62 @@ +#include <cmath> +#include <ctime> + +#include "api/ParticleEmitter.h" + +#include "ComponentManager.h" +#include "ParticleSystem.h" + +using namespace crepe; + +ParticleSystem::ParticleSystem() : elapsed_time(0.0f) {} + +void ParticleSystem::update() { + ComponentManager & mgr = ComponentManager::get_instance(); + std::vector<std::reference_wrapper<ParticleEmitter>> emitters + = mgr.get_components_by_type<ParticleEmitter>(); + float delta_time = 0.10; + for (ParticleEmitter & emitter : emitters) { + float update_amount = 1 / static_cast<float>(emitter.emission_rate); + for (float i = 0; i < delta_time; i += update_amount) { + emit_particle(emitter); + } + for (size_t j = 0; j < emitter.particles.size(); j++) { + if (emitter.particles[j].active) { + emitter.particles[j].update(delta_time); + } + } + } +} + +void ParticleSystem::emit_particle(ParticleEmitter & emitter) { + Position initial_position = {emitter.position.x, emitter.position.y}; + float random_angle = 0.0f; + if (emitter.max_angle < emitter.min_angle) { + random_angle = ((emitter.min_angle + + (std::rand() + % (static_cast<uint32_t>(emitter.max_angle + 360 + - emitter.min_angle + 1)))) + % 360); + } else { + random_angle = emitter.min_angle + + (std::rand() + % (static_cast<uint32_t>(emitter.max_angle + - emitter.min_angle + 1))); + } + float angle_in_radians = random_angle * (M_PI / 180.0f); + float random_speed_offset = (static_cast<float>(std::rand()) / RAND_MAX) + * (2 * emitter.speed_offset) + - emitter.speed_offset; + float velocity_x + = (emitter.speed + random_speed_offset) * std::cos(angle_in_radians); + float velocity_y + = (emitter.speed + random_speed_offset) * std::sin(angle_in_radians); + Position initial_velocity = {velocity_x, velocity_y}; + for (size_t i = 0; i < emitter.particles.size(); i++) { + if (!emitter.particles[i].active) { + emitter.particles[i].reset(emitter.end_lifespan, initial_position, + initial_velocity); + break; + } + } +} diff --git a/src/crepe/ParticleSystem.h b/src/crepe/ParticleSystem.h new file mode 100644 index 0000000..ad96eb0 --- /dev/null +++ b/src/crepe/ParticleSystem.h @@ -0,0 +1,18 @@ +#pragma once + +#include "api/ParticleEmitter.h" + +namespace crepe { + +class ParticleSystem { +public: + ParticleSystem(); + void update(); + +private: + void emit_particle(ParticleEmitter & emitter); //emits a new particle + + float elapsed_time; //elapsed time since the last emission +}; + +} // namespace crepe diff --git a/src/crepe/PhysicsSystem.cpp b/src/crepe/PhysicsSystem.cpp new file mode 100644 index 0000000..16f4c10 --- /dev/null +++ b/src/crepe/PhysicsSystem.cpp @@ -0,0 +1,62 @@ +#include <iostream> + +#include "api/Force.h" +#include "api/Rigidbody.h" +#include "api/Transform.h" + +#include "ComponentManager.h" +#include "PhysicsSystem.h" + +using namespace crepe; +using namespace crepe::api; + +PhysicsSystem::PhysicsSystem() {} + +void PhysicsSystem::update() { + ComponentManager & mgr = ComponentManager::get_instance(); + std::vector<std::reference_wrapper<Rigidbody>> rigidbodies + = mgr.get_components_by_type<Rigidbody>(); + std::vector<std::reference_wrapper<Transform>> transforms + = mgr.get_components_by_type<Transform>(); + + for (Rigidbody & rigidbody : rigidbodies) { + + switch (rigidbody.body_type) { + case BodyType::DYNAMIC: + for (Transform & transform : transforms) { + if (transform.game_object_id == rigidbody.game_object_id) { + rigidbody.velocity_x = 0; + rigidbody.velocity_y = 0; + std::vector<std::reference_wrapper<Force>> forces + = mgr.get_components_by_id<Force>( + rigidbody.game_object_id); + rigidbody.velocity_y + += rigidbody.gravity_scale * 1 * rigidbody.mass; + + for (Force & force : forces) { + rigidbody.velocity_x += force.force_x; + rigidbody.velocity_y += force.force_y; + } + + std::cout << "before transform.postion.x " + << transform.position.x << std::endl; + std::cout << "before transform.postion.y " + << transform.position.y << std::endl; + transform.position.x += rigidbody.velocity_x; + transform.position.y += rigidbody.velocity_y; + std::cout << "after transform.postion.x " + << transform.position.x << std::endl; + std::cout << "after transform.postion.y " + << transform.position.y << std::endl; + } + } + break; + case BodyType::KINEMATIC: + break; //(scripts) + case BodyType::STATIC: + break; //(unmoveable objects) + default: + break; + } + } +} diff --git a/src/crepe/PhysicsSystem.h b/src/crepe/PhysicsSystem.h new file mode 100644 index 0000000..33b4072 --- /dev/null +++ b/src/crepe/PhysicsSystem.h @@ -0,0 +1,11 @@ +#pragma once + +namespace crepe { + +class PhysicsSystem { +public: + PhysicsSystem(); + void update(); +}; + +} // namespace crepe diff --git a/src/crepe/Position.h b/src/crepe/Position.h new file mode 100644 index 0000000..f84b63d --- /dev/null +++ b/src/crepe/Position.h @@ -0,0 +1,10 @@ +#pragma once + +namespace crepe { + +struct Position { + float x = 0.0; + float y = 0.0; +}; + +} // namespace crepe diff --git a/src/crepe/RenderSystem.cpp b/src/crepe/RenderSystem.cpp new file mode 100644 index 0000000..fae93f0 --- /dev/null +++ b/src/crepe/RenderSystem.cpp @@ -0,0 +1,42 @@ +#include <functional> +#include <vector> + +#include "api/Sprite.h" +#include "api/Transform.h" +#include "util/log.h" + +#include "ComponentManager.h" +#include "RenderSystem.h" +#include "SDLContext.h" + +using namespace crepe; +using namespace crepe::api; + +RenderSystem::RenderSystem() { dbg_trace(); } + +RenderSystem::~RenderSystem() { dbg_trace(); } + +RenderSystem & RenderSystem::get_instance() { + static RenderSystem instance; + return instance; +} + +void RenderSystem::update() { + + ComponentManager & mgr = ComponentManager::get_instance(); + + std::vector<std::reference_wrapper<Sprite>> sprites + = mgr.get_components_by_type<Sprite>(); + + SDLContext & render = SDLContext::get_instance(); + render.clear_screen(); + + for (const Sprite & sprite : sprites) { + std::vector<std::reference_wrapper<Transform>> transforms + = mgr.get_components_by_id<Transform>(sprite.game_object_id); + for (const Transform & transform : transforms) { + render.draw(sprite, transform); + } + } + render.present_screen(); +} diff --git a/src/crepe/RenderSystem.h b/src/crepe/RenderSystem.h new file mode 100644 index 0000000..4b910a4 --- /dev/null +++ b/src/crepe/RenderSystem.h @@ -0,0 +1,17 @@ +#pragma once + +#include "System.h" + +namespace crepe { + +class RenderSystem : public System { + +public: + static RenderSystem & get_instance(); + void update(); + +private: + RenderSystem(); + ~RenderSystem(); +}; +} // namespace crepe diff --git a/src/crepe/Rigidbody.cpp b/src/crepe/Rigidbody.cpp deleted file mode 100644 index 495d908..0000000 --- a/src/crepe/Rigidbody.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "Rigidbody.h" - -using namespace crepe; - -Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType) - : mass(mass), gravity_scale(gravityScale), body_type(bodyType) {} diff --git a/src/crepe/Rigidbody.h b/src/crepe/Rigidbody.h deleted file mode 100644 index 63a8877..0000000 --- a/src/crepe/Rigidbody.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "Component.h" - -namespace crepe { - -class Rigidbody : public Component { -public: - Rigidbody(int mass, int gravityScale, int bodyType); - - int mass; - int gravity_scale; - int body_type; -}; - -} // namespace crepe diff --git a/src/crepe/SDLApp.cpp b/src/crepe/SDLApp.cpp new file mode 100644 index 0000000..c6ddeaa --- /dev/null +++ b/src/crepe/SDLApp.cpp @@ -0,0 +1,71 @@ +#include <iostream> + +#include "SDLApp.h" + +SDLApp::SDLApp(int window_width, int window_height) + : window_width(window_width), window_height(window_height), window(nullptr), + renderer(nullptr) {} + +// FIXME: why is there clean_up and ~SDLApp? +SDLApp::~SDLApp() { clean_up(); } + +bool SDLApp::initialize() { + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + // FIXME: throw exception + std::cerr << "SDL Initialization Error: " << SDL_GetError() + << std::endl; + return false; + } + + window = SDL_CreateWindow("Particle System", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, window_width, + window_height, SDL_WINDOW_SHOWN); + if (!window) { + // FIXME: throw exception + std::cerr << "Window Creation Error: " << SDL_GetError() << std::endl; + return false; + } + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!renderer) { + // FIXME: throw exception + std::cerr << "Renderer Creation Error: " << SDL_GetError() << std::endl; + return false; + } + + return true; +} + +void SDLApp::handle_events(bool & running) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + running = false; + } + } +} + +void SDLApp::clear_screen() { + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); +} + +void SDLApp::present_screen() { SDL_RenderPresent(renderer); } + +void SDLApp::draw_square(int x, int y, int size) { + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + SDL_Rect rect = {x, y, size, size}; + SDL_RenderFillRect(renderer, &rect); +} + +SDL_Texture * square_texture = nullptr; // Load this with an image or create it + +void SDLApp::clean_up() { + if (renderer) { + SDL_DestroyRenderer(renderer); + } + if (window) { + SDL_DestroyWindow(window); + } + SDL_Quit(); +} diff --git a/src/crepe/SDLApp.h b/src/crepe/SDLApp.h new file mode 100644 index 0000000..e67947b --- /dev/null +++ b/src/crepe/SDLApp.h @@ -0,0 +1,26 @@ +#pragma once + +#include <SDL2/SDL.h> + +#include "api/ParticleEmitter.h" + +class SDLApp { +public: + SDLApp(int window_width, int window_height); + ~SDLApp(); + + bool initialize(); + void handle_events(bool & running); + void clear_screen(); + void present_screen(); + void draw_square(int x, int y, int size); + void clean_up(); + void draw_particles(const std::vector<crepe::ParticleEmitter> & emitters); + void draw_multiple_squares(const std::vector<SDL_Rect> & squares); + +private: + int window_width; + int window_height; + SDL_Window * window; + SDL_Renderer * renderer; +}; diff --git a/src/crepe/SDLContext.cpp b/src/crepe/SDLContext.cpp new file mode 100644 index 0000000..8bc5bc6 --- /dev/null +++ b/src/crepe/SDLContext.cpp @@ -0,0 +1,158 @@ +#include <SDL2/SDL.h> +#include <SDL2/SDL_image.h> +#include <SDL2/SDL_render.h> +#include <SDL2/SDL_surface.h> +#include <SDL2/SDL_video.h> +#include <cmath> +#include <cstddef> +#include <iostream> + +#include "api/Sprite.h" +#include "api/Texture.h" +#include "api/Transform.h" +#include "util/log.h" + +#include "SDLContext.h" + +using namespace crepe; + +SDLContext & SDLContext::get_instance() { + static SDLContext instance; + return instance; +} + +void SDLContext::handle_events(bool & running) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + running = false; + } + } +} + +SDLContext::~SDLContext() { + dbg_trace(); + + if (this->game_renderer != nullptr) + SDL_DestroyRenderer(this->game_renderer); + + if (this->game_window != nullptr) { + SDL_DestroyWindow(this->game_window); + } + + // TODO: how are we going to ensure that these are called from the same + // thread that SDL_Init() was called on? This has caused problems for me + // before. + IMG_Quit(); + SDL_Quit(); +} + +void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer); } + +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; + } + + this->game_window = SDL_CreateWindow( + "Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + 1920, 1080, SDL_WINDOW_SHOWN); + if (!this->game_window) { + // FIXME: throw exception + std::cerr << "Window could not be created! SDL_Error: " + << SDL_GetError() << std::endl; + } + + this->game_renderer + = SDL_CreateRenderer(this->game_window, -1, SDL_RENDERER_ACCELERATED); + if (!this->game_renderer) { + // FIXME: throw exception + std::cerr << "Renderer could not be created! SDL_Error: " + << SDL_GetError() << std::endl; + SDL_DestroyWindow(this->game_window); + return; + } + + 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; + } +} + +void SDLContext::present_screen() { SDL_RenderPresent(this->game_renderer); } + +void SDLContext::draw(const api::Sprite & sprite, + const api::Transform & transform) { + + static SDL_RendererFlip render_flip + = (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x) + | (SDL_FLIP_VERTICAL * sprite.flip.flip_y)); + + int w, h; + SDL_QueryTexture(sprite.sprite_image->texture, NULL, NULL, &w, &h); + // needs maybe camera for position + SDL_Rect dstrect = { + .x = static_cast<int>(transform.position.x), + .y = static_cast<int>(transform.position.y), + .w = static_cast<int>(w * transform.scale), + .h = static_cast<int>(h * transform.scale), + }; + + double degrees = transform.rotation * 180 / M_PI; + SDL_RenderCopyEx(this->game_renderer, sprite.sprite_image->texture, NULL, + &dstrect, degrees, NULL, render_flip); +} + +/* +SDL_Texture * SDLContext::setTextureFromPath(const char * path, SDL_Rect & clip, + const int row, const int col) { + dbg_trace(); + + SDL_Surface * tmp = IMG_Load(path); + if (!tmp) { + std::cerr << "Error surface " << IMG_GetError << std::endl; + } + + clip. + w = tmp->w / col; + clip.h = tmp->h / row; + + SDL_Texture * CreatedTexture + = SDL_CreateTextureFromSurface(this->game_renderer, tmp); + + if (!CreatedTexture) { + std::cerr << "Error could not create texture " << IMG_GetError + << std::endl; + } + SDL_FreeSurface(tmp); + + return CreatedTexture; +} +*/ + +SDL_Texture * SDLContext::texture_from_path(const char * path) { + dbg_trace(); + + SDL_Surface * tmp = IMG_Load(path); + if (!tmp) { + std::cerr << "Error surface " << IMG_GetError << std::endl; + } + SDL_Texture * created_texture + = SDL_CreateTextureFromSurface(this->game_renderer, tmp); + + if (!created_texture) { + std::cerr << "Error could not create texture " << IMG_GetError + << std::endl; + } + SDL_FreeSurface(tmp); + + return created_texture; +} diff --git a/src/crepe/SDLContext.h b/src/crepe/SDLContext.h new file mode 100644 index 0000000..4d9c1bc --- /dev/null +++ b/src/crepe/SDLContext.h @@ -0,0 +1,52 @@ +#pragma once + +#include <SDL2/SDL_render.h> +#include <SDL2/SDL_video.h> + +#include "api/Sprite.h" +#include "api/Transform.h" + +#include "RenderSystem.h" + +namespace crepe::api { +class Texture; +} + +namespace crepe { + +class SDLContext { + +public: + // singleton + static SDLContext & get_instance(); + SDLContext(const SDLContext &) = delete; + SDLContext(SDLContext &&) = delete; + SDLContext & operator=(const SDLContext &) = delete; + SDLContext & operator=(SDLContext &&) = delete; + + //TODO decide events wouter? + +private: + void handle_events(bool & running); + +private: + SDLContext(); + virtual ~SDLContext(); + +private: + friend class api::Texture; + SDL_Texture * texture_from_path(const char *); + //SDL_Texture* setTextureFromPath(const char*, SDL_Rect& clip, const int row, const int col); + +private: + friend class RenderSystem; + void draw(const api::Sprite &, const api::Transform &); + void clear_screen(); + void present_screen(); + +private: + SDL_Window * game_window = nullptr; + SDL_Renderer * game_renderer = nullptr; +}; + +} // namespace crepe diff --git a/src/crepe/Script.cpp b/src/crepe/Script.cpp deleted file mode 100644 index 42e3666..0000000 --- a/src/crepe/Script.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "Script.h" - -using namespace crepe; - -void Script::init() { } -void Script::update() { } - diff --git a/src/crepe/Script.h b/src/crepe/Script.h deleted file mode 100644 index ba4073a..0000000 --- a/src/crepe/Script.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -namespace crepe { - -class Script { -protected: - virtual void init(); - virtual void update(); - // NOTE: additional *events* (like unity's OnDisable and OnEnable) should be - // implemented as member methods in derivative user script classes and - // registered in init(), otherwise this class will balloon in size with each - // added event. -}; - -} - diff --git a/src/crepe/ScriptSystem.cpp b/src/crepe/ScriptSystem.cpp index e301c71..171b490 100644 --- a/src/crepe/ScriptSystem.cpp +++ b/src/crepe/ScriptSystem.cpp @@ -1,15 +1,20 @@ +#include <forward_list> +#include <functional> +#include <vector> + +#include "api/BehaviorScript.h" +#include "api/Script.h" #include "util/log.h" +#include "ComponentManager.h" #include "ScriptSystem.h" +using namespace std; using namespace crepe; +using namespace crepe::api; -ScriptSystem::ScriptSystem() { - dbg_trace(); -} -ScriptSystem::~ScriptSystem() { - dbg_trace(); -} +ScriptSystem::ScriptSystem() { dbg_trace(); } +ScriptSystem::~ScriptSystem() { dbg_trace(); } ScriptSystem & ScriptSystem::get_instance() { static ScriptSystem instance; @@ -17,6 +22,26 @@ ScriptSystem & ScriptSystem::get_instance() { } void ScriptSystem::update() { + using namespace std; dbg_trace(); + + forward_list<Script *> scripts = this->get_scripts(); + for (Script * script : scripts) script->update(); } +forward_list<Script *> ScriptSystem::get_scripts() { + forward_list<Script *> scripts = {}; + ComponentManager & mgr = ComponentManager::get_instance(); + vector<reference_wrapper<BehaviorScript>> behavior_scripts + = mgr.get_components_by_type<BehaviorScript>(); + + for (auto behavior_script_ref : behavior_scripts) { + BehaviorScript & behavior_script = behavior_script_ref.get(); + if (!behavior_script.active) continue; + Script * script = behavior_script.script.get(); + if (script == nullptr) continue; + scripts.push_front(script); + } + + return scripts; +} diff --git a/src/crepe/ScriptSystem.h b/src/crepe/ScriptSystem.h index e1ed290..1f472a0 100644 --- a/src/crepe/ScriptSystem.h +++ b/src/crepe/ScriptSystem.h @@ -1,18 +1,26 @@ #pragma once +#include <forward_list> + #include "System.h" +namespace crepe::api { +class Script; +} + namespace crepe { class ScriptSystem : public System { public: static ScriptSystem & get_instance(); - virtual void update(); + void update(); private: ScriptSystem(); ~ScriptSystem(); -}; -} +private: + std::forward_list<api::Script *> get_scripts(); +}; +} // namespace crepe diff --git a/src/crepe/Sound.h b/src/crepe/Sound.h index b7cfbb8..917b57e 100644 --- a/src/crepe/Sound.h +++ b/src/crepe/Sound.h @@ -1,10 +1,9 @@ #pragma once +#include <memory> #include <soloud/soloud.h> #include <soloud/soloud_wav.h> -#include <memory> - #include "Asset.h" namespace crepe { diff --git a/src/crepe/Sprite.cpp b/src/crepe/Sprite.cpp deleted file mode 100644 index a5a5e68..0000000 --- a/src/crepe/Sprite.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include <string> - -#include "Sprite.h" - -using namespace crepe; -using namespace std; - -Sprite::Sprite(string path) : path(path) {} diff --git a/src/crepe/Sprite.h b/src/crepe/Sprite.h deleted file mode 100644 index 143e702..0000000 --- a/src/crepe/Sprite.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include <string> - -#include "Component.h" - -namespace crepe { - -class Sprite : public Component { -public: - Sprite(std::string path); - - std::string path; -}; - -} // namespace crepe diff --git a/src/crepe/System.h b/src/crepe/System.h index 3fe3d66..ecbb7f5 100644 --- a/src/crepe/System.h +++ b/src/crepe/System.h @@ -8,8 +8,8 @@ public: virtual void update() = 0; protected: - System() { }; - virtual ~System() { }; + System() {}; + virtual ~System() {}; private: // singleton @@ -19,5 +19,4 @@ private: System & operator=(System &&) = delete; }; -} - +} // namespace crepe diff --git a/src/crepe/api/AssetManager.cpp b/src/crepe/api/AssetManager.cpp new file mode 100644 index 0000000..560df6c --- /dev/null +++ b/src/crepe/api/AssetManager.cpp @@ -0,0 +1,17 @@ +#include "util/log.h" + +#include "AssetManager.h" + +using namespace crepe::api; + +AssetManager & AssetManager::get_instance() { + static AssetManager instance; + return instance; +} + +AssetManager::~AssetManager() { + dbg_trace(); + this->asset_cache.clear(); +} + +AssetManager::AssetManager() { dbg_trace(); } diff --git a/src/crepe/api/AssetManager.h b/src/crepe/api/AssetManager.h new file mode 100644 index 0000000..3e72a49 --- /dev/null +++ b/src/crepe/api/AssetManager.h @@ -0,0 +1,35 @@ +#pragma once + +#include <any> +#include <memory> +#include <string> +#include <unordered_map> + +namespace crepe::api { + +class AssetManager { + +private: + std::unordered_map<std::string, std::any> asset_cache; + +private: + AssetManager(); + virtual ~AssetManager(); + +public: + AssetManager(const AssetManager &) = delete; + AssetManager(AssetManager &&) = delete; + AssetManager & operator=(const AssetManager &) = delete; + AssetManager & operator=(AssetManager &&) = delete; + + static AssetManager & get_instance(); + +public: + template <typename asset> + std::shared_ptr<asset> cache(const std::string & file_path, + bool reload = false); +}; + +} // namespace crepe::api + +#include "AssetManager.hpp" diff --git a/src/crepe/api/AssetManager.hpp b/src/crepe/api/AssetManager.hpp new file mode 100644 index 0000000..468724c --- /dev/null +++ b/src/crepe/api/AssetManager.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "AssetManager.h" + +namespace crepe::api { + +template <typename asset> +std::shared_ptr<asset> AssetManager::cache(const std::string & file_path, + bool reload) { + auto it = asset_cache.find(file_path); + + if (!reload && it != asset_cache.end()) { + return std::any_cast<std::shared_ptr<asset>>(it->second); + } + + std::shared_ptr<asset> new_asset + = std::make_shared<asset>(file_path.c_str()); + + asset_cache[file_path] = new_asset; + + return new_asset; +} + +} // namespace crepe::api diff --git a/src/crepe/api/AudioSource.cpp b/src/crepe/api/AudioSource.cpp index b512d27..35b8d83 100644 --- a/src/crepe/api/AudioSource.cpp +++ b/src/crepe/api/AudioSource.cpp @@ -1,7 +1,8 @@ -#include "AudioSource.h" +#include <memory> #include "../Sound.h" -#include <memory> + +#include "AudioSource.h" using namespace crepe::api; diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 2d26cda..7980212 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -2,8 +2,8 @@ #include <memory> -#include "Asset.h" -#include "Component.h" +#include "../Asset.h" +#include "../Component.h" namespace crepe { class Sound; diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp index 2dd933e..e69de29 100644 --- a/src/crepe/api/BehaviorScript.cpp +++ b/src/crepe/api/BehaviorScript.cpp @@ -1,10 +0,0 @@ -#include "../util/log.h" - -#include "BehaviorScript.h" - -using namespace crepe::api; - -BehaviorScript::BehaviorScript() { - dbg_trace(); -} - diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index e9542c1..6133cc8 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -1,17 +1,35 @@ #pragma once -#include "../Script.h" +#include <memory> + #include "../Component.h" +namespace crepe { +class ScriptSystem; +class ComponentManager; +} // namespace crepe + namespace crepe::api { -class BehaviorScript : public Script, public Component { - // only allow ComponentManager to instantiate scripts - friend class ComponentManager; +class Script; + +class BehaviorScript : public Component { +protected: + friend class crepe::ComponentManager; + using Component::Component; + +public: + virtual ~BehaviorScript() = default; + +public: + template <class T> + BehaviorScript & set_script(); protected: - BehaviorScript(); + friend class crepe::ScriptSystem; + std::unique_ptr<Script> script = nullptr; }; -} +} // namespace crepe::api +#include "BehaviorScript.hpp" diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp new file mode 100644 index 0000000..2a3502f --- /dev/null +++ b/src/crepe/api/BehaviorScript.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include <type_traits> + +#include "../util/log.h" + +#include "BehaviorScript.h" +#include "Script.h" + +namespace crepe::api { + +template <class T> +BehaviorScript & BehaviorScript::set_script() { + static_assert(std::is_base_of<Script, T>::value); + dbg_trace(); + Script * s = new T(); + s->parent = this; + this->script = std::unique_ptr<Script>(s); + return *this; +} + +} // namespace crepe::api diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 86623de..0bb1263 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -1,10 +1,31 @@ target_sources(crepe PUBLIC # AudioSource.cpp BehaviorScript.cpp + Script.cpp + GameObject.cpp + Rigidbody.cpp + Force.cpp + ParticleEmitter.cpp + Transform.cpp + Color.cpp + Texture.cpp + AssetManager.cpp + Sprite.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES # AudioSource.h BehaviorScript.h + Config.h + Script.h + Script.hpp + GameObject.h + GameObject.hpp + Rigidbody.h + Sprite.h + Point.h + Color.h + Texture.h + AssetManager.h + AssetManager.hpp ) - diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h new file mode 100644 index 0000000..762574b --- /dev/null +++ b/src/crepe/api/CircleCollider.h @@ -0,0 +1,13 @@ +#pragma once +#include "../Collider.h" + +namespace crepe::api { + +class CircleCollider : public Collider { +public: + CircleCollider(uint32_t game_object_id, int radius) + : Collider(game_object_id), radius(radius) {} + int radius; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp new file mode 100644 index 0000000..fb5bd1a --- /dev/null +++ b/src/crepe/api/Color.cpp @@ -0,0 +1,34 @@ +#include "Color.h" + +using namespace crepe::api; + +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); + +// FIXME: do we really need double precision for color values? +Color::Color(double red, double green, double blue, double 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; }; diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h new file mode 100644 index 0000000..e818de4 --- /dev/null +++ b/src/crepe/api/Color.h @@ -0,0 +1,37 @@ +#pragma once + +namespace crepe::api { + +class Color { + + // FIXME: can't these colors be defined as a `static constexpr const Color` + // instead? + +public: + Color(double red, double green, double blue, double 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: + double r; + double g; + double b; + double 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; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h new file mode 100644 index 0000000..8a7f268 --- /dev/null +++ b/src/crepe/api/Config.h @@ -0,0 +1,40 @@ +#pragma once + +#include "../util/log.h" + +namespace crepe::api { + +class Config { +private: + Config() = default; + +public: + ~Config() = default; + +public: + //! Retrieve handle to global Config instance + static Config & get_instance() { + static Config instance; + return instance; + } + +public: + //! Logging-related settings + struct { + /** + * \brief Log level + * + * Only messages with equal or higher priority than this value will be + * logged. + */ + util::LogLevel level = util::LogLevel::INFO; + /** + * \brief Colored log output + * + * Enables log coloring using ANSI escape codes. + */ + bool color = true; + } log; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Force.cpp b/src/crepe/api/Force.cpp new file mode 100644 index 0000000..e359adc --- /dev/null +++ b/src/crepe/api/Force.cpp @@ -0,0 +1,21 @@ +#include <cmath> + +#include "Force.h" + +namespace crepe::api { + +Force::Force(uint32_t game_object_id, uint32_t magnitude, uint32_t direction) + : Component(game_object_id) { + // TODO: A standard angle unit should be established for the entire engine + // and assumed to be the default everywhere. Only conversion functions should + // explicitly contain the unit (i.e. `deg_to_rad()` & `rad_to_deg()`) + + // Convert direction from degrees to radians + float radian_direction = static_cast<float>(direction) * (M_PI / 180.0f); + force_x = static_cast<int32_t>( + std::round(magnitude * std::cos(radian_direction))); + force_y = static_cast<int32_t>( + std::round(magnitude * std::sin(radian_direction))); +} + +} // namespace crepe::api diff --git a/src/crepe/api/Force.h b/src/crepe/api/Force.h new file mode 100644 index 0000000..8da9a00 --- /dev/null +++ b/src/crepe/api/Force.h @@ -0,0 +1,17 @@ +#pragma once + +#include <cstdint> + +#include "../Component.h" + +namespace crepe::api { + +class Force : public Component { +public: + Force(uint32_t game_object_id, uint32_t magnitude, uint32_t direction); + + int32_t force_x; + int32_t force_y; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp new file mode 100644 index 0000000..b167187 --- /dev/null +++ b/src/crepe/api/GameObject.cpp @@ -0,0 +1,7 @@ +#include "GameObject.h" + +using namespace crepe::api; +using namespace std; + +GameObject::GameObject(uint32_t id, string name, string tag, int layer) + : id(id), name(name), tag(tag), active(true), layer(layer) {} diff --git a/src/crepe/GameObject.h b/src/crepe/api/GameObject.h index 3588d9a..57508c5 100644 --- a/src/crepe/GameObject.h +++ b/src/crepe/api/GameObject.h @@ -3,14 +3,14 @@ #include <cstdint> #include <string> -namespace crepe { +namespace crepe::api { class GameObject { public: GameObject(uint32_t id, std::string name, std::string tag, int layer); template <typename T, typename... Args> - void add_component(Args &&... args); + T & add_component(Args &&... args); uint32_t id; std::string name; @@ -19,6 +19,6 @@ public: int layer; }; -} // namespace crepe +} // namespace crepe::api #include "GameObject.hpp" diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp new file mode 100644 index 0000000..3c7e867 --- /dev/null +++ b/src/crepe/api/GameObject.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "../ComponentManager.h" + +#include "GameObject.h" + +namespace crepe::api { + +template <typename T, typename... Args> +T & GameObject::add_component(Args &&... args) { + auto & mgr = ComponentManager::get_instance(); + return mgr.add_component<T>(this->id, std::forward<Args>(args)...); +} + +} // namespace crepe::api diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp new file mode 100644 index 0000000..0b3a9ee --- /dev/null +++ b/src/crepe/api/ParticleEmitter.cpp @@ -0,0 +1,37 @@ +#include <ctime> +#include <iostream> + +#include "Particle.h" +#include "ParticleEmitter.h" + +using namespace crepe; + +ParticleEmitter::ParticleEmitter(uint32_t game_object_id, + uint32_t max_particles, uint32_t emission_rate, + uint32_t speed, uint32_t speed_offset, + uint32_t angle, uint32_t angleOffset, + float begin_lifespan, float end_lifespan) + : Component(game_object_id), max_particles(max_particles), + emission_rate(emission_rate), speed(speed), speed_offset(speed_offset), + position{0, 0}, begin_lifespan(begin_lifespan), + end_lifespan(end_lifespan) { + std::srand( + static_cast<uint32_t>(std::time(nullptr))); // initialize random seed + std::cout << "Create emitter" << std::endl; + // FIXME: Why do these expressions start with `360 +`, only to be `% 360`'d + // right after? This does not make any sense to me. + min_angle = (360 + angle - (angleOffset % 360)) % 360; + max_angle = (360 + angle + (angleOffset % 360)) % 360; + position.x = 400; // FIXME: what are these magic values? + position.y = 400; + for (size_t i = 0; i < max_particles; i++) { + this->particles.emplace_back(); + } +} + +ParticleEmitter::~ParticleEmitter() { + std::vector<Particle>::iterator it = this->particles.begin(); + while (it != this->particles.end()) { + it = this->particles.erase(it); + } +} diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h new file mode 100644 index 0000000..2e2e95b --- /dev/null +++ b/src/crepe/api/ParticleEmitter.h @@ -0,0 +1,42 @@ +#pragma once + +#include <cstdint> +#include <vector> + +#include "Component.h" +#include "Particle.h" + +namespace crepe { + +class ParticleEmitter : public Component { +public: + ParticleEmitter(uint32_t game_object_id, uint32_t max_particles, + uint32_t emission_rate, uint32_t speed, + uint32_t speed_offset, uint32_t angle, uint32_t angleOffset, + float begin_lifespan, float end_lifespan); + ~ParticleEmitter(); + + //! position of the emitter + Position position; + //! maximum number of particles + uint32_t max_particles; + //! rate of particle emission + uint32_t emission_rate; + //! base speed of the particles + uint32_t speed; + //! offset for random speed variation + uint32_t speed_offset; + //! min angle of particle emission + uint32_t min_angle; + //! max angle of particle emission + uint32_t max_angle; + //! begin Lifespan of particle (only visual) + float begin_lifespan; + //! begin Lifespan of particle + float end_lifespan; + + //! collection of particles + std::vector<Particle> particles; +}; + +} // namespace crepe diff --git a/src/crepe/api/Point.h b/src/crepe/api/Point.h new file mode 100644 index 0000000..b47b7e6 --- /dev/null +++ b/src/crepe/api/Point.h @@ -0,0 +1,11 @@ +#pragma once + +namespace crepe::api { + +class Point { +public: + double x; + double y; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp new file mode 100644 index 0000000..ebf9fb9 --- /dev/null +++ b/src/crepe/api/Rigidbody.cpp @@ -0,0 +1,8 @@ +#include "Rigidbody.h" + +using namespace crepe::api; + +Rigidbody::Rigidbody(uint32_t game_object_id, int mass, int gravity_scale, + BodyType bodyType) + : Component(game_object_id), mass(mass), gravity_scale(gravity_scale), + body_type(bodyType) {} diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h new file mode 100644 index 0000000..6079a76 --- /dev/null +++ b/src/crepe/api/Rigidbody.h @@ -0,0 +1,30 @@ +#pragma once + +#include <cstdint> + +#include "../Component.h" + +namespace crepe::api { + +// FIXME: can't this enum be defined inside the class declaration of Rigidbody? +enum class BodyType { + //! Does not move (e.g. walls, ground ...) + STATIC, + //! Moves and responds to forces (e.g. player, physics objects ...) + DYNAMIC, + //! Moves but does not respond to forces (e.g. moving platforms ...) + KINEMATIC, +}; + +class Rigidbody : public Component { +public: + Rigidbody(uint32_t game_object_id, int mass, int gravity_scale, + BodyType body_type); + int32_t velocity_x; + int32_t velocity_y; + int mass; + int gravity_scale; + BodyType body_type; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp new file mode 100644 index 0000000..5016ed0 --- /dev/null +++ b/src/crepe/api/Script.cpp @@ -0,0 +1,3 @@ +#include "Script.h" + +using namespace crepe::api; diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h new file mode 100644 index 0000000..59e6ec0 --- /dev/null +++ b/src/crepe/api/Script.h @@ -0,0 +1,38 @@ +#pragma once + +#include <vector> + +namespace crepe { +class ScriptSystem; +} + +namespace crepe::api { + +class BehaviorScript; + +class Script { + friend class crepe::ScriptSystem; + +protected: + virtual void init() {} + virtual void update() {} + // NOTE: additional *events* (like unity's OnDisable and OnEnable) should be + // implemented as member methods in derivative user script classes and + // registered in init(), otherwise this class will balloon in size with each + // added event. + +protected: + template <typename T> + T & get_component(); + + template <typename T> + std::vector<std::reference_wrapper<T>> get_components(); + +private: + friend class crepe::api::BehaviorScript; + BehaviorScript * parent = nullptr; +}; + +} // namespace crepe::api + +#include "Script.hpp" diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp new file mode 100644 index 0000000..8004fe3 --- /dev/null +++ b/src/crepe/api/Script.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "../ComponentManager.h" + +#include "BehaviorScript.h" +#include "Script.h" + +namespace crepe::api { + +template <typename T> +T & Script::get_component() { + std::vector<std::reference_wrapper<T>> all_components + = this->get_components<T>(); + if (all_components.size() < 1) throw nullptr; // TODO + + return all_components.back().get(); +} + +template <typename T> +std::vector<std::reference_wrapper<T>> Script::get_components() { + ComponentManager & mgr = ComponentManager::get_instance(); + return mgr.get_components_by_id<T>(this->parent->game_object_id); +} + +} // namespace crepe::api diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp new file mode 100644 index 0000000..806f147 --- /dev/null +++ b/src/crepe/api/Sprite.cpp @@ -0,0 +1,20 @@ +#include <cstdint> +#include <memory> + +#include "api/Texture.h" +#include "util/log.h" + +#include "Component.h" +#include "Sprite.h" + +using namespace std; +using namespace crepe; +using namespace crepe::api; + +Sprite::Sprite(uint32_t id, shared_ptr<Texture> image, const Color & color, + const FlipSettings & flip) + : Component(id), color(color), flip(flip), sprite_image(image) { + dbg_trace(); +} + +Sprite::~Sprite() { dbg_trace(); } diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h new file mode 100644 index 0000000..b06125e --- /dev/null +++ b/src/crepe/api/Sprite.h @@ -0,0 +1,32 @@ +#pragma once + +#include <SDL2/SDL_rect.h> +#include <cstdint> +#include <memory> + +#include "api/Color.h" +#include "api/Texture.h" + +#include "Component.h" + +namespace crepe::api { + +struct FlipSettings { + bool flip_x = 1; + bool flip_y = 1; +}; + +class Sprite : public Component { + +public: + Sprite(uint32_t game_id, std::shared_ptr<Texture> image, + const Color & color, const FlipSettings & flip); + ~Sprite(); + std::shared_ptr<Texture> sprite_image; + Color color; + FlipSettings flip; + uint8_t sorting_in_layer; + uint8_t order_in_layer; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp new file mode 100644 index 0000000..481ef7c --- /dev/null +++ b/src/crepe/api/Texture.cpp @@ -0,0 +1,32 @@ +#include <SDL2/SDL_render.h> + +#include "util/log.h" + +#include "Asset.h" +#include "SDLContext.h" +#include "Texture.h" + +using namespace crepe::api; +using namespace std; + +Texture::Texture(unique_ptr<Asset> res) { + dbg_trace(); + this->load(std::move(res)); +} + +Texture::Texture(const char * src) { + dbg_trace(); + this->load(make_unique<Asset>(src)); +} + +Texture::~Texture() { + dbg_trace(); + if (this->texture != nullptr) { + SDL_DestroyTexture(this->texture); + } +} + +void Texture::load(unique_ptr<Asset> res) { + SDLContext & ctx = SDLContext::get_instance(); + this->texture = ctx.texture_from_path(res->canonical()); +} diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h new file mode 100644 index 0000000..f8481e3 --- /dev/null +++ b/src/crepe/api/Texture.h @@ -0,0 +1,33 @@ +#pragma once + +// FIXME: this header can't be included because this is an API header, and SDL2 +// development headers won't be bundled with crepe. Why is this facade in the +// API namespace? +#include <SDL2/SDL_render.h> +#include <memory> + +#include "Asset.h" + +namespace crepe { +class SDLContext; +} + +namespace crepe::api { + +class Texture { + +public: + Texture(const char * src); + Texture(std::unique_ptr<Asset> res); + ~Texture(); + +private: + void load(std::unique_ptr<Asset> res); + +private: + SDL_Texture * texture = nullptr; + + friend class crepe::SDLContext; +}; + +} // namespace crepe::api diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp new file mode 100644 index 0000000..3b218bc --- /dev/null +++ b/src/crepe/api/Transform.cpp @@ -0,0 +1,15 @@ +#include <cstdint> + +#include "api/Point.h" +#include "util/log.h" + +#include "Component.h" +#include "Transform.h" + +using namespace crepe::api; + +Transform::Transform(uint32_t game_id, const Point & point, double rot, double scale) + : Component(game_id), position(point), rotation(rot), scale(scale) { + dbg_trace(); +} +Transform::~Transform() { dbg_trace(); } diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h new file mode 100644 index 0000000..c451c16 --- /dev/null +++ b/src/crepe/api/Transform.h @@ -0,0 +1,27 @@ +#pragma once + +#include <cstdint> + +#include "api/Point.h" + +#include "Component.h" + +namespace crepe::api { + +class Transform : public Component { + // FIXME: What's the difference between the `Point` and `Position` + // classes/structs? How about we replace both with a universal `Vec2` that + // works similar (or the same) as those found in GLSL? + +public: + Transform(uint32_t id, const Point &, double, double); + ~Transform(); + //! Translation (shift) + Point position; + //! Rotation, in radians + double rotation; + //! Multiplication factor + double scale; +}; + +} // namespace crepe::api diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index 100f028..bbeaad9 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -1,9 +1,12 @@ target_sources(crepe PUBLIC + color.cpp log.cpp + fmt.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES color.h log.h + fmt.h ) diff --git a/src/crepe/util/color.cpp b/src/crepe/util/color.cpp new file mode 100644 index 0000000..a7bbc81 --- /dev/null +++ b/src/crepe/util/color.cpp @@ -0,0 +1,91 @@ +#include <cstdarg> + +#include "../api/Config.h" +#include "color.h" +#include "fmt.h" + +using namespace crepe::util; +using namespace std; + +static constexpr const char * RESET_CODE = "\e[0m"; + +const string LogColor::str(const string & content) { + auto & cfg = api::Config::get_instance(); + string out = content; + if (cfg.log.color) out = this->code + out; + if (content.size() == 0) return out; + if (cfg.log.color) out = out + RESET_CODE; + return out; +} + +const char * LogColor::c_str(const char * content) { + this->final = this->str(content == NULL ? "" : content); + return this->final.c_str(); +} + +const char * LogColor::fmt(const char * fmt, ...) { + va_list args; + va_start(args, fmt); + string content = va_stringf(args, fmt); + va_end(args); + return this->c_str(content.c_str()); +} + +LogColor & LogColor::add_code(unsigned int code) { + this->code += stringf("\e[%dm", code); + return *this; +} + +LogColor & LogColor::reset() { + this->code = RESET_CODE; + return *this; +} + +LogColor & LogColor::fg_black(bool bright) { + return this->add_code(bright ? 90 : 30); +} +LogColor & LogColor::fg_red(bool bright) { + return this->add_code(bright ? 91 : 31); +} +LogColor & LogColor::fg_green(bool bright) { + return this->add_code(bright ? 92 : 32); +} +LogColor & LogColor::fg_yellow(bool bright) { + return this->add_code(bright ? 93 : 33); +} +LogColor & LogColor::fg_blue(bool bright) { + return this->add_code(bright ? 94 : 34); +} +LogColor & LogColor::fg_magenta(bool bright) { + return this->add_code(bright ? 95 : 35); +} +LogColor & LogColor::fg_cyan(bool bright) { + return this->add_code(bright ? 96 : 36); +} +LogColor & LogColor::fg_white(bool bright) { + return this->add_code(bright ? 97 : 37); +} +LogColor & LogColor::bg_black(bool bright) { + return this->add_code(bright ? 100 : 40); +} +LogColor & LogColor::bg_red(bool bright) { + return this->add_code(bright ? 101 : 41); +} +LogColor & LogColor::bg_green(bool bright) { + return this->add_code(bright ? 102 : 42); +} +LogColor & LogColor::bg_yellow(bool bright) { + return this->add_code(bright ? 103 : 43); +} +LogColor & LogColor::bg_blue(bool bright) { + return this->add_code(bright ? 104 : 44); +} +LogColor & LogColor::bg_magenta(bool bright) { + return this->add_code(bright ? 105 : 45); +} +LogColor & LogColor::bg_cyan(bool bright) { + return this->add_code(bright ? 106 : 46); +} +LogColor & LogColor::bg_white(bool bright) { + return this->add_code(bright ? 107 : 47); +} diff --git a/src/crepe/util/color.h b/src/crepe/util/color.h index 066c9d3..91e1abe 100644 --- a/src/crepe/util/color.h +++ b/src/crepe/util/color.h @@ -1,41 +1,51 @@ #pragma once -namespace crepe::util::color { - -constexpr const char * RESET = "\e[0m"; - -constexpr const char * FG_BLACK = "\e[30m"; -constexpr const char * FG_RED = "\e[31m"; -constexpr const char * FG_GREEN = "\e[32m"; -constexpr const char * FG_YELLOW = "\e[33m"; -constexpr const char * FG_BLUE = "\e[34m"; -constexpr const char * FG_MAGENTA = "\e[35m"; -constexpr const char * FG_CYAN = "\e[36m"; -constexpr const char * FG_WHITE = "\e[37m"; -constexpr const char * BG_BLACK = "\e[40m"; -constexpr const char * BG_RED = "\e[41m"; -constexpr const char * BG_GREEN = "\e[42m"; -constexpr const char * BG_YELLOW = "\e[43m"; -constexpr const char * BG_BLUE = "\e[44m"; -constexpr const char * BG_MAGENTA = "\e[45m"; -constexpr const char * BG_CYAN = "\e[46m"; -constexpr const char * BG_WHITE = "\e[47m"; - -constexpr const char * FG_BLACK_BRIGHT = "\e[90m"; -constexpr const char * FG_RED_BRIGHT = "\e[91m"; -constexpr const char * FG_GREEN_BRIGHT = "\e[92m"; -constexpr const char * FG_YELLOW_BRIGHT = "\e[93m"; -constexpr const char * FG_BLUE_BRIGHT = "\e[94m"; -constexpr const char * FG_MAGENTA_BRIGHT = "\e[95m"; -constexpr const char * FG_CYAN_BRIGHT = "\e[96m"; -constexpr const char * FG_WHITE_BRIGHT = "\e[97m"; -constexpr const char * BG_BLACK_BRIGHT = "\e[100m"; -constexpr const char * BG_RED_BRIGHT = "\e[101m"; -constexpr const char * BG_GREEN_BRIGHT = "\e[102m"; -constexpr const char * BG_YELLOW_BRIGHT = "\e[103m"; -constexpr const char * BG_BLUE_BRIGHT = "\e[104m"; -constexpr const char * BG_MAGENTA_BRIGHT = "\e[105m"; -constexpr const char * BG_CYAN_BRIGHT = "\e[106m"; -constexpr const char * BG_WHITE_BRIGHT = "\e[107m"; - -} // namespace crepe::util::color +#include <string> + +namespace crepe::util { + +class LogColor { +public: + LogColor() = default; + +public: + //! get color code as c-style string (or color content string) + const char * c_str(const char * content = NULL); + //! color printf-style format string + const char * fmt(const char * fmt, ...); + //! get color code as stl string (or color content string) + const std::string str(const std::string & content = ""); + +public: + //! reset color to default foreground and background color + LogColor & reset(); + +public: + LogColor & fg_black(bool bright = false); + LogColor & fg_red(bool bright = false); + LogColor & fg_green(bool bright = false); + LogColor & fg_yellow(bool bright = false); + LogColor & fg_blue(bool bright = false); + LogColor & fg_magenta(bool bright = false); + LogColor & fg_cyan(bool bright = false); + LogColor & fg_white(bool bright = false); + +public: + LogColor & bg_black(bool bright = false); + LogColor & bg_red(bool bright = false); + LogColor & bg_green(bool bright = false); + LogColor & bg_yellow(bool bright = false); + LogColor & bg_blue(bool bright = false); + LogColor & bg_magenta(bool bright = false); + LogColor & bg_cyan(bool bright = false); + LogColor & bg_white(bool bright = false); + +private: + LogColor & add_code(unsigned int code); + +private: + std::string code = ""; + std::string final = ""; +}; + +} // namespace crepe::util diff --git a/src/crepe/util/fmt.cpp b/src/crepe/util/fmt.cpp new file mode 100644 index 0000000..8ef1164 --- /dev/null +++ b/src/crepe/util/fmt.cpp @@ -0,0 +1,33 @@ +#include <cstdarg> +#include <cstdio> +#include <string> + +#include "fmt.h" + +using namespace std; + +string crepe::util::va_stringf(va_list args, const char * fmt) { + va_list args_copy; + va_copy(args_copy, args); + + size_t sz = vsnprintf(NULL, 0, fmt, args_copy) + 1; + char * msg = (char *) malloc(sz); + va_end(args_copy); + + vsnprintf(msg, sz, fmt, args); + + string out = msg; + free(msg); + + va_end(args); + + return out; +} + +string crepe::util::stringf(const char * fmt, ...) { + va_list args; + va_start(args, fmt); + string out = va_stringf(args, fmt); + va_end(args); + return out; +} diff --git a/src/crepe/util/fmt.h b/src/crepe/util/fmt.h new file mode 100644 index 0000000..44c426f --- /dev/null +++ b/src/crepe/util/fmt.h @@ -0,0 +1,10 @@ +#pragma once + +#include <string> + +namespace crepe::util { + +std::string va_stringf(va_list args, const char * fmt); +std::string stringf(const char * fmt, ...); + +} // namespace crepe::util diff --git a/src/crepe/util/log.cpp b/src/crepe/util/log.cpp index f91d52c..6bcc4ae 100644 --- a/src/crepe/util/log.cpp +++ b/src/crepe/util/log.cpp @@ -3,48 +3,51 @@ #include <cstdlib> #include <string> +#include "../api/Config.h" +#include "fmt.h" #include "log.h" using namespace crepe::util; +using namespace std; + +string log_prefix(LogLevel level) { + switch (level) { + case LogLevel::TRACE: + return LogColor().fg_white().str("[TRACE]") + " "; + case LogLevel::DEBUG: + return LogColor().fg_magenta().str("[DEBUG]") + " "; + case LogLevel::INFO: + return LogColor().fg_blue().str("[INFO]") + " "; + case LogLevel::WARNING: + return LogColor().fg_yellow().str("[WARN]") + " "; + case LogLevel::ERROR: + return LogColor().fg_red().str("[ERROR]") + " "; + } + return ""; +} -static const char * const LOG_PREFIX[] = { - [log_level::DEBUG] = "[DBG] ", - [log_level::INFO] = "[INFO] ", - [log_level::WARNING] = "[WARN] ", - [log_level::ERROR] = "[ERR] ", -}; - -static void va_logf(enum log_level level, va_list args, const std::string fmt) { - va_list args_copy; - va_copy(args_copy, args); - - // prepend log level and ensure newline - std::string format_fixed = LOG_PREFIX[level] + fmt; - if (!format_fixed.ends_with("\n")) format_fixed += "\n"; - - size_t sz = vsnprintf(NULL, 0, format_fixed.c_str(), args_copy) + 1; - char * msg = (char *) malloc(sz); - va_end(args_copy); +static void log(LogLevel level, const string msg) { + auto & cfg = crepe::api::Config::get_instance(); + if (level < cfg.log.level) return; - vsnprintf(msg, sz, format_fixed.c_str(), args); + string out = log_prefix(level) + msg; + if (!out.ends_with("\n")) out += "\n"; // TODO: also log to file or smth - printf("%s", msg); + printf("%s", out.c_str()); fflush(stdout); - - free(msg); } void crepe::util::logf(const char * fmt, ...) { va_list args; va_start(args, fmt); - va_logf(crepe::util::log_level::DEBUG, args, fmt); + log(LogLevel::DEBUG, va_stringf(args, fmt)); va_end(args); } -void crepe::util::logf(log_level level, const char * fmt, ...) { +void crepe::util::logf(LogLevel level, const char * fmt, ...) { va_list args; va_start(args, fmt); - va_logf(level, args, fmt); + log(level, va_stringf(args, fmt)); va_end(args); } diff --git a/src/crepe/util/log.h b/src/crepe/util/log.h index 2b0fbe1..308ba96 100644 --- a/src/crepe/util/log.h +++ b/src/crepe/util/log.h @@ -6,24 +6,28 @@ #include "color.h" // utility macros -#define _crepe_logf_here(fmt, ...) \ - crepe::util::logf(util::log_level::DEBUG, "%s%s (%s:%d)" fmt "\n", \ - crepe::util::color::FG_WHITE, __PRETTY_FUNCTION__, \ - __FILE_NAME__, __LINE__, crepe::util::color::RESET, \ - __VA_ARGS__) +#define _crepe_logf_here(level, format, ...) \ + crepe::util::logf( \ + level, "%s" format, \ + crepe::util::LogColor().fg_white(false).fmt( \ + "%s (%s:%d)", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__), \ + __VA_ARGS__) // very illegal global function-style macros // NOLINTBEGIN -#define dbg_logf(fmt, ...) _crepe_logf_here(": " fmt, __VA_ARGS__) -#define dbg_log(str) _crepe_logf_here(": %s", str) -#define dbg_trace() _crepe_logf_here("%s", "") +#define dbg_logf(fmt, ...) \ + _crepe_logf_here(crepe::util::LogLevel::DEBUG, ": " fmt, __VA_ARGS__) +#define dbg_log(str) \ + _crepe_logf_here(crepe::util::LogLevel::DEBUG, "%s: " str, "") +#define dbg_trace() _crepe_logf_here(crepe::util::LogLevel::TRACE, "%s", "") // NOLINTEND #endif namespace crepe::util { -enum log_level { +enum LogLevel { + TRACE, DEBUG, INFO, WARNING, @@ -31,6 +35,6 @@ enum log_level { }; void logf(const char * fmt, ...); -void logf(enum log_level level, const char * fmt, ...); +void logf(enum LogLevel level, const char * fmt, ...); } // namespace crepe::util diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 6df4ce7..fea6f60 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -1,3 +1,6 @@ +# all examples +add_custom_target(examples) + # add_example(target_name [SOURCES...]) function(add_example target_name) # if SOURCES is not specified @@ -10,9 +13,14 @@ function(add_example target_name) add_executable(${target_name} EXCLUDE_FROM_ALL ${sources}) target_link_libraries(${target_name} PUBLIC crepe) + add_dependencies(examples ${target_name}) endfunction() add_example(audio_internal) -add_example(components_internal) +# add_example(components_internal) add_example(script) - +add_example(log) +add_example(rendering) +add_example(asset_manager) +add_example(particle) +add_example(physics) diff --git a/src/example/asset_manager.cpp b/src/example/asset_manager.cpp new file mode 100644 index 0000000..7e15d1f --- /dev/null +++ b/src/example/asset_manager.cpp @@ -0,0 +1,39 @@ + + +#include <crepe/Sound.h> +#include <crepe/api/AssetManager.h> +#include <crepe/api/Texture.h> + +using namespace crepe; +using namespace crepe::api; +int main() { + + // this needs to be called before the asset manager otherwise the destructor + // of sdl is not in the right order + { Texture test("../asset/texture/img.png"); } + // FIXME: make it so the issue described by the above comment is not possible + // (i.e. the order in which internal classes are instantiated should not + // impact the way the engine works). + + auto & mgr = AssetManager::get_instance(); + + { + // TODO: [design] the Sound class can't be directly included by the user as + // it includes SoLoud headers. + auto bgm = mgr.cache<Sound>("../mwe/audio/bgm.ogg"); + auto sfx1 = mgr.cache<Sound>("../mwe/audio/sfx1.wav"); + auto sfx2 = mgr.cache<Sound>("../mwe/audio/sfx2.wav"); + + auto img = mgr.cache<Texture>("../asset/texture/img.png"); + auto img1 = mgr.cache<Texture>("../asset/texture/second.png"); + } + + { + auto bgm = mgr.cache<Sound>("../mwe/audio/bgm.ogg"); + auto sfx1 = mgr.cache<Sound>("../mwe/audio/sfx1.wav"); + auto sfx2 = mgr.cache<Sound>("../mwe/audio/sfx2.wav"); + + auto img = mgr.cache<Texture>("../asset/texture/img.png"); + auto img1 = mgr.cache<Texture>("../asset/texture/second.png"); + } +} diff --git a/src/example/audio_internal.cpp b/src/example/audio_internal.cpp index 1199e2d..f35ad9d 100644 --- a/src/example/audio_internal.cpp +++ b/src/example/audio_internal.cpp @@ -5,26 +5,40 @@ #include <crepe/Sound.h> #include <crepe/util/log.h> +#include <crepe/api/Config.h> -#include <chrono> #include <thread> using namespace crepe; +using namespace crepe::api; using namespace std; using namespace std::chrono_literals; using std::make_unique; -int main() { - dbg_trace(); +// Unrelated stuff that is not part of this POC +int _ = [] () { + // Show dbg_trace() output + auto & cfg = api::Config::get_instance(); + cfg.log.level = util::LogLevel::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 + // Play each sample sequentially while pausing and resuming the background + // track this_thread::sleep_for(500ms); sfx1.play(); this_thread::sleep_for(500ms); @@ -35,11 +49,12 @@ int main() { bgm.play(); this_thread::sleep_for(500ms); - // play all samples simultaniously + // Play all samples simultaniously sfx1.play(); sfx2.play(); sfx3.play(); this_thread::sleep_for(1000ms); - return 0; + // Stop all audio and exit + return EXIT_SUCCESS; } diff --git a/src/example/components_internal.cpp b/src/example/components_internal.cpp index 54ce295..3c68974 100644 --- a/src/example/components_internal.cpp +++ b/src/example/components_internal.cpp @@ -6,14 +6,16 @@ #include <cassert> #include <chrono> -#include <crepe/Collider.h> #include <crepe/Component.h> #include <crepe/ComponentManager.h> -#include <crepe/GameObject.h> -#include <crepe/Rigidbody.h> -#include <crepe/Sprite.h> + +#include <crepe/api/GameObject.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Sprite.h> + #include <crepe/util/log.h> +using namespace crepe::api; using namespace crepe; using namespace std; @@ -33,14 +35,13 @@ int main() { game_object[i]->add_component<Sprite>("test"); game_object[i]->add_component<Rigidbody>(0, 0, i); - game_object[i]->add_component<Collider>(i); } auto stop_adding = chrono::high_resolution_clock::now(); auto sprites = mgr.get_components_by_type<Sprite>(); for (auto sprite : sprites) { - assert(sprite.get().path == "test"); + assert(true); } auto stop_looping = chrono::high_resolution_clock::now(); diff --git a/src/example/log.cpp b/src/example/log.cpp new file mode 100644 index 0000000..04ab9cd --- /dev/null +++ b/src/example/log.cpp @@ -0,0 +1,29 @@ +/** \file + * + * Standalone example for usage of the logging functions + */ + +#include <crepe/api/Config.h> +#include <crepe/util/log.h> + +using namespace crepe; +using namespace crepe::util; + +// unrelated setup code +int _ = [] () { + // make sure all log messages get printed + auto & cfg = api::Config::get_instance(); + cfg.log.level = util::LogLevel::TRACE; + + return 0; // satisfy compiler +} (); + +int main() { + dbg_trace(); + dbg_logf("test printf parameters: %d", 3); + logf(LogLevel::INFO, "info message"); + logf(LogLevel::WARNING, "warning"); + logf(LogLevel::ERROR, "error"); + + return 0; +} diff --git a/src/example/particle.cpp b/src/example/particle.cpp new file mode 100644 index 0000000..53fa213 --- /dev/null +++ b/src/example/particle.cpp @@ -0,0 +1,102 @@ +#include "Particle.h" +#include "ParticleSystem.h" +#include "SDLApp.h" +#include "api/ParticleEmitter.h" +#include <chrono> +#include <crepe/Component.h> +#include <crepe/ComponentManager.h> +#include <crepe/api/GameObject.h> +#include <iostream> +#include <thread> + +using namespace crepe::api; +using namespace crepe; +using namespace std; + +const int WINDOW_WIDTH = 800; +const int WINDOW_HEIGHT = 600; + +int main(int argc, char * argv[]) { + SDLApp app(WINDOW_WIDTH, WINDOW_HEIGHT); + + if (!app.initialize()) { + cerr << "Failed to initialize SDLApp." << endl; + return 1; + } + + GameObject * game_object[1]; + game_object[0] = new GameObject(0, "Name", "Tag", 0); + + // FIXME: all systems are singletons, so this shouldn't even compile. + ParticleSystem particle_system; + + unsigned int max_particles = 100; // maximum number of particles + unsigned int emission_rate = 10; // particles created per second + unsigned int speed = 50; // base speed of particles + unsigned int speed_offset = 10; // random offset for particle speed + unsigned int angle = 270; // base angle of particle emission + unsigned int angle_offset = 30; // random offset for particle angle + float begin_lifespan = 0.0f; // beginning lifespan of particles + float end_lifespan = 6.0f; // ending lifespan of particles + + // Vector to hold all the emitters + // vector<ParticleEmitter> emitters; + game_object[0]->add_component<ParticleEmitter>( + max_particles, emission_rate, speed, speed_offset, angle, angle_offset, + begin_lifespan, end_lifespan); + + // Loop to create 1000 emitters + // for (unsigned int i = 0; i < 1000; ++i) { + // ParticleEmitter emitter(maxParticles, emissionRate, speed, speedOffset, angle, angleOffset, beginLifespan, endLifespan); + + // // Set a position for each emitter, modifying the position for demonstration + // emitter.m_position = {static_cast<float>(200 + (i % 100)), static_cast<float>(200 + (i / 100) * 10)}; // Adjust position for each emitter + + // emitters.push_back(emitter); // Add the emitter to the vector + // } + float delta_time = 0.1f; + bool running = true; + cout << "start loop " << endl; + while (running) { + app.handle_events(running); + + // Start timing + auto start = chrono::high_resolution_clock::now(); + + // POC CODE + particle_system.update(); + // POC CODE + + // End timing + auto end = chrono::high_resolution_clock::now(); + chrono::duration<float, milli> duration + = end - start; // get duration in milliseconds + + cout << "Update took " << duration.count() << " ms" << endl; + app.clear_screen(); + + start = chrono::high_resolution_clock::now(); + // render particles using the draw_square method from SDLApp + ComponentManager & mgr = ComponentManager::get_instance(); + std::vector<std::reference_wrapper<ParticleEmitter>> emitters + = mgr.get_components_by_type<ParticleEmitter>(); + for (const ParticleEmitter & emitter : emitters) { + for (const Particle & particle : emitter.particles) { + if (particle.active) + app.draw_square(particle.position.x, particle.position.y, + 5); // draw each particle + } + } + + app.present_screen(); + end = chrono::high_resolution_clock::now(); + duration = end - start; // get duration in milliseconds + + cout << "screen took " << duration.count() << " ms" << endl; + + this_thread::sleep_for(chrono::milliseconds(20)); // simulate ~50 FPS + } + + app.clean_up(); + return 0; +} diff --git a/src/example/physics.cpp b/src/example/physics.cpp new file mode 100644 index 0000000..db69b9b --- /dev/null +++ b/src/example/physics.cpp @@ -0,0 +1,30 @@ +#include "PhysicsSystem.h" +#include <chrono> +#include <crepe/Component.h> +#include <crepe/ComponentManager.h> +#include <crepe/api/Force.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Transform.h> +#include <iostream> +#include <thread> + +using namespace crepe::api; +using namespace crepe; +using namespace std; + +int main(int argc, char * argv[]) { + PhysicsSystem physics_system; + GameObject * game_object[2]; + game_object[1] = new GameObject(2, "Name", "Tag", 0); // not found not used + game_object[0] = new GameObject(5, "Name", "Tag", 0); + Point point = { + .x = 0, + .y = 0, + }; + game_object[0]->add_component<Transform>(point, 0, 0); + game_object[0]->add_component<Rigidbody>(1, 1, BodyType::DYNAMIC); + game_object[0]->add_component<Force>(1, 0); + physics_system.update(); + return 0; +} diff --git a/src/example/rendering.cpp b/src/example/rendering.cpp new file mode 100644 index 0000000..1bf448c --- /dev/null +++ b/src/example/rendering.cpp @@ -0,0 +1,71 @@ + + +#include <crepe/ComponentManager.h> +#include <crepe/RenderSystem.h> +#include <crepe/api/GameObject.h> +#include <crepe/util/log.h> + +#include <crepe/api/AssetManager.h> +#include <crepe/api/Color.h> +#include <crepe/api/Point.h> +#include <crepe/api/Sprite.h> +#include <crepe/api/Texture.h> +#include <crepe/api/Transform.h> + +#include <chrono> +#include <memory> + +using namespace std; +using namespace crepe; +using namespace crepe::api; + +int main() { + + dbg_trace(); + + auto obj = GameObject(0, "name", "tag", 0); + auto obj1 = GameObject(1, "name", "tag", 0); + auto obj2 = GameObject(2, "name", "tag", 0); + + auto & mgr = AssetManager::get_instance(); + // Normal adding components + { + Color color(0, 0, 0, 0); + Point point = { + .x = 0, + .y = 0, + }; + obj.add_component<Transform>(point, 1, 1); + obj.add_component<Sprite>( + make_shared<Texture>("../asset/texture/img.png"), color, + FlipSettings{true, true}); + } + + { + Color color(0, 0, 0, 0); + Point point = { + .x = 500, + .y = 0, + }; + obj1.add_component<Transform>(point, 0, 0.1); + auto img = mgr.cache<Texture>("../asset/texture/second.png"); + obj1.add_component<Sprite>(img, color, FlipSettings{true, true}); + } + + { + Color color(0, 0, 0, 0); + Point point = { + .x = 800, + .y = 0, + }; + //obj.add_component<Transform>(point, 0, 0.1); + auto img = mgr.cache<Texture>("../asset/texture/second.png"); + obj2.add_component<Sprite>(img, color, FlipSettings{true, true}); + } + + auto & sys = crepe::RenderSystem::get_instance(); + 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/script.cpp b/src/example/script.cpp index 28605c7..cda9591 100644 --- a/src/example/script.cpp +++ b/src/example/script.cpp @@ -3,31 +3,52 @@ * Standalone example for usage of the script component and system */ -#include <crepe/util/log.h> -#include <crepe/ScriptSystem.h> #include <crepe/ComponentManager.h> -#include <crepe/GameObject.h> +#include <crepe/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 crepe::api; using namespace std; -class MyScript : public api::BehaviorScript { +// Unrelated stuff that is not part of this POC +int _ = [] () { + // Show dbg_trace() output + auto & cfg = api::Config::get_instance(); + cfg.log.level = util::LogLevel::TRACE; + + return 0; // satisfy compiler +}(); + + + +// User-defined script: +class MyScript : public Script { void update() { - dbg_trace(); + // 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() { - dbg_trace(); - + // Create game object with Transform and BehaviorScript components auto obj = GameObject(0, "name", "tag", 0); - obj.add_component<MyScript>(); + obj.add_component<Transform>(Point { .x = 1.2, .y = 3.4, }, 0, 0); + obj.add_component<BehaviorScript>().set_script<MyScript>(); + // Get ScriptSystem singleton instance (this would normally be done from the + // game loop) auto & sys = ScriptSystem::get_instance(); - sys.update(); // -> MyScript::update + // Update all scripts. This should result in MyScript::update being called + sys.update(); - return 0; + return EXIT_SUCCESS; } diff --git a/src/makefile b/src/makefile index c1ef601..3f74a2a 100644 --- a/src/makefile +++ b/src/makefile @@ -2,5 +2,7 @@ FMT += $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp') format: FORCE - clang-tidy -p build/compile_commands.json --fix-errors $(FMT) + # clang-tidy -p build/compile_commands.json --fix-errors $(FMT) + +# TODO: re-enable linter after 2024-11-10 diff --git a/src/readme.md b/src/readme.md index a4a71e7..15fa6f3 100644 --- a/src/readme.md +++ b/src/readme.md @@ -8,26 +8,27 @@ Examples (using Ninja): ``` $ cmake -B build -G Ninja -$ ninja -C build +$ cmake --build build ``` Unit tests can be built by explicitly specifying the target `test_main` when running the build command: ``` -$ ninja -C build test_main +$ cmake --build build --target test_main ``` -Each source file in the example/ folder corresponds to a CMake target as well: +Each source file in the example/ folder corresponds to a CMake target as well +(all examples can be built at once by specifying the `examples` target): ``` -$ ninja -C build audio_internal components_internal +$ cmake --build build --target audio_internal script ``` For installing crêpe system-wide after building (install must be run with elevated privileges): ``` -# ninja -C build install +# cmake --install build ``` |