diff options
44 files changed, 783 insertions, 173 deletions
diff --git a/.clang-format b/.clang-format index 5d233eb..3ae6c28 100644 --- a/.clang-format +++ b/.clang-format @@ -22,6 +22,7 @@ AlignTrailingComments: ReflowComments: false AlignEscapedNewlines: DontAlign BreakBeforeBinaryOperators: All +AlwaysBreakTemplateDeclarations: Yes ... # vim: ft=yaml diff --git a/.clang-tidy b/.clang-tidy index b242e3f..214a5ab 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,18 +1,35 @@ Checks: '-*,readability-identifier-naming' CheckOptions: - - { key: readability-identifier-naming.EnumCase, value: lower_case } - - { key: readability-identifier-naming.GlobalFunctionCase, value: lower_case } - - { key: readability-identifier-naming.ClassCase, value: CamelCase } - - { key: readability-identifier-naming.ClassMethodCase, value: lower_case } - - { key: readability-identifier-naming.ClassMethodPrefix, value: '' } - - { key: readability-identifier-naming.ClassMemberCase, value: lower_case } - - { key: readability-identifier-naming.ClassMemberPrefix, value: '' } - - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } - - { key: readability-identifier-naming.GlobalConstantIgnoredRegexp, value: _.* } - - { key: readability-identifier-naming.GlobalVariableCase, value: lower_case } - - { key: readability-identifier-naming.GlobalVariableIgnoredRegexp, value: _.* } - - { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE } - - { key: readability-identifier-naming.MacroDefinitionIgnoredRegexp, value: _.* } - - { key: readability-identifier-naming.StructCase, value: lower_case } + - key: 'readability-identifier-naming.EnumCase' + value: 'lower_case' + - key: 'readability-identifier-naming.GlobalFunctionCase' + value: 'lower_case' + - key: 'readability-identifier-naming.ClassCase' + value: 'CamelCase' + - key: 'readability-identifier-naming.MethodCase' + value: 'lower_case' + - key: 'readability-identifier-naming.MethodPrefix' + value: '' + - key: 'readability-identifier-naming.MemberCase' + value: 'lower_case' + - key: 'readability-identifier-naming.MemberPrefix' + value: '' + - key: 'readability-identifier-naming.ConstantCase' + value: 'UPPER_CASE' + - key: 'readability-identifier-naming.ConstantIgnoredRegexp' + value: '_.*' + - key: 'readability-identifier-naming.ConstantParameterCase' + value: 'lower_case' + - key: 'readability-identifier-naming.VariableCase' + value: 'lower_case' + - key: 'readability-identifier-naming.VariableIgnoredRegexp' + value: '_.*' + - key: 'readability-identifier-naming.MacroDefinitionCase' + value: 'UPPER_CASE' + - key: 'readability-identifier-naming.MacroDefinitionIgnoredRegexp' + value: '_.*' + - key: 'readability-identifier-naming.StructCase' + value: 'lower_case' # vim: ft=yaml + diff --git a/.editorconfig b/.editorconfig index 1bd7da9..df2cf5e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,7 +5,7 @@ indent_style = tab end_of_line = lf insert_final_newline = true -[*.{md,yml}] +[{*.{md,yml},.clang-*}] indent_style = space indent_size = 2 diff --git a/.gitmodules b/.gitmodules index bb860c0..7dd39b2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/google/googletest shallow = true [submodule "lib/soloud/src"] - path = lib/soloud/src + path = lib/soloud url = https://github.com/jarikomppa/soloud shallow = true [submodule "lib/sdl2"] diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0fd5d23..19afa6f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,28 +1,38 @@ { + "version": "2.0.0", "tasks": [ { - "type": "cppbuild", - "label": "C/C++: g++ build active file", - "command": "/usr/bin/g++", + "label": "configure", + "type": "shell", + "command": "cmake", "args": [ - "-fdiagnostics-color=always", - "-g", - "${file}", - "-o", - "${fileDirname}/${fileBasenameNoExtension}" + "-G", + "Ninja", + "-B", + "${workspaceFolder}/mwe/ecs-homemade/build", // Create build folder here + "${workspaceFolder}/mwe/ecs-homemade" // Path to your source directory ], - "options": { - "cwd": "${fileDirname}" + "group": { + "kind": "build", + "isDefault": false }, - "problemMatcher": [ - "$gcc" + "problemMatcher": [] + }, + { + "label": "build", + "type": "shell", + "command": "cmake", + "args": [ + "--build", + "${workspaceFolder}/mwe/ecs-homemade/build" // Build directory ], + "dependsOn": "configure", // Ensure the configure task runs first "group": { "kind": "build", "isDefault": true }, - "detail": "Task generated by Debugger." + "problemMatcher": ["$gcc"], + "detail": "Generated task for building the project." } - ], - "version": "2.0.0" + ] } diff --git a/contributing.md b/contributing.md index 2ff411a..364e835 100644 --- a/contributing.md +++ b/contributing.md @@ -17,6 +17,12 @@ # Code style - ASCII only +- Class names are always singular +- Explanatory comments are placed above the line(s) they are explaining +- Source files should only contain comments that plainly state what the code is + supposed to do +- Explanatory comments in headers may be used to clarify implementation design + decisions - 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) - When using libraries of which the header include order is important, make @@ -30,6 +36,29 @@ as these are no longer accessible when the engine is installed - Getter and setter functions are appropriately prefixed with `get_` and `set_`. +- Doxygen commands are used with a backslash instead of an at-sign (i.e. + `\brief` instead of `@brief`) +- A singleton's instance is always accessed using a getter function that + instantiates its own class as a static variable within the getter function + scope, instead of storing the instance as a member variable directly: + + ```cpp + class Bad { + static Bad instance; + Bad & get_instance() { return instance; } + }; + + class Good { + Good & get_instance() { + static Good instance; + return instance; + } + }; + ``` +- Member variable default values should be directly defined in the class + declaration instead of using the constructor. +- Header files declare either a single class or symbols within a single + namespace. ## CMakeLists specific diff --git a/lib/soloud/src b/lib/soloud -Subproject e82fd32c1f62183922f08c14c814a02b58db187 +Subproject e82fd32c1f62183922f08c14c814a02b58db187 diff --git a/lib/soloud/CMakeLists.txt b/lib/soloud/CMakeLists.txt deleted file mode 100644 index aaccd96..0000000 --- a/lib/soloud/CMakeLists.txt +++ /dev/null @@ -1,97 +0,0 @@ -cmake_minimum_required(VERSION 3.28) - -set(CMAKE_C_STANDARD 11) -set(CMAKE_CXX_STANDARD 20) - -add_compile_definitions(WITH_SDL2) - -add_subdirectory(../sdl2 sdl2) - -project(soloud C CXX) - -add_library(soloud SHARED - src/src/audiosource/ay/chipplayer.cpp - src/src/audiosource/ay/sndbuffer.cpp - src/src/audiosource/ay/sndchip.cpp - src/src/audiosource/ay/sndrender.cpp - src/src/audiosource/ay/soloud_ay.cpp - src/src/audiosource/monotone/soloud_monotone.cpp - src/src/audiosource/noise/soloud_noise.cpp - src/src/audiosource/openmpt/soloud_openmpt.cpp - src/src/audiosource/openmpt/soloud_openmpt_dll.c - src/src/audiosource/sfxr/soloud_sfxr.cpp - src/src/audiosource/speech/darray.cpp - src/src/audiosource/speech/klatt.cpp - src/src/audiosource/speech/resonator.cpp - src/src/audiosource/speech/soloud_speech.cpp - src/src/audiosource/speech/tts.cpp - src/src/audiosource/tedsid/sid.cpp - src/src/audiosource/tedsid/soloud_tedsid.cpp - src/src/audiosource/tedsid/ted.cpp - src/src/audiosource/vic/soloud_vic.cpp - src/src/audiosource/vizsn/soloud_vizsn.cpp - src/src/audiosource/wav/dr_impl.cpp - src/src/audiosource/wav/soloud_wav.cpp - src/src/audiosource/wav/soloud_wavstream.cpp - src/src/audiosource/wav/stb_vorbis.c - - src/src/backend/alsa/soloud_alsa.cpp - src/src/backend/coreaudio/soloud_coreaudio.cpp - src/src/backend/jack/soloud_jack.cpp - src/src/backend/miniaudio/soloud_miniaudio.cpp - src/src/backend/nosound/soloud_nosound.cpp - src/src/backend/null/soloud_null.cpp - src/src/backend/openal/soloud_openal.cpp - src/src/backend/openal/soloud_openal_dll.c - src/src/backend/opensles/soloud_opensles.cpp - src/src/backend/oss/soloud_oss.cpp - src/src/backend/portaudio/soloud_portaudio.cpp - src/src/backend/portaudio/soloud_portaudio_dll.c - src/src/backend/sdl/soloud_sdl1.cpp - src/src/backend/sdl/soloud_sdl1_dll.c - src/src/backend/sdl/soloud_sdl2.cpp - src/src/backend/sdl/soloud_sdl2_dll.c - src/src/backend/sdl2_static/soloud_sdl2_static.cpp - src/src/backend/sdl_static/soloud_sdl_static.cpp - src/src/backend/wasapi/soloud_wasapi.cpp - src/src/backend/winmm/soloud_winmm.cpp - src/src/backend/xaudio2/soloud_xaudio2.cpp - - src/src/core/soloud.cpp - src/src/core/soloud_audiosource.cpp - src/src/core/soloud_bus.cpp - src/src/core/soloud_core_3d.cpp - src/src/core/soloud_core_basicops.cpp - src/src/core/soloud_core_faderops.cpp - src/src/core/soloud_core_filterops.cpp - src/src/core/soloud_core_getters.cpp - src/src/core/soloud_core_setters.cpp - src/src/core/soloud_core_voicegroup.cpp - src/src/core/soloud_core_voiceops.cpp - src/src/core/soloud_fader.cpp - src/src/core/soloud_fft.cpp - src/src/core/soloud_fft_lut.cpp - src/src/core/soloud_file.cpp - src/src/core/soloud_filter.cpp - src/src/core/soloud_misc.cpp - src/src/core/soloud_queue.cpp - src/src/core/soloud_thread.cpp - - # src/src/filter/soloud_bassboostfilter.cpp - # src/src/filter/soloud_biquadresonantfilter.cpp - # src/src/filter/soloud_dcremovalfilter.cpp - # src/src/filter/soloud_duckfilter.cpp - # src/src/filter/soloud_echofilter.cpp - # src/src/filter/soloud_eqfilter.cpp - # src/src/filter/soloud_fftfilter.cpp - # src/src/filter/soloud_flangerfilter.cpp - # src/src/filter/soloud_freeverbfilter.cpp - # src/src/filter/soloud_lofifilter.cpp - # src/src/filter/soloud_robotizefilter.cpp - # src/src/filter/soloud_waveshaperfilter.cpp -) -target_include_directories(soloud PRIVATE src/include) -target_include_directories(soloud SYSTEM INTERFACE src/include) - -target_link_libraries(soloud PRIVATE SDL2) - diff --git a/mwe/ecs-homemade/inc/ComponentManager.hpp b/mwe/ecs-homemade/inc/ComponentManager.hpp index 161ce71..d6da8e8 100644 --- a/mwe/ecs-homemade/inc/ComponentManager.hpp +++ b/mwe/ecs-homemade/inc/ComponentManager.hpp @@ -42,7 +42,8 @@ void ComponentManager::DeleteComponentsById(std::uint32_t id) { } } -template <typename T> void ComponentManager::DeleteComponents() { +template <typename T> +void ComponentManager::DeleteComponents() { std::type_index type = typeid( T); //Determine the type of T (this is used as the key of the unordered_map<>) diff --git a/mwe/ecs-homemade/inc/GameObjectMax.h b/mwe/ecs-homemade/inc/GameObjectMax.h index c88a834..3029053 100644 --- a/mwe/ecs-homemade/inc/GameObjectMax.h +++ b/mwe/ecs-homemade/inc/GameObjectMax.h @@ -7,7 +7,8 @@ class GameObject { public: GameObject(std::uint32_t id, std::string name, std::string tag, int layer); - template <typename T, typename... Args> void AddComponent(Args &&... args); + template <typename T, typename... Args> + void AddComponent(Args &&... args); std::uint32_t mId; std::string mName; @@ -8,18 +8,45 @@ This repository contains: |`mwe/`|minimal working examples and proof-of-concepts| |`src/crepe/`|game engine source code| |`src/test/`|unit tests| -|`src/example`|standalone examples using game engine| +|`src/example/`|standalone examples using game engine| ## Compilation This repository uses CMake (the makefile in the root of this repository is for -running auxiliary tasks only). Make sure you have initialized and updated the -submodules before compiling. +running auxiliary tasks only). See [src/readme.md](src/readme.md) for detailed +building instructions. + +## Installing libraries + +The expected library (source) versions are included in this repository as git +submodules. Follow these steps for manually building one of the required +libraries from source: + +1. Ensure the git submodules are initialized: + ``` + $ git submodule update --init --recursive --depth 1 + ``` +2. `cd` into the library source folder: + ``` + $ cd lib/googletest + or + $ cd lib/sdl2 + or + $ cd lib/soloud/contrib + ``` +3. Configure the build, run the build and install: + ``` + $ cmake -B build -G Ninja + $ ninja -C build + # ninja -C build install + ``` ## Documentation -API documentation is done using Doxygen. To generate the docs, run `make -doxygen`. +API documentation is done using Doxygen. To generate the docs, run +``` +$ make doxygen +``` ## Code style diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 62ca9a0..09c60bd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,16 +3,14 @@ cmake_minimum_required(VERSION 3.28) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) - -# enable debug features set(CMAKE_BUILD_TYPE Debug) -add_compile_definitions(DEBUG) - -add_subdirectory(../lib/soloud soloud) -add_subdirectory(../lib/googletest googletest) project(crepe C CXX) +find_package(SDL2 REQUIRED) +find_package(SoLoud REQUIRED) +find_package(GTest REQUIRED) + add_library(crepe SHARED) add_executable(test_main EXCLUDE_FROM_ALL) @@ -20,9 +18,9 @@ target_include_directories(crepe PUBLIC SYSTEM INTERFACE . ) -# TODO: libraries should be linked as PRIVATE target_link_libraries(crepe - PUBLIC soloud + PRIVATE soloud + PRIVATE SDL2 ) add_subdirectory(crepe) diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 208ba1f..d85aef0 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -2,12 +2,30 @@ target_sources(crepe PUBLIC Asset.cpp Sound.cpp SoundContext.cpp + ComponentManager.cpp + Component.cpp + GameObject.cpp + Collider.cpp + Rigidbody.cpp + Sprite.cpp + ScriptSystem.cpp + Script.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES Asset.h Sound.h SoundContext.h + ComponentManager.h + ComponentManager.hpp + Component.h + GameObject.h + GameObject.hpp + Collider.h + Rigidbody.h + Sprite.h + System.h + ScriptSystem.h ) add_subdirectory(api) diff --git a/src/crepe/Collider.cpp b/src/crepe/Collider.cpp new file mode 100644 index 0000000..3f12afd --- /dev/null +++ b/src/crepe/Collider.cpp @@ -0,0 +1,5 @@ +#include "Collider.h" + +using namespace crepe; + +Collider::Collider(int size) : size(size) {} diff --git a/src/crepe/Collider.h b/src/crepe/Collider.h new file mode 100644 index 0000000..120da05 --- /dev/null +++ b/src/crepe/Collider.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Component.h" + +namespace crepe { + +class Collider : public Component { +public: + Collider(int size); + + int size; +}; + +} // namespace crepe diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp new file mode 100644 index 0000000..d14159c --- /dev/null +++ b/src/crepe/Component.cpp @@ -0,0 +1,5 @@ +#include "Component.h" + +using namespace crepe; + +Component::Component() : active(true) {} diff --git a/src/crepe/Component.h b/src/crepe/Component.h new file mode 100644 index 0000000..16a4ce5 --- /dev/null +++ b/src/crepe/Component.h @@ -0,0 +1,14 @@ +#pragma once + +namespace crepe { + +class Component { +public: + Component(); + // TODO: shouldn't this constructor be deleted because this class will never + // directly be instantiated? + + bool active; +}; + +} // namespace crepe diff --git a/src/crepe/ComponentManager.cpp b/src/crepe/ComponentManager.cpp new file mode 100644 index 0000000..9a3fec7 --- /dev/null +++ b/src/crepe/ComponentManager.cpp @@ -0,0 +1,24 @@ +#include "ComponentManager.h" + +using namespace crepe; + +ComponentManager & ComponentManager::get_instance() { + static ComponentManager instance; + return instance; +} + +void ComponentManager::delete_all_components_of_id(uint32_t id) { + // Loop through all the types (in the unordered_map<>) + for (auto & [type, componentArray] : components) { + // Make sure that the id (that we are looking for) is within the boundaries of the vector<> + if (id < componentArray.size()) { + // Clear the components at this specific id + componentArray[id].clear(); + } + } +} + +void ComponentManager::delete_all_components() { + // Clear the whole unordered_map<> + components.clear(); +} diff --git a/src/crepe/ComponentManager.h b/src/crepe/ComponentManager.h new file mode 100644 index 0000000..2ab9dc8 --- /dev/null +++ b/src/crepe/ComponentManager.h @@ -0,0 +1,62 @@ +#pragma once + +#include <cstdint> +#include <memory> +#include <typeindex> +#include <unordered_map> +#include <utility> +#include <vector> + +#include "Component.h" + +namespace crepe { + +class ComponentManager { +public: + // Singleton + static ComponentManager & get_instance(); + ComponentManager(const ComponentManager &) = delete; + ComponentManager(ComponentManager &&) = delete; + ComponentManager & operator=(const ComponentManager &) = delete; + ComponentManager & operator=(ComponentManager &&) = delete; + +public: + //! Add a component of a specific type + template <typename T, typename... Args> + void 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); + //! Deletes all components of a specific type + template <typename T> + void delete_components(); + //! Deletes all components of a specific id + void delete_all_components_of_id(uint32_t id); + //! Deletes all components + void delete_all_components(); + + //! Get a vector<> of all components at specific type and id + template <typename T> + std::vector<std::reference_wrapper<T>> + get_components_by_id(uint32_t id) const; + //! Get a vector<> of all components of a specific type + template <typename T> + std::vector<std::reference_wrapper<T>> get_components_by_type() const; + +private: + ComponentManager() = default; + + /* + * 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: + * The std::unordered_map<> has a key and value. The key is a std::type_index and the value is a std::vector. So, a new std::vector will be created for each new std::type_index. + * The first std::vector<> stores another vector<>. This first vector<> is to bind the entity's id to a component. + * The second std::vector<> stores unique_ptrs. Each component can be gathered via an unique_ptr. This second vector<> allows multiple components of the same std::type_index for one entity (id). + */ + std::unordered_map<std::type_index, + std::vector<std::vector<std::unique_ptr<Component>>>> + components; +}; + +} // namespace crepe + +#include "ComponentManager.hpp" diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp new file mode 100644 index 0000000..084cd33 --- /dev/null +++ b/src/crepe/ComponentManager.hpp @@ -0,0 +1,152 @@ +#pragma once + +#include <type_traits> + +#include "ComponentManager.h" + +namespace crepe { + +template <class T, typename... Args> +void 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"); + + // Determine the type of T (this is used as the key of the unordered_map<>) + type_index type = typeid(T); + + // Check if this component type is already in the unordered_map<> + if (components.find(type) == components.end()) { + //If not, create a new (empty) vector<> of vector<unique_ptr<Component>> + components[type] = vector<vector<unique_ptr<Component>>>(); + } + + // Resize the vector<> if the id is greater than the current size + if (id >= components[type].size()) { + // Initialize new slots to nullptr (resize does automatically init to nullptr) + components[type].resize(id + 1); + } + + // Create a new component of type T (arguments directly forwarded). The + // constructor must be called by ComponentManager. + T * instance = new T(forward<Args>(args)...); + // store its unique_ptr in the vector<> + components[type][id].push_back(unique_ptr<T>(instance)); +} + +template <typename T> +void ComponentManager::delete_components_by_id(uint32_t id) { + using namespace std; + + // Determine the type of T (this is used as the key of the unordered_map<>) + type_index type = typeid(T); + + // Find the type (in the unordered_map<>) + if (components.find(type) != components.end()) { + // Get the correct vector<> + vector<vector<unique_ptr<Component>>> & component_array + = components[type]; + + // Make sure that the id (that we are looking for) is within the boundaries of the vector<> + if (id < component_array.size()) { + // Clear the whole vector<> of this specific type and id + component_array[id].clear(); + } + } +} + +template <typename T> +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(); + } +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::get_components_by_id(uint32_t id) const { + using namespace std; + + // Determine the type of T (this is used as the key of the unordered_map<>) + type_index type = typeid(T); + + // 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); + + // 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); + } + } + } + } + + // Return the vector<> + return component_vector; +} + +template <typename T> +std::vector<std::reference_wrapper<T>> +ComponentManager::get_components_by_type() const { + using namespace std; + + // Determine the type of T (this is used as the key of the unordered_map<>) + type_index type = typeid(T); + + // Create an empty vector<> + vector<reference_wrapper<T>> component_vector; + // Set the id to 0 (the id will also be stored in the returned vector<>) + // uint32_t id = 0; + + // 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); + + // 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; + } + } + + // Return the vector<> + return component_vector; +} + +} // namespace crepe diff --git a/src/crepe/GameObject.cpp b/src/crepe/GameObject.cpp new file mode 100644 index 0000000..de3beb6 --- /dev/null +++ b/src/crepe/GameObject.cpp @@ -0,0 +1,7 @@ +#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.h b/src/crepe/GameObject.h new file mode 100644 index 0000000..3588d9a --- /dev/null +++ b/src/crepe/GameObject.h @@ -0,0 +1,24 @@ +#pragma once + +#include <cstdint> +#include <string> + +namespace crepe { + +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); + + uint32_t id; + std::string name; + std::string tag; + bool active; + int layer; +}; + +} // namespace crepe + +#include "GameObject.hpp" diff --git a/src/crepe/GameObject.hpp b/src/crepe/GameObject.hpp new file mode 100644 index 0000000..5966fbf --- /dev/null +++ b/src/crepe/GameObject.hpp @@ -0,0 +1,15 @@ +#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/Rigidbody.cpp b/src/crepe/Rigidbody.cpp new file mode 100644 index 0000000..495d908 --- /dev/null +++ b/src/crepe/Rigidbody.cpp @@ -0,0 +1,6 @@ +#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 new file mode 100644 index 0000000..63a8877 --- /dev/null +++ b/src/crepe/Rigidbody.h @@ -0,0 +1,16 @@ +#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/Script.cpp b/src/crepe/Script.cpp new file mode 100644 index 0000000..42e3666 --- /dev/null +++ b/src/crepe/Script.cpp @@ -0,0 +1,7 @@ +#include "Script.h" + +using namespace crepe; + +void Script::init() { } +void Script::update() { } + diff --git a/src/crepe/Script.h b/src/crepe/Script.h new file mode 100644 index 0000000..ba4073a --- /dev/null +++ b/src/crepe/Script.h @@ -0,0 +1,16 @@ +#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 new file mode 100644 index 0000000..e301c71 --- /dev/null +++ b/src/crepe/ScriptSystem.cpp @@ -0,0 +1,22 @@ +#include "util/log.h" + +#include "ScriptSystem.h" + +using namespace crepe; + +ScriptSystem::ScriptSystem() { + dbg_trace(); +} +ScriptSystem::~ScriptSystem() { + dbg_trace(); +} + +ScriptSystem & ScriptSystem::get_instance() { + static ScriptSystem instance; + return instance; +} + +void ScriptSystem::update() { + dbg_trace(); +} + diff --git a/src/crepe/ScriptSystem.h b/src/crepe/ScriptSystem.h new file mode 100644 index 0000000..e1ed290 --- /dev/null +++ b/src/crepe/ScriptSystem.h @@ -0,0 +1,18 @@ +#pragma once + +#include "System.h" + +namespace crepe { + +class ScriptSystem : public System { +public: + static ScriptSystem & get_instance(); + virtual void update(); + +private: + ScriptSystem(); + ~ScriptSystem(); +}; + +} + diff --git a/src/crepe/Sound.h b/src/crepe/Sound.h index 1ac20a7..b7cfbb8 100644 --- a/src/crepe/Sound.h +++ b/src/crepe/Sound.h @@ -1,7 +1,7 @@ #pragma once -#include <soloud.h> -#include <soloud_wav.h> +#include <soloud/soloud.h> +#include <soloud/soloud_wav.h> #include <memory> diff --git a/src/crepe/SoundContext.h b/src/crepe/SoundContext.h index 090966d..d3123d2 100644 --- a/src/crepe/SoundContext.h +++ b/src/crepe/SoundContext.h @@ -1,6 +1,6 @@ #pragma once -#include <soloud.h> +#include <soloud/soloud.h> #include "Sound.h" diff --git a/src/crepe/Sprite.cpp b/src/crepe/Sprite.cpp new file mode 100644 index 0000000..a5a5e68 --- /dev/null +++ b/src/crepe/Sprite.cpp @@ -0,0 +1,8 @@ +#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 new file mode 100644 index 0000000..143e702 --- /dev/null +++ b/src/crepe/Sprite.h @@ -0,0 +1,16 @@ +#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 new file mode 100644 index 0000000..3fe3d66 --- /dev/null +++ b/src/crepe/System.h @@ -0,0 +1,23 @@ +#pragma once + +namespace crepe { + +class System { +public: + static System & get_instance(); + virtual void update() = 0; + +protected: + System() { }; + virtual ~System() { }; + +private: + // singleton + System(const System &) = delete; + System(System &&) = delete; + System & operator=(const System &) = delete; + System & operator=(System &&) = delete; +}; + +} + diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp new file mode 100644 index 0000000..2dd933e --- /dev/null +++ b/src/crepe/api/BehaviorScript.cpp @@ -0,0 +1,10 @@ +#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 new file mode 100644 index 0000000..e9542c1 --- /dev/null +++ b/src/crepe/api/BehaviorScript.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../Script.h" +#include "../Component.h" + +namespace crepe::api { + +class BehaviorScript : public Script, public Component { + // only allow ComponentManager to instantiate scripts + friend class ComponentManager; + +protected: + BehaviorScript(); +}; + +} + diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 9548594..86623de 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -1,9 +1,10 @@ target_sources(crepe PUBLIC # AudioSource.cpp + BehaviorScript.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - AudioSource.h - Component.h + # AudioSource.h + BehaviorScript.h ) diff --git a/src/crepe/api/Component.h b/src/crepe/api/Component.h deleted file mode 100644 index d5e0499..0000000 --- a/src/crepe/api/Component.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -namespace crepe::api { - -class Component { -public: - bool active; -}; - -} // namespace crepe::api diff --git a/src/crepe/util/log.cpp b/src/crepe/util/log.cpp index 6829ec3..f91d52c 100644 --- a/src/crepe/util/log.cpp +++ b/src/crepe/util/log.cpp @@ -8,10 +8,10 @@ using namespace crepe::util; static const char * const LOG_PREFIX[] = { - [log_level::debug] = "[DBG] ", - [log_level::info] = "[INFO] ", - [log_level::warning] = "[WARN] ", - [log_level::error] = "[ERR] ", + [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) { @@ -38,7 +38,7 @@ static void va_logf(enum log_level level, va_list args, const std::string fmt) { void crepe::util::logf(const char * fmt, ...) { va_list args; va_start(args, fmt); - va_logf(crepe::util::log_level::debug, args, fmt); + va_logf(crepe::util::log_level::DEBUG, args, fmt); va_end(args); } diff --git a/src/crepe/util/log.h b/src/crepe/util/log.h index bfe7291..2b0fbe1 100644 --- a/src/crepe/util/log.h +++ b/src/crepe/util/log.h @@ -7,7 +7,7 @@ // utility macros #define _crepe_logf_here(fmt, ...) \ - crepe::util::logf(util::log_level::debug, "%s%s (%s:%d)" fmt "\n", \ + 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__) @@ -24,10 +24,10 @@ namespace crepe::util { enum log_level { - debug, - info, - warning, - error, + DEBUG, + INFO, + WARNING, + ERROR, }; void logf(const char * fmt, ...); diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index bcc9271..6df4ce7 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -1,3 +1,18 @@ -add_executable(audio_internal EXCLUDE_FROM_ALL audio_internal.cpp) -target_link_libraries(audio_internal PUBLIC crepe) +# add_example(target_name [SOURCES...]) +function(add_example target_name) + # if SOURCES is not specified + if(NOT ARGV1) + # A .cpp file with target_name exists, and should be used + set(sources ${target_name}.cpp) + else() + set(sources ${ARGV}) + endif() + + add_executable(${target_name} EXCLUDE_FROM_ALL ${sources}) + target_link_libraries(${target_name} PUBLIC crepe) +endfunction() + +add_example(audio_internal) +add_example(components_internal) +add_example(script) diff --git a/src/example/components_internal.cpp b/src/example/components_internal.cpp new file mode 100644 index 0000000..54ce295 --- /dev/null +++ b/src/example/components_internal.cpp @@ -0,0 +1,60 @@ +/** \file + * + * Standalone example for usage of the internal ECS + */ + +#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/util/log.h> + +using namespace crepe; +using namespace std; + +#define OBJ_COUNT 100000 + +int main() { + dbg_trace(); + + auto & mgr = ComponentManager::get_instance(); + + auto start_adding = chrono::high_resolution_clock::now(); + + GameObject * game_object[OBJ_COUNT]; + + for (int i = 0; i < OBJ_COUNT; ++i) { + game_object[i] = new GameObject(i, "Name", "Tag", 0); + + 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"); + } + + auto stop_looping = chrono::high_resolution_clock::now(); + + for (int i = 0; i < OBJ_COUNT; ++i) { + delete game_object[i]; + } + + 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/script.cpp b/src/example/script.cpp new file mode 100644 index 0000000..28605c7 --- /dev/null +++ b/src/example/script.cpp @@ -0,0 +1,33 @@ +/** \file + * + * 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/api/BehaviorScript.h> + +using namespace crepe; +using namespace std; + +class MyScript : public api::BehaviorScript { + void update() { + dbg_trace(); + } +}; + +int main() { + dbg_trace(); + + auto obj = GameObject(0, "name", "tag", 0); + obj.add_component<MyScript>(); + + auto & sys = ScriptSystem::get_instance(); + sys.update(); // -> MyScript::update + + return 0; +} + diff --git a/src/readme.md b/src/readme.md index 1c5d3a2..a4a71e7 100644 --- a/src/readme.md +++ b/src/readme.md @@ -2,7 +2,32 @@ This folder contains the crêpe engine source files, unit tests, and some toy examples. The only target built by default by the CMakeLists.txt in this folder -is the crêpe shared library object. Unit tests can be built by explicitly -specifying the target `test_main` when running the build command. Each source -file in the example/ folder corresponds to a CMake target as well. +is the crêpe shared library object. + +Examples (using Ninja): + +``` +$ cmake -B build -G Ninja +$ ninja -C build +``` + +Unit tests can be built by explicitly specifying the target `test_main` when +running the build command: + +``` +$ ninja -C build test_main +``` + +Each source file in the example/ folder corresponds to a CMake target as well: + +``` +$ ninja -C build audio_internal components_internal +``` + +For installing crêpe system-wide after building (install must be run with +elevated privileges): + +``` +# ninja -C build install +``` |