aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoek Le Blansch <loek@pipeframe.xyz>2024-11-14 11:10:31 +0100
committerLoek Le Blansch <loek@pipeframe.xyz>2024-11-14 11:10:31 +0100
commit6e13510f3c6d4155707f748d237bb1fa05243450 (patch)
treeb21ca972d14aac56b7c58c85c54d8a2fd1278a7e
parent319d511135da1b58acb78ca939d7ee01fee4bbf3 (diff)
parentbe1e97bc7a494963ab1567492fafcda99e36f683 (diff)
merge `master` into `loek/audio`
-rw-r--r--.clang-format1
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules8
-rw-r--r--contributing.md93
m---------lib/SDL_ttf0
m---------lib/libdb0
m---------lib/sdl_image0
m---------lib/sdl_ttf0
-rw-r--r--mwe/ecs-homemade/src/Components.cpp4
-rw-r--r--mwe/ecs-homemade/src/GameObjectMax.cpp6
-rw-r--r--mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp3
-rw-r--r--mwe/ecs-memory-efficient/src/Components.cpp4
-rw-r--r--mwe/ecs-memory-efficient/src/GameObjectMax.cpp6
-rw-r--r--mwe/events/include/customTypes.h4
-rw-r--r--mwe/events/include/eventHandler.h3
-rw-r--r--mwe/events/src/event.cpp31
-rw-r--r--mwe/events/src/main.cpp4
-rw-r--r--mwe/events/src/uiObject.cpp13
-rw-r--r--mwe/gameloop/src/gameObject.cpp7
-rw-r--r--readme.md39
-rw-r--r--src/crepe/Exception.cpp5
-rw-r--r--src/crepe/Exception.h3
-rw-r--r--src/crepe/Particle.cpp29
-rw-r--r--src/crepe/Particle.h64
-rw-r--r--src/crepe/ValueBroker.h7
-rw-r--r--src/crepe/ValueBroker.hpp11
-rw-r--r--src/crepe/api/Animator.cpp26
-rw-r--r--src/crepe/api/Animator.h76
-rw-r--r--src/crepe/api/AssetManager.h36
-rw-r--r--src/crepe/api/AudioSource.h2
-rw-r--r--src/crepe/api/CMakeLists.txt8
-rw-r--r--src/crepe/api/Camera.cpp18
-rw-r--r--src/crepe/api/Camera.h55
-rw-r--r--src/crepe/api/CircleCollider.h3
-rw-r--r--src/crepe/api/Color.cpp3
-rw-r--r--src/crepe/api/Color.h17
-rw-r--r--src/crepe/api/Config.cpp1
-rw-r--r--src/crepe/api/Config.h6
-rw-r--r--src/crepe/api/LoopManager.cpp55
-rw-r--r--src/crepe/api/LoopManager.h79
-rw-r--r--src/crepe/api/LoopTimer.cpp86
-rw-r--r--src/crepe/api/LoopTimer.h147
-rw-r--r--src/crepe/api/Metadata.cpp4
-rw-r--r--src/crepe/api/ParticleEmitter.cpp36
-rw-r--r--src/crepe/api/ParticleEmitter.h99
-rw-r--r--src/crepe/api/Rigidbody.cpp3
-rw-r--r--src/crepe/api/SaveManager.cpp53
-rw-r--r--src/crepe/api/SaveManager.h7
-rw-r--r--src/crepe/api/Sprite.cpp12
-rw-r--r--src/crepe/api/Sprite.h76
-rw-r--r--src/crepe/api/Texture.cpp15
-rw-r--r--src/crepe/api/Texture.h54
-rw-r--r--src/crepe/api/Transform.cpp5
-rw-r--r--src/crepe/api/Transform.h2
-rw-r--r--src/crepe/api/Vector2.cpp6
-rw-r--r--src/crepe/api/Vector2.h10
-rw-r--r--src/crepe/facade/DB.cpp20
-rw-r--r--src/crepe/facade/DB.h7
-rw-r--r--src/crepe/facade/SDLContext.cpp201
-rw-r--r--src/crepe/facade/SDLContext.h129
-rw-r--r--src/crepe/system/AnimatorSystem.cpp37
-rw-r--r--src/crepe/system/AnimatorSystem.h44
-rw-r--r--src/crepe/system/AudioSystem.cpp2
-rw-r--r--src/crepe/system/AudioSystem.h8
-rw-r--r--src/crepe/system/CMakeLists.txt2
-rw-r--r--src/crepe/system/ParticleSystem.cpp157
-rw-r--r--src/crepe/system/ParticleSystem.h67
-rw-r--r--src/crepe/system/PhysicsSystem.h10
-rw-r--r--src/crepe/system/RenderSystem.cpp36
-rw-r--r--src/crepe/system/RenderSystem.h49
-rw-r--r--src/crepe/util/Proxy.h7
-rw-r--r--src/crepe/util/Proxy.hpp9
-rw-r--r--src/example/CMakeLists.txt2
-rw-r--r--src/example/db.cpp4
-rw-r--r--src/example/gameloop.cpp7
-rw-r--r--src/example/particles.cpp43
-rw-r--r--src/example/proxy.cpp20
-rw-r--r--src/example/rendering.cpp13
-rw-r--r--src/example/savemgr.cpp13
-rw-r--r--src/makefile100
-rw-r--r--src/test/AudioTest.cpp24
-rw-r--r--src/test/CMakeLists.txt6
-rw-r--r--src/test/ParticleTest.cpp206
-rw-r--r--src/test/audio.cpp10
-rw-r--r--src/test/dummy.cpp3
85 files changed, 2062 insertions, 521 deletions
diff --git a/.clang-format b/.clang-format
index 3ae6c28..8ce4033 100644
--- a/.clang-format
+++ b/.clang-format
@@ -23,6 +23,7 @@ ReflowComments: false
AlignEscapedNewlines: DontAlign
BreakBeforeBinaryOperators: All
AlwaysBreakTemplateDeclarations: Yes
+PackConstructorInitializers: CurrentLine
...
# vim: ft=yaml
diff --git a/.gitignore b/.gitignore
index 180afb8..8bfb819 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,4 @@ compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json
-
+compile.sh
diff --git a/.gitmodules b/.gitmodules
index 481519b..2f64601 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -14,3 +14,11 @@
path = lib/sdl_image
url = https://github.com/libsdl-org/SDL_image
shallow = true
+[submodule "lib/sdl_ttf"]
+ path = lib/sdl_ttf
+ url = https://github.com/libsdl-org/SDL_ttf.git
+ shallow = true
+[submodule "lib/libdb"]
+ path = lib/libdb
+ url = https://github.com/berkeleydb/libdb
+ shallow = true
diff --git a/contributing.md b/contributing.md
index 8799057..3090727 100644
--- a/contributing.md
+++ b/contributing.md
@@ -54,8 +54,6 @@ that you can click on to open them.
- Implementation details (if applicable)
- Header files (`.h`) contain the following types of comments:
- [Usage documentation](#documentation) (required)
- > [!NOTE]
- > Constructors/destructors aren't required to have a `\brief` description
- Implementation details (if they affect the header)
- Design/data structure decisions (if applicable)
- <details><summary>
@@ -436,7 +434,8 @@ that you can click on to open them.
```
</td></tr></table></details>
- <details><summary>
- Variables that are being moved always use the fully qualified <code>std::move</code>
+ Variables that are being moved always use the fully qualified
+ <code>std::move</code>
</summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
```cpp
@@ -652,6 +651,33 @@ that you can click on to open them.
bool bar = 0;
```
</td></tr></table></details>
+- <details><summary>
+ The reason for <code>friend</code> relations are documented
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ //! ComponentManager calls the private constructor of this class
+ friend class ComponentManager;
+ ```
+ </td><td>
+
+ ```cpp
+ friend class ComponentManager;
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Do not <i>pick</i> fixed-width integer types (unless required)
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ unsigned long long foo();
+ ```
+ </td><td>
+
+ ```cpp
+ uint64_t foo();
+ ```
+ </td></tr></table></details>
## CMakeLists-specific
@@ -702,6 +728,67 @@ that you can click on to open them.
void foo();
```
</td></tr></table></details>
+- <details><summary>
+ The default constructor and destructor aren't required to have a
+ <code>\brief</code> description
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ Foo();
+ virtual ~Foo();
+ ```
+ </td><td>
+
+ ```cpp
+ //! Create instance of Foo
+ Foo();
+ //! Destroy instance of Foo
+ virtual ~Foo();
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Parameter direction shouldn't be specified using Doxygen
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ /**
+ * \param bar Reference to Bar
+ */
+ void foo(const Bar & bar);
+ ```
+ </td><td>
+
+ ```cpp
+ /**
+ * \param[in] bar Reference to Bar
+ */
+ void foo(const Bar & bar);
+ ```
+ </td></tr></table></details>
+- <details><summary>
+ Deleted functions shouldn't have Doxygen comments
+ </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td>
+
+ ```cpp
+ // singleton
+ Foo(const Foo &) = delete;
+ Foo(Foo &&) = delete;
+ Foo & operator=(const Foo &) = delete;
+ Foo & operator=(Foo &&) = delete;
+ ```
+ </td><td>
+
+ ```cpp
+ //! Deleted copy constructor
+ Foo(const Foo &) = delete;
+ //! Deleted move constructor
+ Foo(Foo &&) = delete;
+ //! Deleted copy assignment operator
+ Foo & operator=(const Foo &) = delete;
+ //! Deleted move assignment operator
+ Foo & operator=(Foo &&) = delete;
+ ```
+ </td></tr></table></details>
# Libraries
diff --git a/lib/SDL_ttf b/lib/SDL_ttf
deleted file mode 160000
-Subproject a3d0895c1b60c41ff9e85d9203ddd7485c014da
diff --git a/lib/libdb b/lib/libdb
new file mode 160000
+Subproject 5b7b02ae052442626af54c176335b67ecc613a3
diff --git a/lib/sdl_image b/lib/sdl_image
-Subproject c6c7278b86b5de1232b10de8f612ed05cf2d11f
+Subproject abcf63aa71b4e3ac32120fa9870a6500ddcdcc8
diff --git a/lib/sdl_ttf b/lib/sdl_ttf
new file mode 160000
+Subproject 4a318f8dfaa1bb6f10e0c5e54052e25d3c7f344
diff --git a/mwe/ecs-homemade/src/Components.cpp b/mwe/ecs-homemade/src/Components.cpp
index de8753e..0d62bd5 100644
--- a/mwe/ecs-homemade/src/Components.cpp
+++ b/mwe/ecs-homemade/src/Components.cpp
@@ -6,7 +6,9 @@ Component::Component() : mActive(true) {}
Sprite::Sprite(std::string path) : mPath(path) {}
Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType)
- : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {}
+ : mMass(mass),
+ mGravityScale(gravityScale),
+ mBodyType(bodyType) {}
Colider::Colider(int size) : mSize(size) {}
diff --git a/mwe/ecs-homemade/src/GameObjectMax.cpp b/mwe/ecs-homemade/src/GameObjectMax.cpp
index b0c5af7..753c8e2 100644
--- a/mwe/ecs-homemade/src/GameObjectMax.cpp
+++ b/mwe/ecs-homemade/src/GameObjectMax.cpp
@@ -4,4 +4,8 @@
GameObject::GameObject(std::uint32_t id, std::string name, std::string tag,
int layer)
- : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {}
+ : mId(id),
+ mName(name),
+ mTag(tag),
+ mActive(true),
+ mLayer(layer) {}
diff --git a/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp
index 408d5aa..ff8fde4 100644
--- a/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp
+++ b/mwe/ecs-memory-efficient/inc/ContiguousContainer.hpp
@@ -1,5 +1,6 @@
template <typename T>
-ContiguousContainer<T>::ContiguousContainer() : mSize(0), mCapacity(10) {
+ContiguousContainer<T>::ContiguousContainer() : mSize(0),
+ mCapacity(10) {
// Allocate memory for 10 objects initially
mData = static_cast<T *>(malloc(mCapacity * sizeof(T)));
if (!mData) {
diff --git a/mwe/ecs-memory-efficient/src/Components.cpp b/mwe/ecs-memory-efficient/src/Components.cpp
index c8347b3..2ec8609 100644
--- a/mwe/ecs-memory-efficient/src/Components.cpp
+++ b/mwe/ecs-memory-efficient/src/Components.cpp
@@ -6,6 +6,8 @@ Component::Component() : mActive(true) {}
Sprite::Sprite(std::string path) : mPath(path) {}
Rigidbody::Rigidbody(int mass, int gravityScale, int bodyType)
- : mMass(mass), mGravityScale(gravityScale), mBodyType(bodyType) {}
+ : mMass(mass),
+ mGravityScale(gravityScale),
+ mBodyType(bodyType) {}
Colider::Colider(int size) : mSize(size) {}
diff --git a/mwe/ecs-memory-efficient/src/GameObjectMax.cpp b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp
index b0c5af7..753c8e2 100644
--- a/mwe/ecs-memory-efficient/src/GameObjectMax.cpp
+++ b/mwe/ecs-memory-efficient/src/GameObjectMax.cpp
@@ -4,4 +4,8 @@
GameObject::GameObject(std::uint32_t id, std::string name, std::string tag,
int layer)
- : mId(id), mName(name), mTag(tag), mActive(true), mLayer(layer) {}
+ : mId(id),
+ mName(name),
+ mTag(tag),
+ mActive(true),
+ mLayer(layer) {}
diff --git a/mwe/events/include/customTypes.h b/mwe/events/include/customTypes.h
index a5d8dc9..415b989 100644
--- a/mwe/events/include/customTypes.h
+++ b/mwe/events/include/customTypes.h
@@ -33,6 +33,8 @@ struct Collision {
// Constructor to initialize a Collision
Collision(int idA, int idB, const Vector2 & point, const Vector2 & normal,
float depth)
- : objectIdA(idA), objectIdB(idB), contactPoint(point),
+ : objectIdA(idA),
+ objectIdB(idB),
+ contactPoint(point),
contactNormal(normal) {}
};
diff --git a/mwe/events/include/eventHandler.h b/mwe/events/include/eventHandler.h
index aa8f63b..3a83b15 100644
--- a/mwe/events/include/eventHandler.h
+++ b/mwe/events/include/eventHandler.h
@@ -24,7 +24,8 @@ class EventHandlerWrapper : public IEventHandlerWrapper {
public:
explicit EventHandlerWrapper(const EventHandler<EventType> & handler,
const bool destroyOnSuccess = false)
- : m_handler(handler), m_handlerType(m_handler.target_type().name()),
+ : m_handler(handler),
+ m_handlerType(m_handler.target_type().name()),
m_destroyOnSuccess(destroyOnSuccess) {
// std::cout << m_handlerType << std::endl;
}
diff --git a/mwe/events/src/event.cpp b/mwe/events/src/event.cpp
index 0c9f3ed..8ffa0b1 100644
--- a/mwe/events/src/event.cpp
+++ b/mwe/events/src/event.cpp
@@ -23,7 +23,9 @@ void Event::markHandled() { isHandled = true; }
// KeyPressedEvent class methods
KeyPressedEvent::KeyPressedEvent(int keycode)
- : Event("KeyPressedEvent"), key(keycode), repeatCount(0) {}
+ : Event("KeyPressedEvent"),
+ key(keycode),
+ repeatCount(0) {}
Keycode KeyPressedEvent::getKeyCode() const { return key; }
@@ -31,13 +33,16 @@ int KeyPressedEvent::getRepeatCount() const { return repeatCount; }
// KeyReleasedEvent class methods
KeyReleasedEvent::KeyReleasedEvent(int keycode)
- : Event("KeyReleasedEvent"), key(keycode) {}
+ : Event("KeyReleasedEvent"),
+ key(keycode) {}
Keycode KeyReleasedEvent::getKeyCode() const { return key; }
// MousePressedEvent class methods
MousePressedEvent::MousePressedEvent(int mouseX, int mouseY)
- : Event("MousePressedEvent"), mouseX(mouseX), mouseY(mouseY) {}
+ : Event("MousePressedEvent"),
+ mouseX(mouseX),
+ mouseY(mouseY) {}
std::pair<int, int> MousePressedEvent::getMousePosition() const {
return {mouseX, mouseY};
@@ -45,26 +50,36 @@ std::pair<int, int> MousePressedEvent::getMousePosition() const {
//Collision event
CollisionEvent::CollisionEvent(Collision collision)
- : collisionData(collision), Event("CollisionEvent") {}
+ : collisionData(collision),
+ Event("CollisionEvent") {}
Collision CollisionEvent::getCollisionData() const {
return this->collisionData;
}
TextSubmitEvent::TextSubmitEvent(std::string text)
- : text(text), Event("TextSubmitEvent") {}
+ : text(text),
+ Event("TextSubmitEvent") {}
std::string TextSubmitEvent::getText() const { return this->text; }
MouseReleasedEvent::MouseReleasedEvent(int x, int y, MouseButton button)
- : mouseX(x), mouseY(y), button(button), Event("MouseReleased") {}
+ : mouseX(x),
+ mouseY(y),
+ button(button),
+ Event("MouseReleased") {}
std::pair<int, int> MouseReleasedEvent::getMousePosition() const {
return {mouseX, mouseY};
}
MouseClickEvent::MouseClickEvent(int x, int y, MouseButton button)
- : mouseX(x), mouseY(y), button(button), Event("MouseClickEvent") {}
+ : mouseX(x),
+ mouseY(y),
+ button(button),
+ Event("MouseClickEvent") {}
MouseMovedEvent::MouseMovedEvent(int x, int y)
- : mouseX(x), mouseY(y), Event("MouseMovedEvent") {}
+ : mouseX(x),
+ mouseY(y),
+ Event("MouseMovedEvent") {}
std::pair<int, int> MouseClickEvent::getMousePosition() const {
return {mouseX, mouseY};
}
diff --git a/mwe/events/src/main.cpp b/mwe/events/src/main.cpp
index d49cf74..f4e7390 100644
--- a/mwe/events/src/main.cpp
+++ b/mwe/events/src/main.cpp
@@ -11,7 +11,9 @@
class PlayerDamagedEvent : public Event {
public:
PlayerDamagedEvent(int damage, int playerID)
- : Event("PlayerDamaged"), damage(damage), playerID(playerID) {}
+ : Event("PlayerDamaged"),
+ damage(damage),
+ playerID(playerID) {}
REGISTER_EVENT_TYPE(PlayerDamagedEvent);
diff --git a/mwe/events/src/uiObject.cpp b/mwe/events/src/uiObject.cpp
index 8405469..947d1a2 100644
--- a/mwe/events/src/uiObject.cpp
+++ b/mwe/events/src/uiObject.cpp
@@ -7,7 +7,9 @@ UIObject::UIObject(int width, int height) : width(width), height(height) {}
Button::Button(int width, int height) : UIObject(width, height) {}
Text::Text(int width, int height)
- : UIObject(width, height), size(12), font(nullptr),
+ : UIObject(width, height),
+ size(12),
+ font(nullptr),
color{255, 255, 255} { // Default size and color
alignment.horizontal = Alignment::Horizontal::CENTER;
alignment.vertical = Alignment::Vertical::MIDDLE;
@@ -15,8 +17,13 @@ Text::Text(int width, int height)
}
TextInput::TextInput(int width, int height)
- : UIObject(width, height), textBuffer(""), placeholder(""), isActive(false),
- textColor{255, 255, 255}, backgroundColor{0, 0, 0}, maxLength(100),
+ : UIObject(width, height),
+ textBuffer(""),
+ placeholder(""),
+ isActive(false),
+ textColor{255, 255, 255},
+ backgroundColor{0, 0, 0},
+ maxLength(100),
font(nullptr) {
alignment.horizontal = Alignment::Horizontal::LEFT;
alignment.vertical = Alignment::Vertical::TOP;
diff --git a/mwe/gameloop/src/gameObject.cpp b/mwe/gameloop/src/gameObject.cpp
index 78217c4..b33dc78 100644
--- a/mwe/gameloop/src/gameObject.cpp
+++ b/mwe/gameloop/src/gameObject.cpp
@@ -26,5 +26,10 @@ void GameObject::setVelY(float value) { velY = value; }
GameObject::GameObject(std::string name, float x, float y, float width,
float height, float velX, float velY)
- : name(name), x(x), y(y), width(width), height(height), velX(velX),
+ : name(name),
+ x(x),
+ y(y),
+ width(width),
+ height(height),
+ velX(velX),
velY(velY) {}
diff --git a/readme.md b/readme.md
index 7cf044f..c83853d 100644
--- a/readme.md
+++ b/readme.md
@@ -16,12 +16,32 @@ This repository uses CMake (the makefile in the root of this repository is for
running auxiliary tasks only). See [src/readme.md](src/readme.md) for detailed
building instructions.
-## Installing libraries
+## Code style
+
+Please read [contributing.md](./contributing.md).
+
+## Libraries
+
+This project uses the following libraries
+
+|Name|Version|
+|-|-:|
+|`SDL2`|2.30.9|
+|`SDL2_image`|2.8.2|
+|`SDL_ttf`|2.22.0|
+|`SoLoud`|(latest git `master` version)|
+|Google Test (`GTest`)|1.15.2|
+|Berkeley DB (`libdb`)|5.3.21|
+
+> [!NOTE]
+> Most of these libraries are likely available from your package manager if you
+> are using Linux or MinGW. Make sure your entire distribution is up-to-date if
+> you are not using a rolling release distribution.
-The expected library (source) versions are included in this repository as git
-submodules, which may be used if your distro's package manager does not provide
-(recent enough versions of) them. To build any of the dependencies, make sure
-the submodules are initialized by running:
+The sources for all of the above libraries are also included in this repository
+as git submodules, which may be used if your distro's package manager does not
+provide (recent enough versions of) them. To build any of the libraries
+manually, make sure the submodules are initialized by running:
```
$ git submodule update --init --recursive --depth 1
@@ -35,6 +55,7 @@ Then, follow these steps for each library you want to install:
$ cd lib/sdl2
$ cd lib/soloud/contrib
$ cd lib/sdl_image
+ $ cd lib/sdl_ttf
```
2. Use CMake to configure the build, run the build and install (run **all** of
these):
@@ -44,6 +65,10 @@ Then, follow these steps for each library you want to install:
# cmake --install build
```
+## Tooling
+
+- TODO
+
## Documentation
API documentation is done using Doxygen. To generate the docs, run
@@ -51,7 +76,3 @@ API documentation is done using Doxygen. To generate the docs, run
$ make doxygen
```
-## Code style
-
-Please read [contributing.md](./contributing.md).
-
diff --git a/src/crepe/Exception.cpp b/src/crepe/Exception.cpp
index f27d5a8..dab8f2e 100644
--- a/src/crepe/Exception.cpp
+++ b/src/crepe/Exception.cpp
@@ -6,9 +6,7 @@
using namespace std;
using namespace crepe;
-const char * Exception::what() {
- return error.c_str();
-}
+const char * Exception::what() { return error.c_str(); }
Exception::Exception(const char * fmt, ...) {
va_list args;
@@ -16,4 +14,3 @@ Exception::Exception(const char * fmt, ...) {
this->error = va_stringf(args, fmt);
va_end(args);
}
-
diff --git a/src/crepe/Exception.h b/src/crepe/Exception.h
index e4a7bb8..6473043 100644
--- a/src/crepe/Exception.h
+++ b/src/crepe/Exception.h
@@ -17,7 +17,6 @@ protected:
Exception() = default;
//! Formatted error message
std::string error;
-
};
-}
+} // namespace crepe
diff --git a/src/crepe/Particle.cpp b/src/crepe/Particle.cpp
index 4810e80..582edf4 100644
--- a/src/crepe/Particle.cpp
+++ b/src/crepe/Particle.cpp
@@ -2,19 +2,32 @@
using namespace crepe;
-Particle::Particle() { this->active = false; }
-
-void Particle::reset(float lifespan, Position position, Position velocity) {
+void Particle::reset(uint32_t lifespan, const Vector2 & position,
+ const Vector2 & velocity, double angle) {
+ // Initialize the particle state
this->time_in_life = 0;
this->lifespan = lifespan;
this->position = position;
this->velocity = velocity;
+ this->angle = angle;
this->active = true;
+ // Reset force accumulation
+ this->force_over_time = {0, 0};
+}
+
+void Particle::update() {
+ // Deactivate particle if it has exceeded its lifespan
+ if (++time_in_life >= lifespan) {
+ this->active = false;
+ return;
+ }
+
+ // Update velocity based on accumulated force and update position
+ this->velocity += force_over_time;
+ this->position += velocity;
}
-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;
+void Particle::stop_movement() {
+ // Reset velocity to halt movement
+ this->velocity = {0, 0};
}
diff --git a/src/crepe/Particle.h b/src/crepe/Particle.h
index 21e691d..3eaebc3 100644
--- a/src/crepe/Particle.h
+++ b/src/crepe/Particle.h
@@ -1,22 +1,64 @@
#pragma once
-#include "Position.h"
+#include <cstdint>
+
+#include "api/Vector2.h"
namespace crepe {
+/**
+ * \brief Represents a particle in the particle emitter.
+ *
+ * This class stores information about a single particle, including its position,
+ * velocity, lifespan, and other properties. Particles can be updated over time
+ * to simulate movement and can also be reset or stopped.
+ */
class Particle {
+ // TODO: add friend particleSsytem and rendersystem. Unit test will fail.
+
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;
+ //! Position of the particle in 2D space.
+ Vector2 position;
+ //! Velocity vector indicating the speed and direction of the particle.
+ Vector2 velocity;
+ //! Accumulated force affecting the particle over time.
+ Vector2 force_over_time;
+ //! Total lifespan of the particle in milliseconds.
+ uint32_t lifespan;
+ //! Active state of the particle; true if it is in use, false otherwise.
+ bool active = false;
+ //! The time the particle has been alive, in milliseconds.
+ uint32_t time_in_life = 0;
+ //! The angle at which the particle is oriented or moving.
+ double angle = 0;
- Particle();
- void reset(float lifespan, Position position, Position velocity);
- void update(float deltaTime);
- float time_in_life;
+ /**
+ * \brief Resets the particle with new properties.
+ *
+ * This method initializes the particle with a specific lifespan, position,
+ * velocity, and angle, marking it as active and resetting its life counter.
+ *
+ * \param lifespan The lifespan of the particle in amount of updates.
+ * \param position The starting position of the particle.
+ * \param velocity The initial velocity of the particle.
+ * \param angle The angle of the particle's trajectory or orientation.
+ */
+ void reset(uint32_t lifespan, const Vector2 & position,
+ const Vector2 & velocity, double angle);
+ /**
+ * \brief Updates the particle's state.
+ *
+ * Advances the particle's position based on its velocity and applies accumulated forces.
+ * Deactivates the particle if its lifespan has expired.
+ */
+ void update();
+ /**
+ * \brief Stops the particle's movement.
+ *
+ * Sets the particle's velocity to zero, effectively halting any further
+ * movement.
+ */
+ void stop_movement();
};
} // namespace crepe
diff --git a/src/crepe/ValueBroker.h b/src/crepe/ValueBroker.h
index 88988b4..d844d6a 100644
--- a/src/crepe/ValueBroker.h
+++ b/src/crepe/ValueBroker.h
@@ -23,10 +23,12 @@ public:
virtual const T & get();
typedef std::function<void(const T & target)> setter_t;
- typedef std::function<const T & ()> getter_t;
+ typedef std::function<const T &()> getter_t;
+
private:
setter_t setter;
getter_t getter;
+
public:
/**
* \param setter Function that sets the variable
@@ -35,7 +37,6 @@ public:
ValueBroker(const setter_t & setter, const getter_t & getter);
};
-}
+} // namespace crepe
#include "ValueBroker.hpp"
-
diff --git a/src/crepe/ValueBroker.hpp b/src/crepe/ValueBroker.hpp
index 0d08333..5c3bed9 100644
--- a/src/crepe/ValueBroker.hpp
+++ b/src/crepe/ValueBroker.hpp
@@ -5,11 +5,9 @@
namespace crepe {
template <typename T>
-ValueBroker<T>::ValueBroker(const setter_t & setter, const getter_t & getter) :
- setter(setter),
- getter(getter)
- {
-}
+ValueBroker<T>::ValueBroker(const setter_t & setter, const getter_t & getter)
+ : setter(setter),
+ getter(getter) {}
template <typename T>
const T & ValueBroker<T>::get() {
@@ -21,5 +19,4 @@ void ValueBroker<T>::set(const T & value) {
this->setter(value);
}
-}
-
+} // namespace crepe
diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp
new file mode 100644
index 0000000..58fee2a
--- /dev/null
+++ b/src/crepe/api/Animator.cpp
@@ -0,0 +1,26 @@
+
+#include <cstdint>
+
+#include "util/log.h"
+
+#include "Animator.h"
+#include "Component.h"
+#include "Sprite.h"
+
+using namespace crepe;
+
+Animator::Animator(uint32_t id, Sprite & ss, int row, int col, int col_animator)
+ : Component(id),
+ spritesheet(ss),
+ row(row),
+ col(col) {
+ dbg_trace();
+
+ animator_rect = spritesheet.sprite_rect;
+ animator_rect.h /= col;
+ animator_rect.w /= row;
+ animator_rect.x = 0;
+ animator_rect.y = col_animator * animator_rect.h;
+ this->active = false;
+}
+Animator::~Animator() { dbg_trace(); }
diff --git a/src/crepe/api/Animator.h b/src/crepe/api/Animator.h
new file mode 100644
index 0000000..def0240
--- /dev/null
+++ b/src/crepe/api/Animator.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <cstdint>
+
+#include "Component.h"
+#include "Sprite.h"
+
+namespace crepe {
+class AnimatorSystem;
+class SDLContext;
+
+/**
+ * \brief The Animator component is used to animate sprites by managing the movement
+ * and frame changes within a sprite sheet.
+ *
+ * This component allows for controlling sprite animation through rows and columns of a sprite sheet.
+ * It can be used to play animations, loop them, or stop them.
+ */
+class Animator : public Component {
+
+public:
+ //TODO: need to implement this
+ void loop();
+ void stop();
+
+public:
+ /**
+ * \brief Constructs an Animator object that will control animations for a sprite sheet.
+ *
+ * \param id The unique identifier for the component, typically assigned automatically.
+ * \param spritesheet A reference to the Sprite object which holds the sprite sheet for animation.
+ * \param row The maximum number of rows in the sprite sheet.
+ * \param col The maximum number of columns in the sprite sheet.
+ * \param col__animate The specific col index of the sprite sheet to animate. This allows selecting which col to animate from multiple col in the sheet.
+ *
+ * This constructor sets up the Animator with the given parameters, and initializes the animation system.
+ */
+ Animator(uint32_t id, Sprite & spritesheet, int row, int col,
+ int col_animate);
+
+ ~Animator(); // dbg_trace
+ Animator(const Animator &) = delete;
+ Animator(Animator &&) = delete;
+ Animator & operator=(const Animator &) = delete;
+ Animator & operator=(Animator &&) = delete;
+
+private:
+ //! A reference to the Sprite sheet containing the animation frames.
+ Sprite & spritesheet;
+
+ //! The maximum number of columns in the sprite sheet.
+ const int col;
+
+ //! The maximum number of rows in the sprite sheet.
+ const int row;
+
+ //! The current col being animated.
+ int curr_col = 0;
+
+ //! The current row being animated.
+ int curr_row = 0;
+
+ Rect animator_rect;
+
+ //TODO: Is this necessary?
+ //int fps;
+
+private:
+ //! AnimatorSystem adjust the private member parameters of Animator;
+ friend class AnimatorSystem;
+
+ //! SDLContext reads the Animator member var's
+ friend class SDLContext;
+};
+} // namespace crepe
+//
diff --git a/src/crepe/api/AssetManager.h b/src/crepe/api/AssetManager.h
index fefbed9..86a9902 100644
--- a/src/crepe/api/AssetManager.h
+++ b/src/crepe/api/AssetManager.h
@@ -7,9 +7,19 @@
namespace crepe {
+/**
+ * \brief The AssetManager is responsible for storing and managing assets over
+ * multiple scenes.
+ *
+ * The AssetManager ensures that assets are loaded once and can be accessed
+ * across different scenes. It caches assets to avoid reloading them every time
+ * a scene is loaded. Assets are retained in memory until the AssetManager is
+ * destroyed, at which point the cached assets are cleared.
+ */
class AssetManager {
private:
+ //! A cache that holds all the assets, accessible by their file path, over multiple scenes.
std::unordered_map<std::string, std::any> asset_cache;
private:
@@ -22,12 +32,32 @@ public:
AssetManager & operator=(const AssetManager &) = delete;
AssetManager & operator=(AssetManager &&) = delete;
+ /**
+ * \brief Retrieves the singleton instance of the AssetManager.
+ *
+ * \return A reference to the single instance of the AssetManager.
+ */
static AssetManager & get_instance();
public:
- template <typename asset>
- std::shared_ptr<asset> cache(const std::string & file_path,
- bool reload = false);
+ /**
+ * \brief Caches an asset by loading it from the given file path.
+ *
+ * \param file_path The path to the asset file to load.
+ * \param reload If true, the asset will be reloaded from the file, even if
+ * it is already cached.
+ * \tparam T The type of asset to cache (e.g., texture, sound, etc.).
+ *
+ * \return A shared pointer to the cached asset.
+ *
+ * This template function caches the asset at the given file path. If the
+ * asset is already cached and `reload` is false, the existing cached version
+ * will be returned. Otherwise, the asset will be reloaded and added to the
+ * cache.
+ */
+ template <typename T>
+ std::shared_ptr<T> cache(const std::string & file_path,
+ bool reload = false);
};
} // namespace crepe
diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h
index fd2b6f1..5bc70f9 100644
--- a/src/crepe/api/AudioSource.h
+++ b/src/crepe/api/AudioSource.h
@@ -11,7 +11,7 @@ namespace crepe {
//! Audio source component
class AudioSource : public Component {
public:
- AudioSource(game_object_id_t id, std::unique_ptr<Asset> audio_clip);
+ AudioSource(game_object_id_t id, const Asset & source);
virtual ~AudioSource() = default;
public:
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index 3b20142..85696c4 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -16,6 +16,10 @@ target_sources(crepe PUBLIC
Scene.cpp
SceneManager.cpp
Vector2.cpp
+ Camera.cpp
+ Animator.cpp
+ LoopManager.cpp
+ LoopTimer.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -38,4 +42,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Metadata.h
SceneManager.h
SceneManager.hpp
+ Camera.h
+ Animator.h
+ LoopManager.h
+ LoopTimer.h
)
diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp
new file mode 100644
index 0000000..6355a03
--- /dev/null
+++ b/src/crepe/api/Camera.cpp
@@ -0,0 +1,18 @@
+
+#include <cstdint>
+
+#include "util/log.h"
+
+#include "Camera.h"
+#include "Color.h"
+#include "Component.h"
+
+using namespace crepe;
+
+Camera::Camera(uint32_t id, const Color & bg_color)
+ : Component(id),
+ bg_color(bg_color) {
+ dbg_trace();
+}
+
+Camera::~Camera() { dbg_trace(); }
diff --git a/src/crepe/api/Camera.h b/src/crepe/api/Camera.h
new file mode 100644
index 0000000..ba3a9ef
--- /dev/null
+++ b/src/crepe/api/Camera.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <cstdint>
+
+#include "Color.h"
+#include "Component.h"
+
+namespace crepe {
+
+/**
+ * \class Camera
+ * \brief Represents a camera component for rendering in the game.
+ *
+ * The Camera class defines the view parameters, including background color,
+ * aspect ratio, position, and zoom level. It controls what part of the game
+ * world is visible on the screen.
+ */
+class Camera : public Component {
+
+public:
+ /**
+ * \brief Constructs a Camera with the specified ID and background color.
+ * \param id Unique identifier for the camera component.
+ * \param bg_color Background color for the camera view.
+ */
+ Camera(uint32_t id, const Color & bg_color);
+ ~Camera(); // dbg_trace only
+
+public:
+ //! Background color of the camera view.
+ Color bg_color;
+
+ //! Aspect ratio height for the camera.
+ double aspect_height = 480;
+
+ //! Aspect ratio width for the camera.
+ double aspect_width = 640;
+
+ //! X-coordinate of the camera position.
+ double x = 0.0;
+
+ //! Y-coordinate of the camera position.
+ double y = 0.0;
+
+ //! Zoom level of the camera view.
+ double zoom = 1.0;
+
+public:
+ /**
+ * \brief Gets the maximum number of camera instances allowed.
+ * \return Maximum instance count as an integer.
+ */
+ virtual int get_instances_max() const { return 10; }
+};
+} // namespace crepe
diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h
index caa7e43..e77a592 100644
--- a/src/crepe/api/CircleCollider.h
+++ b/src/crepe/api/CircleCollider.h
@@ -6,7 +6,8 @@ namespace crepe {
class CircleCollider : public Collider {
public:
CircleCollider(game_object_id_t game_object_id, int radius)
- : Collider(game_object_id), radius(radius) {}
+ : Collider(game_object_id),
+ radius(radius) {}
int radius;
};
diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp
index fc6313d..9e5f187 100644
--- a/src/crepe/api/Color.cpp
+++ b/src/crepe/api/Color.cpp
@@ -11,8 +11,7 @@ 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) {
+Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {
this->a = alpha;
this->r = red;
this->g = green;
diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h
index 6b54888..aa47bf4 100644
--- a/src/crepe/api/Color.h
+++ b/src/crepe/api/Color.h
@@ -1,14 +1,17 @@
#pragma once
+#include <cstdint>
+
namespace crepe {
+// TODO: make Color a struct w/o constructors/destructors
class Color {
// FIXME: can't these colors be defined as a `static constexpr const Color`
// instead?
public:
- Color(double red, double green, double blue, double alpha);
+ Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha);
static const Color & get_white();
static const Color & get_red();
static const Color & get_green();
@@ -19,10 +22,11 @@ public:
static const Color & get_black();
private:
- double r;
- double g;
- double b;
- double a;
+ // TODO: why are these private!?
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
static Color white;
static Color red;
@@ -32,6 +36,9 @@ private:
static Color magenta;
static Color yellow;
static Color black;
+
+private:
+ friend class SDLContext;
};
} // namespace crepe
diff --git a/src/crepe/api/Config.cpp b/src/crepe/api/Config.cpp
index d6206da..0100bcc 100644
--- a/src/crepe/api/Config.cpp
+++ b/src/crepe/api/Config.cpp
@@ -6,4 +6,3 @@ Config & Config::get_instance() {
static Config instance;
return instance;
}
-
diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h
index 56e3af5..8c9e643 100644
--- a/src/crepe/api/Config.h
+++ b/src/crepe/api/Config.h
@@ -7,6 +7,7 @@ namespace crepe {
class Config {
private:
Config() = default;
+
public:
~Config() = default;
@@ -16,8 +17,8 @@ public:
// singleton
Config(const Config &) = delete;
Config(Config &&) = delete;
- Config & operator = (const Config &) = delete;
- Config & operator = (Config &&) = delete;
+ Config & operator=(const Config &) = delete;
+ Config & operator=(Config &&) = delete;
public:
//! Logging-related settings
@@ -60,4 +61,3 @@ public:
};
} // namespace crepe
-
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
new file mode 100644
index 0000000..2e9823f
--- /dev/null
+++ b/src/crepe/api/LoopManager.cpp
@@ -0,0 +1,55 @@
+
+#include "../facade/SDLContext.h"
+#include "../system/RenderSystem.h"
+#include "../system/ScriptSystem.h"
+
+#include "LoopManager.h"
+#include "LoopTimer.h"
+
+using namespace crepe;
+
+LoopManager::LoopManager() {}
+void LoopManager::process_input() {
+ SDLContext::get_instance().handle_events(this->game_running);
+}
+void LoopManager::start() {
+ this->setup();
+ this->loop();
+}
+void LoopManager::set_running(bool running) { this->game_running = running; }
+
+void LoopManager::fixed_update() {}
+
+void LoopManager::loop() {
+ LoopTimer & timer = LoopTimer::get_instance();
+ timer.start();
+
+ while (game_running) {
+ timer.update();
+
+ while (timer.get_lag() >= timer.get_fixed_delta_time()) {
+ this->process_input();
+ this->fixed_update();
+ timer.advance_fixed_update();
+ }
+
+ this->update();
+ this->render();
+
+ timer.enforce_frame_rate();
+ }
+}
+
+void LoopManager::setup() {
+ this->game_running = true;
+ LoopTimer::get_instance().start();
+ LoopTimer::get_instance().set_fps(60);
+}
+
+void LoopManager::render() {
+ if (this->game_running) {
+ RenderSystem::get_instance().update();
+ }
+}
+
+void LoopManager::update() { LoopTimer & timer = LoopTimer::get_instance(); }
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
new file mode 100644
index 0000000..2f03193
--- /dev/null
+++ b/src/crepe/api/LoopManager.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <memory>
+
+class RenderSystem;
+class SDLContext;
+class LoopTimer;
+class ScriptSystem;
+class SoundSystem;
+class ParticleSystem;
+class PhysicsSystem;
+class AnimatorSystem;
+class CollisionSystem;
+namespace crepe {
+
+class LoopManager {
+public:
+ void start();
+ LoopManager();
+
+private:
+ /**
+ * \brief Setup function for one-time initialization.
+ *
+ * This function initializes necessary components for the game.
+ */
+ void setup();
+ /**
+ * \brief Main game loop function.
+ *
+ * This function runs the main loop, handling game updates and rendering.
+ */
+ void loop();
+
+ /**
+ * \brief Function for handling input-related system calls.
+ *
+ * Processes user inputs from keyboard and mouse.
+ */
+ void process_input();
+
+ /**
+ * \brief Per-frame update.
+ *
+ * Updates the game state based on the elapsed time since the last frame.
+ */
+ void update();
+
+ /**
+ * \brief Late update which is called after update().
+ *
+ * This function can be used for final adjustments before rendering.
+ */
+ void late_update();
+
+ /**
+ * \brief Fixed update executed at a fixed rate.
+ *
+ * This function updates physics and game logic based on LoopTimer's fixed_delta_time.
+ */
+ void fixed_update();
+ /**
+ * \brief Set game running variable
+ *
+ * \param running running (false = game shutdown, true = game running)
+ */
+ void set_running(bool running);
+ /**
+ * \brief Function for executing render-related systems.
+ *
+ * Renders the current state of the game to the screen.
+ */
+ void render();
+
+ bool game_running = false;
+ //#TODO add system instances
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp
new file mode 100644
index 0000000..8f09e41
--- /dev/null
+++ b/src/crepe/api/LoopTimer.cpp
@@ -0,0 +1,86 @@
+#include <chrono>
+
+#include "../facade/SDLContext.h"
+#include "../util/log.h"
+
+#include "LoopTimer.h"
+
+using namespace crepe;
+
+LoopTimer::LoopTimer() { dbg_trace(); }
+
+LoopTimer & LoopTimer::get_instance() {
+ static LoopTimer instance;
+ return instance;
+}
+
+void LoopTimer::start() {
+ this->last_frame_time = std::chrono::steady_clock::now();
+ this->elapsed_time = std::chrono::milliseconds(0);
+ this->elapsed_fixed_time = std::chrono::milliseconds(0);
+ this->delta_time = std::chrono::milliseconds(0);
+}
+
+void LoopTimer::update() {
+ auto current_frame_time = std::chrono::steady_clock::now();
+ // Convert to duration in seconds for delta time
+ this->delta_time
+ = std::chrono::duration_cast<std::chrono::duration<double>>(
+ current_frame_time - last_frame_time);
+
+ if (this->delta_time > this->maximum_delta_time) {
+ this->delta_time = this->maximum_delta_time;
+ }
+
+ this->delta_time *= this->game_scale;
+ this->elapsed_time += this->delta_time;
+ this->last_frame_time = current_frame_time;
+}
+
+double LoopTimer::get_delta_time() const { return this->delta_time.count(); }
+
+double LoopTimer::get_current_time() const {
+ return this->elapsed_time.count();
+}
+
+void LoopTimer::advance_fixed_update() {
+ this->elapsed_fixed_time += this->fixed_delta_time;
+}
+
+double LoopTimer::get_fixed_delta_time() const {
+ return this->fixed_delta_time.count();
+}
+
+void LoopTimer::set_fps(int fps) {
+ this->fps = fps;
+ // target time per frame in seconds
+ this->frame_target_time = std::chrono::seconds(1) / fps;
+}
+
+int LoopTimer::get_fps() const { return this->fps; }
+
+void LoopTimer::set_game_scale(double value) { this->game_scale = value; }
+
+double LoopTimer::get_game_scale() const { return this->game_scale; }
+void LoopTimer::enforce_frame_rate() {
+ std::chrono::steady_clock::time_point current_frame_time
+ = std::chrono::steady_clock::now();
+ std::chrono::milliseconds frame_duration
+ = std::chrono::duration_cast<std::chrono::milliseconds>(
+ current_frame_time - this->last_frame_time);
+
+ if (frame_duration < this->frame_target_time) {
+ std::chrono::milliseconds delay_time
+ = std::chrono::duration_cast<std::chrono::milliseconds>(
+ this->frame_target_time - frame_duration);
+ if (delay_time.count() > 0) {
+ SDLContext::get_instance().delay(delay_time.count());
+ }
+ }
+
+ this->last_frame_time = current_frame_time;
+}
+
+double LoopTimer::get_lag() const {
+ return (this->elapsed_time - this->elapsed_fixed_time).count();
+}
diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h
new file mode 100644
index 0000000..85687be
--- /dev/null
+++ b/src/crepe/api/LoopTimer.h
@@ -0,0 +1,147 @@
+#pragma once
+
+#include <chrono>
+#include <cstdint>
+
+namespace crepe {
+
+class LoopTimer {
+public:
+ /**
+ * \brief Get the singleton instance of LoopTimer.
+ *
+ * \return A reference to the LoopTimer instance.
+ */
+ static LoopTimer & get_instance();
+
+ /**
+ * \brief Get the current delta time for the current frame.
+ *
+ * \return Delta time in seconds since the last frame.
+ */
+ double get_delta_time() const;
+
+ /**
+ * \brief Get the current game time.
+ *
+ * \note The current game time may vary from real-world elapsed time.
+ * It is the cumulative sum of each frame's delta time.
+ *
+ * \return Elapsed game time in seconds.
+ */
+ double get_current_time() const;
+
+ /**
+ * \brief Set the target frames per second (FPS).
+ *
+ * \param fps The desired frames rendered per second.
+ */
+ void set_fps(int fps);
+
+ /**
+ * \brief Get the current frames per second (FPS).
+ *
+ * \return Current FPS.
+ */
+ int get_fps() const;
+
+ /**
+ * \brief Get the current game scale.
+ *
+ * \return The current game scale, where 0 = paused, 1 = normal speed, and values > 1 speed up the game.
+ */
+ double get_game_scale() const;
+
+ /**
+ * \brief Set the game scale.
+ *
+ * \param game_scale The desired game scale (0 = pause, 1 = normal speed, > 1 = speed up).
+ */
+ void set_game_scale(double game_scale);
+
+private:
+ friend class LoopManager;
+
+ /**
+ * \brief Start the loop timer.
+ *
+ * Initializes the timer to begin tracking frame times.
+ */
+ void start();
+
+ /**
+ * \brief Enforce the frame rate limit.
+ *
+ * Ensures that the game loop does not exceed the target FPS by delaying
+ * frame updates as necessary.
+ */
+ void enforce_frame_rate();
+
+ /**
+ * \brief Get the fixed delta time for consistent updates.
+ *
+ * Fixed delta time is used for operations that require uniform time steps,
+ * such as physics calculations.
+ *
+ * \return Fixed delta time in seconds.
+ */
+ double get_fixed_delta_time() const;
+
+ /**
+ * \brief Get the accumulated lag in the game loop.
+ *
+ * Lag represents the difference between the target frame time and the
+ * actual frame time, useful for managing fixed update intervals.
+ *
+ * \return Accumulated lag in seconds.
+ */
+ double get_lag() const;
+
+ /**
+ * \brief Construct a new LoopTimer object.
+ *
+ * Private constructor for singleton pattern to restrict instantiation
+ * outside the class.
+ */
+ LoopTimer();
+
+ /**
+ * \brief Update the timer to the current frame.
+ *
+ * Calculates and updates the delta time for the current frame and adds it to
+ * the cumulative game time.
+ */
+ void update();
+
+ /**
+ * \brief Advance the game loop by a fixed update interval.
+ *
+ * This method progresses the game state by a consistent, fixed time step,
+ * allowing for stable updates independent of frame rate fluctuations.
+ */
+ void advance_fixed_update();
+
+private:
+ //! Current frames per second
+ int fps = 50;
+ //! Current game scale
+ double game_scale = 1;
+ //! Maximum delta time in seconds to avoid large jumps
+ std::chrono::duration<double> maximum_delta_time{0.25};
+ //! Delta time for the current frame in seconds
+ std::chrono::duration<double> delta_time{0.0};
+ //! Target time per frame in seconds
+ std::chrono::duration<double> frame_target_time
+ = std::chrono::seconds(1) / fps;
+ //! Fixed delta time for fixed updates in seconds
+ std::chrono::duration<double> fixed_delta_time
+ = std::chrono::seconds(1) / 50;
+ //! Total elapsed game time in seconds
+ std::chrono::duration<double> elapsed_time{0.0};
+ //! Total elapsed time for fixed updates in seconds
+ std::chrono::duration<double> elapsed_fixed_time{0.0};
+ //! Time of the last frame
+ std::chrono::steady_clock::time_point last_frame_time;
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/Metadata.cpp b/src/crepe/api/Metadata.cpp
index 76f11d7..d421de5 100644
--- a/src/crepe/api/Metadata.cpp
+++ b/src/crepe/api/Metadata.cpp
@@ -4,4 +4,6 @@ using namespace crepe;
using namespace std;
Metadata::Metadata(game_object_id_t id, const string & name, const string & tag)
- : Component(id), name(name), tag(tag) {}
+ : Component(id),
+ name(name),
+ tag(tag) {}
diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp
index 3b2e2f2..35f960d 100644
--- a/src/crepe/api/ParticleEmitter.cpp
+++ b/src/crepe/api/ParticleEmitter.cpp
@@ -1,36 +1,12 @@
-#include <ctime>
-#include <iostream>
-
-#include "Particle.h"
#include "ParticleEmitter.h"
using namespace crepe;
-ParticleEmitter::ParticleEmitter(game_object_id_t 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(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);
+ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id,
+ const Data & data)
+ : Component(game_object_id),
+ data(data) {
+ for (size_t i = 0; i < this->data.max_particles; i++) {
+ this->data.particles.emplace_back();
}
}
diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h
index 5939723..a9e872f 100644
--- a/src/crepe/api/ParticleEmitter.h
+++ b/src/crepe/api/ParticleEmitter.h
@@ -1,42 +1,85 @@
#pragma once
-#include <cstdint>
#include <vector>
#include "Component.h"
#include "Particle.h"
+#include "Vector2.h"
namespace crepe {
+class Sprite;
+
+/**
+ * \brief Data holder for particle emission parameters.
+ *
+ * The ParticleEmitter class stores configuration data for particle properties,
+ * defining the characteristics and boundaries of particle emissions.
+ */
class ParticleEmitter : public Component {
public:
- ParticleEmitter(game_object_id_t 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;
+ /**
+ * \brief Defines the boundary within which particles are constrained.
+ *
+ * This structure specifies the boundary's size and offset, as well as the
+ * behavior of particles upon reaching the boundary limits.
+ */
+ struct Boundary {
+ //! boundary width (midpoint is emitter location)
+ double width = 0.0;
+ //! boundary height (midpoint is emitter location)
+ double height = 0.0;
+ //! boundary offset from particle emitter location
+ Vector2 offset;
+ //! reset on exit or stop velocity and set max postion
+ bool reset_on_exit = false;
+ };
+
+ /**
+ * \brief Holds parameters that control particle emission.
+ *
+ * Contains settings for the emitter’s position, particle speed, angle, lifespan,
+ * boundary, and the sprite used for rendering particles.
+ */
+ struct Data {
+ //! position of the emitter
+ Vector2 position;
+ //! maximum number of particles
+ const unsigned int max_particles = 0;
+ //! rate of particle emission per update (Lowest value = 0.001 any lower is ignored)
+ double emission_rate = 0;
+ //! min speed of the particles
+ double min_speed = 0;
+ //! min speed of the particles
+ double max_speed = 0;
+ //! min angle of particle emission
+ double min_angle = 0;
+ //! max angle of particle emission
+ double max_angle = 0;
+ //! begin Lifespan of particle (only visual)
+ double begin_lifespan = 0.0;
+ //! end Lifespan of particle
+ double end_lifespan = 0.0;
+ //! force over time (physics)
+ Vector2 force_over_time;
+ //! particle boundary
+ Boundary boundary;
+ //! collection of particles
+ std::vector<Particle> particles;
+ //! sprite reference
+ const Sprite & sprite;
+ };
+
+public:
+ /**
+ * \param game_object_id Identifier for the game object using this emitter.
+ * \param data Configuration data defining particle properties.
+ */
+ ParticleEmitter(game_object_id_t game_object_id, const Data & data);
+
+public:
+ //! Configuration data for particle emission settings.
+ Data data;
};
} // namespace crepe
diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp
index cbf1325..3bf1c5b 100644
--- a/src/crepe/api/Rigidbody.cpp
+++ b/src/crepe/api/Rigidbody.cpp
@@ -3,7 +3,8 @@
using namespace crepe;
crepe::Rigidbody::Rigidbody(uint32_t game_object_id, const Data & data)
- : Component(game_object_id), data(data) {}
+ : Component(game_object_id),
+ data(data) {}
void crepe::Rigidbody::add_force_linear(const Vector2 & force) {
this->data.linear_velocity += force;
diff --git a/src/crepe/api/SaveManager.cpp b/src/crepe/api/SaveManager.cpp
index 23587e4..43276c5 100644
--- a/src/crepe/api/SaveManager.cpp
+++ b/src/crepe/api/SaveManager.cpp
@@ -2,8 +2,8 @@
#include "../util/log.h"
#include "Config.h"
-#include "ValueBroker.h"
#include "SaveManager.h"
+#include "ValueBroker.h"
using namespace std;
using namespace crepe;
@@ -65,17 +65,33 @@ string SaveManager::deserialize(const string & value) const noexcept {
return value;
}
-template <> uint8_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<uint64_t>(value); }
-template <> int8_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<int64_t>(value); }
-template <> uint16_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<uint64_t>(value); }
-template <> int16_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<int64_t>(value); }
-template <> uint32_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<uint64_t>(value); }
-template <> int32_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<int64_t>(value); }
-
-SaveManager::SaveManager() {
- dbg_trace();
+template <>
+uint8_t SaveManager::deserialize(const string & value) const noexcept {
+ return deserialize<uint64_t>(value);
+}
+template <>
+int8_t SaveManager::deserialize(const string & value) const noexcept {
+ return deserialize<int64_t>(value);
+}
+template <>
+uint16_t SaveManager::deserialize(const string & value) const noexcept {
+ return deserialize<uint64_t>(value);
+}
+template <>
+int16_t SaveManager::deserialize(const string & value) const noexcept {
+ return deserialize<int64_t>(value);
+}
+template <>
+uint32_t SaveManager::deserialize(const string & value) const noexcept {
+ return deserialize<uint64_t>(value);
+}
+template <>
+int32_t SaveManager::deserialize(const string & value) const noexcept {
+ return deserialize<int64_t>(value);
}
+SaveManager::SaveManager() { dbg_trace(); }
+
SaveManager & SaveManager::get_instance() {
dbg_trace();
static SaveManager instance;
@@ -118,17 +134,19 @@ template void SaveManager::set(const string &, const double &);
template <typename T>
ValueBroker<T> SaveManager::get(const string & key, const T & default_value) {
- if (!this->has(key))
- this->set<T>(key, default_value);
+ if (!this->has(key)) this->set<T>(key, default_value);
return this->get<T>(key);
}
template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &);
template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &);
-template ValueBroker<uint16_t> SaveManager::get(const string &, const uint16_t &);
+template ValueBroker<uint16_t> SaveManager::get(const string &,
+ const uint16_t &);
template ValueBroker<int16_t> SaveManager::get(const string &, const int16_t &);
-template ValueBroker<uint32_t> SaveManager::get(const string &, const uint32_t &);
+template ValueBroker<uint32_t> SaveManager::get(const string &,
+ const uint32_t &);
template ValueBroker<int32_t> SaveManager::get(const string &, const int32_t &);
-template ValueBroker<uint64_t> SaveManager::get(const string &, const uint64_t &);
+template ValueBroker<uint64_t> SaveManager::get(const string &,
+ const uint64_t &);
template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &);
template ValueBroker<float> SaveManager::get(const string &, const float &);
template ValueBroker<double> SaveManager::get(const string &, const double &);
@@ -138,8 +156,8 @@ template <typename T>
ValueBroker<T> SaveManager::get(const string & key) {
T value;
return {
- [this, key] (const T & target) { this->set<T>(key, target); },
- [this, key, value] () mutable -> const T & {
+ [this, key](const T & target) { this->set<T>(key, target); },
+ [this, key, value]() mutable -> const T & {
value = this->deserialize<T>(this->get_db().get(key));
return value;
},
@@ -156,4 +174,3 @@ template ValueBroker<int64_t> SaveManager::get(const string &);
template ValueBroker<float> SaveManager::get(const string &);
template ValueBroker<double> SaveManager::get(const string &);
template ValueBroker<string> SaveManager::get(const string &);
-
diff --git a/src/crepe/api/SaveManager.h b/src/crepe/api/SaveManager.h
index 3073656..4be85fb 100644
--- a/src/crepe/api/SaveManager.h
+++ b/src/crepe/api/SaveManager.h
@@ -93,8 +93,8 @@ public:
static SaveManager & get_instance();
SaveManager(const SaveManager &) = delete;
SaveManager(SaveManager &&) = delete;
- SaveManager & operator = (const SaveManager &) = delete;
- SaveManager & operator = (SaveManager &&) = delete;
+ SaveManager & operator=(const SaveManager &) = delete;
+ SaveManager & operator=(SaveManager &&) = delete;
private:
/**
@@ -110,5 +110,4 @@ private:
static DB & get_db();
};
-}
-
+} // namespace crepe
diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp
index d3465c7..6f0433f 100644
--- a/src/crepe/api/Sprite.cpp
+++ b/src/crepe/api/Sprite.cpp
@@ -1,7 +1,7 @@
-#include <cstdint>
#include <memory>
#include "../util/log.h"
+#include "facade/SDLContext.h"
#include "Component.h"
#include "Sprite.h"
@@ -10,10 +10,16 @@
using namespace std;
using namespace crepe;
-Sprite::Sprite(game_object_id_t id, shared_ptr<Texture> image,
+Sprite::Sprite(game_object_id_t id, const shared_ptr<Texture> image,
const Color & color, const FlipSettings & flip)
- : Component(id), color(color), flip(flip), sprite_image(image) {
+ : Component(id),
+ color(color),
+ flip(flip),
+ sprite_image(image) {
dbg_trace();
+
+ this->sprite_rect.w = sprite_image->get_width();
+ this->sprite_rect.h = sprite_image->get_height();
}
Sprite::~Sprite() { dbg_trace(); }
diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h
index 00dcb27..deb3f93 100644
--- a/src/crepe/api/Sprite.h
+++ b/src/crepe/api/Sprite.h
@@ -1,32 +1,88 @@
#pragma once
-#include <SDL2/SDL_rect.h>
#include <cstdint>
#include <memory>
-#include "api/Color.h"
-#include "api/Texture.h"
-
+#include "Color.h"
#include "Component.h"
+#include "Texture.h"
namespace crepe {
+struct Rect {
+ int w = 0;
+ int h = 0;
+ int x = 0;
+ int y = 0;
+};
+
struct FlipSettings {
- bool flip_x = true;
- bool flip_y = true;
+ bool flip_x = false;
+ bool flip_y = false;
};
+class SDLContext;
+class Animator;
+class AnimatorSystem;
+
+/**
+ * \brief Represents a renderable sprite component.
+ *
+ * A renderable sprite that can be displayed in the game. It includes a texture,
+ * color, and flip settings, and is managed in layers with defined sorting orders.
+ */
class Sprite : public Component {
public:
- Sprite(game_object_id_t id, std::shared_ptr<Texture> image,
+ // TODO: Loek comment in github #27 will be looked another time
+ // about shared_ptr Texture
+ /**
+ * \brief Constructs a Sprite with specified parameters.
+ * \param game_id Unique identifier for the game object this sprite belongs to.
+ * \param image Shared pointer to the texture for this sprite.
+ * \param color Color tint applied to the sprite.
+ * \param flip Flip settings for horizontal and vertical orientation.
+ */
+ Sprite(game_object_id_t id, const std::shared_ptr<Texture> image,
const Color & color, const FlipSettings & flip);
+
+ /**
+ * \brief Destroys the Sprite instance.
+ */
~Sprite();
- std::shared_ptr<Texture> sprite_image;
+
+ //! Texture used for the sprite
+ const std::shared_ptr<Texture> sprite_image;
+ //! Color tint of the sprite
Color color;
+ //! Flip settings for the sprite
FlipSettings flip;
- uint8_t sorting_in_layer;
- uint8_t order_in_layer;
+ //! Layer sorting level of the sprite
+ uint8_t sorting_in_layer = 0;
+ //! Order within the sorting layer
+ uint8_t order_in_layer = 0;
+
+public:
+ /**
+ * \brief Gets the maximum number of instances allowed for this sprite.
+ * \return Maximum instance count as an integer.
+ *
+ * For now is this number randomly picked. I think it will eventually be 1.
+ */
+ virtual int get_instances_max() const { return 10; }
+
+private:
+ //! Reads the sprite_rect of sprite
+ friend class SDLContext;
+
+ //! Reads the all the variables plus the sprite_rect
+ friend class Animator;
+
+ //! Reads the all the variables plus the sprite_rect
+ friend class AnimatorSystem;
+
+ //! Render area of the sprite this will also be adjusted by the AnimatorSystem if an Animator object is present in GameObject
+ Rect sprite_rect;
};
} // namespace crepe
diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp
index 8fc5c13..5ebd23d 100644
--- a/src/crepe/api/Texture.cpp
+++ b/src/crepe/api/Texture.cpp
@@ -21,12 +21,19 @@ Texture::Texture(const char * src) {
Texture::~Texture() {
dbg_trace();
- if (this->texture != nullptr) {
- SDL_DestroyTexture(this->texture);
- }
+ this->texture.reset();
}
void Texture::load(unique_ptr<Asset> res) {
SDLContext & ctx = SDLContext::get_instance();
- this->texture = ctx.texture_from_path(res->canonical());
+ this->texture = std::move(ctx.texture_from_path(res->canonical()));
+}
+
+int Texture::get_width() const {
+ if (this->texture == nullptr) return 0;
+ return SDLContext::get_instance().get_width(*this);
+}
+int Texture::get_height() const {
+ if (this->texture == nullptr) return 0;
+ return SDLContext::get_instance().get_width(*this);
}
diff --git a/src/crepe/api/Texture.h b/src/crepe/api/Texture.h
index 9a86f6f..b89bc17 100644
--- a/src/crepe/api/Texture.h
+++ b/src/crepe/api/Texture.h
@@ -3,31 +3,75 @@
// 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 <functional>
#include <memory>
#include "Asset.h"
namespace crepe {
-class SDLContext;
-}
-namespace crepe {
+class SDLContext;
+class Animator;
+/**
+ * \class Texture
+ * \brief Manages texture loading and properties.
+ *
+ * The Texture class is responsible for loading an image from a source
+ * and providing access to its dimensions. Textures can be used for rendering.
+ */
class Texture {
public:
+ /**
+ * \brief Constructs a Texture from a file path.
+ * \param src Path to the image file to be loaded as a texture.
+ */
Texture(const char * src);
+
+ /**
+ * \brief Constructs a Texture from an Asset resource.
+ * \param res Unique pointer to an Asset resource containing texture data.
+ */
Texture(std::unique_ptr<Asset> res);
+
+ /**
+ * \brief Destroys the Texture instance, freeing associated resources.
+ */
~Texture();
+ // FIXME: this constructor shouldn't be necessary because this class doesn't
+ // manage memory
+
+ /**
+ * \brief Gets the width of the texture.
+ * \return Width of the texture in pixels.
+ */
+ int get_width() const;
+
+ /**
+ * \brief Gets the height of the texture.
+ * \return Height of the texture in pixels.
+ */
+ int get_height() const;
private:
+ /**
+ * \brief Loads the texture from an Asset resource.
+ * \param res Unique pointer to an Asset resource to load the texture from.
+ */
void load(std::unique_ptr<Asset> res);
private:
- SDL_Texture * texture = nullptr;
+ //! The texture of the class from the library
+ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> texture;
+
+ //! Grants SDLContext access to private members.
+ friend class SDLContext;
- friend class crepe::SDLContext;
+ //! Grants Animator access to private members.
+ friend class Animator;
};
} // namespace crepe
diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp
index a244bc5..e401120 100644
--- a/src/crepe/api/Transform.cpp
+++ b/src/crepe/api/Transform.cpp
@@ -6,6 +6,9 @@ using namespace crepe;
Transform::Transform(game_object_id_t id, const Vector2 & point,
double rotation, double scale)
- : Component(id), position(point), rotation(rotation), scale(scale) {
+ : Component(id),
+ position(point),
+ rotation(rotation),
+ scale(scale) {
dbg_trace();
}
diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h
index d7a5b8a..756e45b 100644
--- a/src/crepe/api/Transform.h
+++ b/src/crepe/api/Transform.h
@@ -32,7 +32,7 @@ public:
public:
//! Translation (shift)
Vector2 position;
- //! Rotation, in radians
+ //! Rotation, in degrees
double rotation;
//! Multiplication factor
double scale;
diff --git a/src/crepe/api/Vector2.cpp b/src/crepe/api/Vector2.cpp
index 09bb59b..09b3fa3 100644
--- a/src/crepe/api/Vector2.cpp
+++ b/src/crepe/api/Vector2.cpp
@@ -3,7 +3,7 @@
namespace crepe {
// Constructor with initial values
-Vector2::Vector2(float x, float y) : x(x), y(y) {}
+Vector2::Vector2(double x, double y) : x(x), y(y) {}
// Subtracts another vector from this vector and returns the result.
Vector2 Vector2::operator-(const Vector2 & other) const {
@@ -16,7 +16,7 @@ Vector2 Vector2::operator+(const Vector2 & other) const {
}
// Multiplies this vector by a scalar and returns the result.
-Vector2 Vector2::operator*(float scalar) const {
+Vector2 Vector2::operator*(double scalar) const {
return {x * scalar, y * scalar};
}
@@ -35,7 +35,7 @@ Vector2 & Vector2::operator+=(const Vector2 & other) {
}
// Adds a scalar value to both components of this vector and updates this vector.
-Vector2 & Vector2::operator+=(float other) {
+Vector2 & Vector2::operator+=(double other) {
x += other;
y += other;
return *this;
diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h
index 741951b..5a57484 100644
--- a/src/crepe/api/Vector2.h
+++ b/src/crepe/api/Vector2.h
@@ -6,15 +6,15 @@ namespace crepe {
class Vector2 {
public:
//! X component of the vector
- float x;
+ double x;
//! Y component of the vector
- float y;
+ double y;
//! Default constructor
Vector2() = default;
//! Constructor with initial values
- Vector2(float x, float y);
+ Vector2(double x, double y);
//! Subtracts another vector from this vector and returns the result.
Vector2 operator-(const Vector2 & other) const;
@@ -23,7 +23,7 @@ public:
Vector2 operator+(const Vector2 & other) const;
//! Multiplies this vector by a scalar and returns the result.
- Vector2 operator*(float scalar) const;
+ Vector2 operator*(double scalar) const;
//! Multiplies this vector by another vector element-wise and updates this vector.
Vector2 & operator*=(const Vector2 & other);
@@ -32,7 +32,7 @@ public:
Vector2 & operator+=(const Vector2 & other);
//! Adds a scalar value to both components of this vector and updates this vector.
- Vector2 & operator+=(float other);
+ Vector2 & operator+=(double other);
//! Returns the negation of this vector.
Vector2 operator-() const;
diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp
index c885560..0a2f455 100644
--- a/src/crepe/facade/DB.cpp
+++ b/src/crepe/facade/DB.cpp
@@ -1,7 +1,7 @@
#include <cstring>
-#include "util/log.h"
#include "Exception.h"
+#include "util/log.h"
#include "DB.h"
@@ -16,20 +16,21 @@ DB::DB(const string & path) {
libdb::DB * db;
if ((ret = libdb::db_create(&db, NULL, 0)) != 0)
throw Exception("db_create: %s", libdb::db_strerror(ret));
- this->db = { db, [] (libdb::DB * db) { db->close(db, 0); } };
+ this->db = {db, [](libdb::DB * db) { db->close(db, 0); }};
// load or create database file
- if ((ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL, libdb::DB_BTREE, DB_CREATE, 0)) != 0)
+ if ((ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL,
+ libdb::DB_BTREE, DB_CREATE, 0))
+ != 0)
throw Exception("db->open: %s", libdb::db_strerror(ret));
// create cursor
libdb::DBC * cursor;
if ((ret = this->db->cursor(this->db.get(), NULL, &cursor, 0)) != 0)
throw Exception("db->cursor: %s", libdb::db_strerror(ret));
- this->cursor = { cursor, [] (libdb::DBC * cursor) { cursor->close(cursor); } };
+ this->cursor = {cursor, [](libdb::DBC * cursor) { cursor->close(cursor); }};
}
-
libdb::DBT DB::to_thing(const string & thing) const noexcept {
libdb::DBT thang;
memset(&thang, 0, sizeof(libdb::DBT));
@@ -44,17 +45,15 @@ string DB::get(const string & key) {
memset(&db_val, 0, sizeof(libdb::DBT));
int ret = this->cursor->get(this->cursor.get(), &db_key, &db_val, DB_FIRST);
- if (ret != 0)
- throw Exception("cursor->get: %s", libdb::db_strerror(ret));
- return { static_cast<char *>(db_val.data), db_val.size };
+ if (ret != 0) throw Exception("cursor->get: %s", libdb::db_strerror(ret));
+ return {static_cast<char *>(db_val.data), db_val.size};
}
void DB::set(const string & key, const string & value) {
libdb::DBT db_key = this->to_thing(key);
libdb::DBT db_val = this->to_thing(value);
int ret = this->db->put(this->db.get(), NULL, &db_key, &db_val, 0);
- if (ret != 0)
- throw Exception("cursor->get: %s", libdb::db_strerror(ret));
+ if (ret != 0) throw Exception("cursor->get: %s", libdb::db_strerror(ret));
}
bool DB::has(const std::string & key) noexcept {
@@ -65,4 +64,3 @@ bool DB::has(const std::string & key) noexcept {
}
return true;
}
-
diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h
index b62a974..7c757a2 100644
--- a/src/crepe/facade/DB.h
+++ b/src/crepe/facade/DB.h
@@ -1,14 +1,14 @@
#pragma once
-#include <string>
#include <functional>
#include <memory>
+#include <string>
namespace libdb {
extern "C" {
#include <db.h>
}
-}
+} // namespace libdb
namespace crepe {
@@ -71,5 +71,4 @@ private:
libdb::DBT to_thing(const std::string & thing) const noexcept;
};
-}
-
+} // namespace crepe
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 8da93e9..236bf8c 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -1,16 +1,22 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
+#include <SDL2/SDL_rect.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_surface.h>
#include <SDL2/SDL_video.h>
#include <cmath>
#include <cstddef>
+#include <functional>
#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
#include "../api/Sprite.h"
#include "../api/Texture.h"
#include "../api/Transform.h"
#include "../util/log.h"
+#include "Exception.h"
#include "SDLContext.h"
@@ -21,34 +27,6 @@ SDLContext & SDLContext::get_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
@@ -59,26 +37,32 @@ SDLContext::SDLContext() {
<< std::endl;
return;
}
-
- this->game_window = SDL_CreateWindow(
+ SDL_Window * tmp_window = SDL_CreateWindow(
"Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
- 1920, 1080, SDL_WINDOW_SHOWN);
- if (!this->game_window) {
+ this->viewport.w, this->viewport.h, 0);
+ if (!tmp_window) {
// FIXME: throw exception
std::cerr << "Window could not be created! SDL_Error: "
<< SDL_GetError() << std::endl;
+ return;
}
+ this->game_window
+ = {tmp_window, [](SDL_Window * window) { SDL_DestroyWindow(window); }};
- this->game_renderer
- = SDL_CreateRenderer(this->game_window, -1, SDL_RENDERER_ACCELERATED);
- if (!this->game_renderer) {
+ SDL_Renderer * tmp_renderer = SDL_CreateRenderer(
+ this->game_window.get(), -1, SDL_RENDERER_ACCELERATED);
+ if (!tmp_renderer) {
// FIXME: throw exception
std::cerr << "Renderer could not be created! SDL_Error: "
<< SDL_GetError() << std::endl;
- SDL_DestroyWindow(this->game_window);
+ SDL_DestroyWindow(this->game_window.get());
return;
}
+ this->game_renderer = {tmp_renderer, [](SDL_Renderer * renderer) {
+ SDL_DestroyRenderer(renderer);
+ }};
+
int img_flags = IMG_INIT_PNG;
if (!(IMG_Init(img_flags) & img_flags)) {
// FIXME: throw exception
@@ -87,71 +71,124 @@ SDLContext::SDLContext() {
}
}
-void SDLContext::present_screen() { SDL_RenderPresent(this->game_renderer); }
+SDLContext::~SDLContext() {
+ dbg_trace();
-void SDLContext::draw(const Sprite & sprite, const Transform & transform) {
+ this->game_renderer.reset();
+ this->game_window.reset();
- static SDL_RendererFlip render_flip
+ // 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::handle_events(bool & running) {
+ //TODO: wouter i need events
+ /*
+ SDL_Event event;
+ SDL_PollEvent(&event);
+ switch (event.type) {
+ case SDL_QUIT:
+ running = false;
+ break;
+ case SDL_KEYDOWN:
+ triggerEvent(KeyPressedEvent(getCustomKey(event.key.keysym.sym)));
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ int x, y;
+ SDL_GetMouseState(&x, &y);
+ triggerEvent(MousePressedEvent(x, y));
+ break;
+ }
+ */
+}
+
+void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer.get()); }
+void SDLContext::present_screen() {
+ SDL_RenderPresent(this->game_renderer.get());
+}
+
+void SDLContext::draw(const Sprite & sprite, const Transform & transform,
+ const Camera & cam) {
+
+ SDL_RendererFlip render_flip
= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
| (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
- int w, h;
- SDL_QueryTexture(sprite.sprite_image->texture, NULL, NULL, &w, &h);
- // needs maybe camera for position
+ double adjusted_x = (transform.position.x - cam.x) * cam.zoom;
+ double adjusted_y = (transform.position.y - cam.y) * cam.zoom;
+ double adjusted_w = sprite.sprite_rect.w * transform.scale * cam.zoom;
+ double adjusted_h = sprite.sprite_rect.h * transform.scale * cam.zoom;
+
+ SDL_Rect srcrect = {
+ .x = sprite.sprite_rect.x,
+ .y = sprite.sprite_rect.y,
+ .w = sprite.sprite_rect.w,
+ .h = sprite.sprite_rect.h,
+ };
+
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),
+ .x = static_cast<int>(adjusted_x),
+ .y = static_cast<int>(adjusted_y),
+ .w = static_cast<int>(adjusted_w),
+ .h = static_cast<int>(adjusted_h),
};
- double degrees = transform.rotation * 180 / M_PI;
- SDL_RenderCopyEx(this->game_renderer, sprite.sprite_image->texture, NULL,
- &dstrect, degrees, NULL, render_flip);
+ SDL_RenderCopyEx(this->game_renderer.get(),
+ sprite.sprite_image->texture.get(), &srcrect,
+
+ &dstrect, transform.rotation, NULL, render_flip);
}
-/*
-SDL_Texture * SDLContext::setTextureFromPath(const char * path, SDL_Rect & clip,
- const int row, const int col) {
- dbg_trace();
+void SDLContext::camera(const Camera & cam) {
+ this->viewport.w = static_cast<int>(cam.aspect_width);
+ this->viewport.h = static_cast<int>(cam.aspect_height);
+ this->viewport.x = static_cast<int>(cam.x) - (SCREEN_WIDTH / 2);
+ this->viewport.y = static_cast<int>(cam.y) - (SCREEN_HEIGHT / 2);
- SDL_Surface * tmp = IMG_Load(path);
- if (!tmp) {
- std::cerr << "Error surface " << IMG_GetError << std::endl;
- }
+ SDL_SetRenderDrawColor(this->game_renderer.get(), cam.bg_color.r,
+ cam.bg_color.g, cam.bg_color.b, cam.bg_color.a);
+}
- clip.
- w = tmp->w / col;
- clip.h = tmp->h / row;
+uint64_t SDLContext::get_ticks() const { return SDL_GetTicks64(); }
- SDL_Texture * CreatedTexture
- = SDL_CreateTextureFromSurface(this->game_renderer, tmp);
+std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>
+SDLContext::texture_from_path(const std::string & path) {
- if (!CreatedTexture) {
- std::cerr << "Error could not create texture " << IMG_GetError
- << std::endl;
+ SDL_Surface * tmp = IMG_Load(path.c_str());
+ if (tmp == nullptr) {
+ tmp = IMG_Load("../asset/texture/ERROR.png");
}
- SDL_FreeSurface(tmp);
- return CreatedTexture;
-}
-*/
+ std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>>
+ img_surface;
+ img_surface
+ = {tmp, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }};
-SDL_Texture * SDLContext::texture_from_path(const char * path) {
- dbg_trace();
+ SDL_Texture * tmp_texture = SDL_CreateTextureFromSurface(
+ this->game_renderer.get(), img_surface.get());
- SDL_Surface * tmp = IMG_Load(path);
- if (!tmp) {
- std::cerr << "Error surface " << IMG_GetError << std::endl;
+ if (tmp_texture == nullptr) {
+ throw Exception("Texture cannot be load from %s", path.c_str());
}
- 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);
+ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>
+ img_texture;
+ img_texture = {tmp_texture,
+ [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }};
- return created_texture;
+ return img_texture;
+}
+int SDLContext::get_width(const Texture & ctx) const {
+ int w;
+ SDL_QueryTexture(ctx.texture.get(), NULL, NULL, &w, NULL);
+ return w;
+}
+int SDLContext::get_height(const Texture & ctx) const {
+ int h;
+ SDL_QueryTexture(ctx.texture.get(), NULL, NULL, NULL, &h);
+ return h;
}
+void SDLContext::delay(int ms) const { SDL_Delay(ms); }
diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h
index f1ba8a6..536dec5 100644
--- a/src/crepe/facade/SDLContext.h
+++ b/src/crepe/facade/SDLContext.h
@@ -1,48 +1,157 @@
#pragma once
+#include <SDL2/SDL_keycode.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_video.h>
+#include <functional>
+#include <memory>
+#include <string>
#include "../api/Sprite.h"
#include "../api/Transform.h"
-#include "../system/RenderSystem.h"
+#include "api/Camera.h"
+
+// FIXME: this needs to be removed
+const int SCREEN_WIDTH = 640;
+const int SCREEN_HEIGHT = 480;
namespace crepe {
+// TODO: SDL_Keycode is defined in a header not distributed with crepe, which
+// means this typedef is unusable when crepe is packaged. Wouter will fix this
+// later.
+typedef SDL_Keycode CREPE_KEYCODES;
+
class Texture;
+class LoopManager;
+
+/**
+ * \class SDLContext
+ * \brief Facade for the SDL library
+ *
+ * SDLContext is a singleton that handles the SDL window and renderer, provides methods
+ * for event handling, and rendering to the screen. It is never used directly by the user
+ */
class SDLContext {
public:
- // singleton
+ /**
+ * \brief Gets the singleton instance of SDLContext.
+ * \return Reference to the SDLContext instance.
+ */
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:
+ //! will only use handle_events
+ friend class LoopManager;
+ /**
+ * \brief Handles SDL events such as window close and input.
+ * \param running Reference to a boolean flag that controls the main loop.
+ */
void handle_events(bool & running);
private:
+ //! Will only use get_ticks
+ friend class AnimatorSystem;
+ //! Will only use delay
+ friend class LoopTimer;
+ /**
+ * \brief Gets the current SDL ticks since the program started.
+ * \return Current ticks in milliseconds as a constant uint64_t.
+ */
+ uint64_t get_ticks() const;
+ /**
+ * \brief Pauses the execution for a specified duration.
+ *
+ * This function uses SDL's delay function to halt the program execution
+ * for a given number of milliseconds, allowing for frame rate control
+ * or other timing-related functionality.
+ *
+ * \param ms Duration of the delay in milliseconds.
+ */
+ void delay(int ms) const;
+
+private:
+ /**
+ * \brief Constructs an SDLContext instance.
+ * Initializes SDL, creates a window and renderer.
+ */
SDLContext();
- virtual ~SDLContext();
+
+ /**
+ * \brief Destroys the SDLContext instance.
+ * Cleans up SDL resources, including the window and renderer.
+ */
+ ~SDLContext();
private:
+ //! Will use the funtions: texture_from_path, get_width,get_height.
friend class Texture;
- SDL_Texture * texture_from_path(const char *);
- //SDL_Texture* setTextureFromPath(const char*, SDL_Rect& clip, const int row, const int col);
+
+ //! Will use the funtions: texture_from_path, get_width,get_height.
+ friend class Animator;
+
+ /**
+ * \brief Loads a texture from a file path.
+ * \param path Path to the image file.
+ * \return Pointer to the created SDL_Texture.
+ */
+ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>
+ texture_from_path(const std::string & path);
+ /**
+ * \brief Gets the width of a texture.
+ * \param texture Reference to the Texture object.
+ * \return Width of the texture as an integer.
+ */
+ int get_width(const Texture &) const;
+
+ /**
+ * \brief Gets the height of a texture.
+ * \param texture Reference to the Texture object.
+ * \return Height of the texture as an integer.
+ */
+ int get_height(const Texture &) const;
private:
+ //! Will use draw,clear_screen, present_screen, camera.
friend class RenderSystem;
- void draw(const Sprite &, const Transform &);
+
+ /**
+ * \brief Draws a sprite to the screen using the specified transform and camera.
+ * \param sprite Reference to the Sprite to draw.
+ * \param transform Reference to the Transform for positioning.
+ * \param camera Reference to the Camera for view adjustments.
+ */
+ void draw(const Sprite & sprite, const Transform & transform,
+ const Camera & camera);
+
+ //! Clears the screen, preparing for a new frame.
void clear_screen();
+
+ //! Presents the rendered frame to the screen.
void present_screen();
+ /**
+ * \brief Sets the current camera for rendering.
+ * \param camera Reference to the Camera object.
+ */
+ void camera(const Camera & camera);
+
private:
- SDL_Window * game_window = nullptr;
- SDL_Renderer * game_renderer = nullptr;
+ //! sdl Window
+ std::unique_ptr<SDL_Window, std::function<void(SDL_Window *)>> game_window;
+
+ //! renderer for the crepe engine
+ std::unique_ptr<SDL_Renderer, std::function<void(SDL_Renderer *)>>
+ game_renderer;
+
+ //! viewport for the camera window
+ SDL_Rect viewport = {0, 0, 640, 480};
};
} // namespace crepe
diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp
new file mode 100644
index 0000000..bf45362
--- /dev/null
+++ b/src/crepe/system/AnimatorSystem.cpp
@@ -0,0 +1,37 @@
+
+#include <cstdint>
+#include <functional>
+#include <vector>
+
+#include "api/Animator.h"
+#include "facade/SDLContext.h"
+#include "util/log.h"
+
+#include "AnimatorSystem.h"
+#include "ComponentManager.h"
+
+using namespace crepe;
+
+AnimatorSystem::AnimatorSystem() { dbg_trace(); }
+AnimatorSystem::~AnimatorSystem() { dbg_trace(); }
+
+AnimatorSystem & AnimatorSystem::get_instance() {
+ static AnimatorSystem instance;
+ return instance;
+}
+
+void AnimatorSystem::update() {
+ ComponentManager & mgr = ComponentManager::get_instance();
+
+ std::vector<std::reference_wrapper<Animator>> animations
+ = mgr.get_components_by_type<Animator>();
+
+ uint64_t tick = SDLContext::get_instance().get_ticks();
+ for (Animator & a : animations) {
+ if (a.active) {
+ a.curr_row = (tick / 100) % a.row;
+ a.animator_rect.x = (a.curr_row * a.animator_rect.w) + a.curr_col;
+ a.spritesheet.sprite_rect = a.animator_rect;
+ }
+ }
+}
diff --git a/src/crepe/system/AnimatorSystem.h b/src/crepe/system/AnimatorSystem.h
new file mode 100644
index 0000000..969e9d1
--- /dev/null
+++ b/src/crepe/system/AnimatorSystem.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "System.h"
+
+//TODO:
+// control if flip works with animation system
+
+namespace crepe {
+
+/**
+ * \brief The AnimatorSystem is responsible for managing and updating all Animator components.
+ *
+ * This system is responsible for controlling the behavior of the animations for all entities
+ * that have the Animator component attached. It updates the animations by controlling their
+ * frame changes, looping behavior, and overall animation state.
+ */
+class AnimatorSystem : public System {
+
+public:
+ /**
+ * \brief Retrieves the singleton instance of the AnimatorSystem.
+ *
+ * \return A reference to the single instance of the AnimatorSystem.
+ *
+ * This method ensures that there is only one instance of the AnimatorSystem, following the
+ * singleton design pattern. It can be used to access the system globally.
+ */
+ static AnimatorSystem & get_instance();
+
+ /**
+ * \brief Updates the Animator components.
+ *
+ * This method is called periodically (likely every frame) to update the state of all
+ * Animator components, moving the animations forward and managing their behavior (e.g., looping).
+ */
+ void update() override;
+
+private:
+ // private because singleton
+ AnimatorSystem(); // dbg_trace
+ ~AnimatorSystem(); // dbg_trace
+};
+
+} // namespace crepe
diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp
index 93c0955..9fdd7eb 100644
--- a/src/crepe/system/AudioSystem.cpp
+++ b/src/crepe/system/AudioSystem.cpp
@@ -1,5 +1,3 @@
-#pragma once
-
#include "AudioSystem.h"
#include "ComponentManager.h"
diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h
index 8fb681f..e037f51 100644
--- a/src/crepe/system/AudioSystem.h
+++ b/src/crepe/system/AudioSystem.h
@@ -8,13 +8,11 @@ namespace crepe {
class AudioSystem : public System {
public:
- AudioSystem(SoundContext & ctx);
-
-public:
- void update();
+ using System::System;
+ void update() override;
private:
- SoundContext & ctx;
+ SoundContext context {};
};
} // namespace crepe
diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt
index ca62add..f507b90 100644
--- a/src/crepe/system/CMakeLists.txt
+++ b/src/crepe/system/CMakeLists.txt
@@ -6,6 +6,7 @@ target_sources(crepe PUBLIC
CollisionSystem.cpp
RenderSystem.cpp
AudioSystem.cpp
+ AnimatorSystem.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -15,4 +16,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
CollisionSystem.h
RenderSystem.h
AudioSystem.h
+ AnimatorSystem.h
)
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 397b586..e7a3bec 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -1,62 +1,141 @@
#include <cmath>
+#include <cstdlib>
#include <ctime>
-#include "../ComponentManager.h"
-#include "../api/ParticleEmitter.h"
+#include "api/ParticleEmitter.h"
+#include "api/Transform.h"
+#include "api/Vector2.h"
+#include "ComponentManager.h"
#include "ParticleSystem.h"
using namespace crepe;
-ParticleSystem::ParticleSystem() : elapsed_time(0.0f) {}
-
void ParticleSystem::update() {
+ // Get all emitters
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);
+ // Get transform linked to emitter
+ const Transform & transform
+ = mgr.get_components_by_id<Transform>(emitter.game_object_id)
+ .front()
+ .get();
+
+ // Emit particles based on emission_rate
+ int updates
+ = calculate_update(this->update_count, emitter.data.emission_rate);
+ for (size_t i = 0; i < updates; i++) {
+ emit_particle(emitter, transform);
}
- for (size_t j = 0; j < emitter.particles.size(); j++) {
- if (emitter.particles[j].active) {
- emitter.particles[j].update(delta_time);
+
+ // Update all particles
+ for (Particle & particle : emitter.data.particles) {
+ if (particle.active) {
+ particle.update();
}
}
+
+ // Check if within boundary
+ check_bounds(emitter, transform);
}
+
+ this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT;
}
-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);
+void ParticleSystem::emit_particle(ParticleEmitter & emitter,
+ const Transform & transform) {
+ constexpr double DEG_TO_RAD = M_PI / 180.0;
+
+ Vector2 initial_position = emitter.data.position + transform.position;
+ double random_angle
+ = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle);
+
+ double random_speed
+ = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed);
+ double angle_radians = random_angle * DEG_TO_RAD;
+
+ Vector2 velocity = {random_speed * std::cos(angle_radians),
+ random_speed * std::sin(angle_radians)};
+
+ for (Particle & particle : emitter.data.particles) {
+ if (!particle.active) {
+ particle.reset(emitter.data.end_lifespan, initial_position,
+ velocity, random_angle);
break;
}
}
}
+
+int ParticleSystem::calculate_update(int count, double emission) const {
+ double integer_part = std::floor(emission);
+ double fractional_part = emission - integer_part;
+
+ if (fractional_part > 0) {
+ int denominator = static_cast<int>(1.0 / fractional_part);
+ return (count % denominator == 0) ? 1 : 0;
+ }
+
+ return static_cast<int>(emission);
+}
+
+void ParticleSystem::check_bounds(ParticleEmitter & emitter,
+ const Transform & transform) {
+ Vector2 offset = emitter.data.boundary.offset + transform.position
+ + emitter.data.position;
+ double half_width = emitter.data.boundary.width / 2.0;
+ double half_height = emitter.data.boundary.height / 2.0;
+
+ const double LEFT = offset.x - half_width;
+ const double RIGHT = offset.x + half_width;
+ const double TOP = offset.y - half_height;
+ const double BOTTOM = offset.y + half_height;
+
+ for (Particle & particle : emitter.data.particles) {
+ const Vector2 & position = particle.position;
+ bool within_bounds = (position.x >= LEFT && position.x <= RIGHT
+ && position.y >= TOP && position.y <= BOTTOM);
+
+ if (!within_bounds) {
+ if (emitter.data.boundary.reset_on_exit) {
+ particle.active = false;
+ } else {
+ particle.velocity = {0, 0};
+ if (position.x < LEFT) particle.position.x = LEFT;
+ else if (position.x > RIGHT) particle.position.x = RIGHT;
+ if (position.y < TOP) particle.position.y = TOP;
+ else if (position.y > BOTTOM) particle.position.y = BOTTOM;
+ }
+ }
+ }
+}
+
+double ParticleSystem::generate_random_angle(double min_angle,
+ double max_angle) const {
+ if (min_angle == max_angle) {
+ return min_angle;
+ } else if (min_angle < max_angle) {
+ return min_angle
+ + static_cast<double>(std::rand()
+ % static_cast<int>(max_angle - min_angle));
+ } else {
+ double angle_offset = (360 - min_angle) + max_angle;
+ double random_angle = min_angle
+ + static_cast<double>(
+ std::rand() % static_cast<int>(angle_offset));
+ return (random_angle >= 360) ? random_angle - 360 : random_angle;
+ }
+}
+
+double ParticleSystem::generate_random_speed(double min_speed,
+ double max_speed) const {
+ if (min_speed == max_speed) {
+ return min_speed;
+ } else {
+ return min_speed
+ + static_cast<double>(std::rand()
+ % static_cast<int>(max_speed - min_speed));
+ }
+}
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index 3ac1d3f..d7ca148 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -1,18 +1,71 @@
#pragma once
-#include "../api/ParticleEmitter.h"
+#include <cstdint>
-namespace crepe {
+#include "System.h"
-class ParticleSystem {
+namespace crepe {
+class ParticleEmitter;
+class Transform;
+/**
+ * \brief ParticleSystem class responsible for managing particle emission, updates, and bounds checking.
+ */
+class ParticleSystem : public System {
public:
- ParticleSystem();
- void update();
+ /**
+ * \brief Updates all particle emitters by emitting particles, updating particle states, and checking bounds.
+ */
+ void update() override;
private:
- void emit_particle(ParticleEmitter & emitter); //emits a new particle
+ /**
+ * \brief Emits a particle from the specified emitter based on its emission properties.
+ *
+ * \param emitter Reference to the ParticleEmitter.
+ * \param transform Const reference to the Transform component associated with the emitter.
+ */
+ void emit_particle(ParticleEmitter & emitter, const Transform & transform);
+
+ /**
+ * \brief Calculates the number of times particles should be emitted based on emission rate and update count.
+ *
+ * \param count Current update count.
+ * \param emission Emission rate.
+ * \return The number of particles to emit.
+ */
+ int calculate_update(int count, double emission) const;
+
+ /**
+ * \brief Checks whether particles are within the emitter’s boundary, resets or stops particles if they exit.
+ *
+ * \param emitter Reference to the ParticleEmitter.
+ * \param transform Const reference to the Transform component associated with the emitter.
+ */
+ void check_bounds(ParticleEmitter & emitter, const Transform & transform);
- float elapsed_time; //elapsed time since the last emission
+ /**
+ * \brief Generates a random angle for particle emission within the specified range.
+ *
+ * \param min_angle Minimum emission angle in degrees.
+ * \param max_angle Maximum emission angle in degrees.
+ * \return Random angle in degrees.
+ */
+ double generate_random_angle(double min_angle, double max_angle) const;
+
+ /**
+ * \brief Generates a random speed for particle emission within the specified range.
+ *
+ * \param min_speed Minimum emission speed.
+ * \param max_speed Maximum emission speed.
+ * \return Random speed.
+ */
+ double generate_random_speed(double min_speed, double max_speed) const;
+
+private:
+ //! Counter to count updates to determine how many times emit_particle is called.
+ unsigned int update_count = 0;
+ //! Determines the lowest amount of emission rate (1000 = 0.001 = 1 particle per 1000 updates).
+ static constexpr unsigned int MAX_UPDATE_COUNT = 100;
};
} // namespace crepe
diff --git a/src/crepe/system/PhysicsSystem.h b/src/crepe/system/PhysicsSystem.h
index cc13b70..038c120 100644
--- a/src/crepe/system/PhysicsSystem.h
+++ b/src/crepe/system/PhysicsSystem.h
@@ -1,5 +1,7 @@
#pragma once
+#include "System.h"
+
namespace crepe {
/**
* \brief System that controls all physics
@@ -7,18 +9,14 @@ namespace crepe {
* This class is a physics system that uses a rigidbody and transform
* to add physics to a game object.
*/
-class PhysicsSystem {
+class PhysicsSystem : public System {
public:
/**
- * Constructor is default
- */
- PhysicsSystem() = default;
- /**
* \brief updates the physics system.
*
* It calculates new velocties and changes the postion in the transform.
*/
- void update();
+ void update() override;
};
} // namespace crepe
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index 5a07cc2..10211a3 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -20,7 +20,25 @@ RenderSystem & RenderSystem::get_instance() {
return instance;
}
-void RenderSystem::update() {
+void RenderSystem::clear_screen() const {
+ SDLContext::get_instance().clear_screen();
+}
+
+void RenderSystem::present_screen() const {
+ SDLContext::get_instance().present_screen();
+}
+void RenderSystem::update_camera() {
+ ComponentManager & mgr = ComponentManager::get_instance();
+
+ std::vector<std::reference_wrapper<Camera>> cameras
+ = mgr.get_components_by_type<Camera>();
+
+ for (Camera & cam : cameras) {
+ SDLContext::get_instance().camera(cam);
+ this->curr_cam = &cam;
+ }
+}
+void RenderSystem::render_sprites() const {
ComponentManager & mgr = ComponentManager::get_instance();
@@ -28,14 +46,16 @@ void RenderSystem::update() {
= 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
+ auto transforms
= mgr.get_components_by_id<Transform>(sprite.game_object_id);
- for (const Transform & transform : transforms) {
- render.draw(sprite, transform);
- }
+ render.draw(sprite, transforms[0], *curr_cam);
}
- render.present_screen();
+}
+
+void RenderSystem::update() {
+ this->clear_screen();
+ this->update_camera();
+ this->render_sprites();
+ this->present_screen();
}
diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h
index 4b910a4..70db21a 100644
--- a/src/crepe/system/RenderSystem.h
+++ b/src/crepe/system/RenderSystem.h
@@ -1,17 +1,64 @@
#pragma once
+#include "api/Camera.h"
+
#include "System.h"
namespace crepe {
+/**
+ * \class RenderSystem
+ * \brief Manages rendering operations for all game objects.
+ *
+ * RenderSystem is responsible for rendering sprites, clearing and presenting the screen,
+ * and managing the active camera. It functions as a singleton, providing centralized
+ * rendering services for the application.
+ */
class RenderSystem : public System {
public:
+ /**
+ * \brief Gets the singleton instance of RenderSystem.
+ * \return Reference to the RenderSystem instance.
+ */
static RenderSystem & get_instance();
- void update();
+
+ /**
+ * \brief Updates the RenderSystem for the current frame.
+ * This method is called to perform all rendering operations for the current game frame.
+ */
+ void update() override;
private:
+ // Private constructor to enforce singleton pattern.
RenderSystem();
~RenderSystem();
+
+ //! Clears the screen in preparation for rendering.
+ void clear_screen() const;
+
+ //! Presents the rendered frame to the display.
+ void present_screen() const;
+
+ //! Updates the active camera used for rendering.
+ void update_camera();
+
+ //! Renders all active sprites to the screen.
+ void render_sprites() const;
+
+ /**
+ * \todo Include color handling for sprites.
+ * \todo Implement particle emitter rendering with sprites.
+ * \todo Add text rendering using SDL_ttf for text components.
+ * \todo Implement a text component and a button component.
+ * \todo Ensure each sprite is checked for active status before rendering.
+ * \todo Sort all layers by order before rendering.
+ * \todo Consider adding text input functionality.
+ */
+
+private:
+ //! Pointer to the current active camera for rendering
+ Camera * curr_cam = nullptr;
+ // TODO: needs a better solution
};
} // namespace crepe
diff --git a/src/crepe/util/Proxy.h b/src/crepe/util/Proxy.h
index fbfed0c..f84e462 100644
--- a/src/crepe/util/Proxy.h
+++ b/src/crepe/util/Proxy.h
@@ -16,9 +16,9 @@ template <typename T>
class Proxy {
public:
//! Set operator
- Proxy & operator = (const T &);
+ Proxy & operator=(const T &);
//! Get operator
- operator const T & ();
+ operator const T &();
public:
Proxy(ValueBroker<T>);
@@ -27,7 +27,6 @@ private:
ValueBroker<T> broker;
};
-}
+} // namespace crepe
#include "Proxy.hpp"
-
diff --git a/src/crepe/util/Proxy.hpp b/src/crepe/util/Proxy.hpp
index 4aec9e9..b9923db 100644
--- a/src/crepe/util/Proxy.hpp
+++ b/src/crepe/util/Proxy.hpp
@@ -5,18 +5,17 @@
namespace crepe {
template <typename T>
-Proxy<T>::Proxy(ValueBroker<T> broker) : broker(broker) { }
+Proxy<T>::Proxy(ValueBroker<T> broker) : broker(broker) {}
template <typename T>
-Proxy<T> & Proxy<T>::operator = (const T & val) {
+Proxy<T> & Proxy<T>::operator=(const T & val) {
this->broker.set(val);
return *this;
}
template <typename T>
-Proxy<T>::operator const T & () {
+Proxy<T>::operator const T &() {
return this->broker.get();
}
-}
-
+} // namespace crepe
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index 36f9d4d..3a5b543 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -28,4 +28,6 @@ add_example(proxy)
add_example(db)
add_example(ecs)
add_example(scene_manager)
+add_example(particles)
+add_example(gameloop)
diff --git a/src/example/db.cpp b/src/example/db.cpp
index c046421..8c06a84 100644
--- a/src/example/db.cpp
+++ b/src/example/db.cpp
@@ -1,12 +1,12 @@
-#include <crepe/facade/DB.h>
#include <crepe/api/Config.h>
+#include <crepe/facade/DB.h>
#include <crepe/util/log.h>
using namespace crepe;
using namespace std;
// run before main
-static auto _ = [] () {
+static auto _ = []() {
auto & cfg = Config::get_instance();
cfg.log.level = LogLevel::TRACE;
return 0;
diff --git a/src/example/gameloop.cpp b/src/example/gameloop.cpp
new file mode 100644
index 0000000..a676f20
--- /dev/null
+++ b/src/example/gameloop.cpp
@@ -0,0 +1,7 @@
+#include "crepe/api/LoopManager.h"
+using namespace crepe;
+int main() {
+ LoopManager gameloop;
+ gameloop.start();
+ return 1;
+}
diff --git a/src/example/particles.cpp b/src/example/particles.cpp
new file mode 100644
index 0000000..6eab046
--- /dev/null
+++ b/src/example/particles.cpp
@@ -0,0 +1,43 @@
+#include <crepe/ComponentManager.h>
+#include <crepe/api/AssetManager.h>
+
+#include <crepe/Component.h>
+#include <crepe/api/Color.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Texture.h>
+#include <crepe/api/Transform.h>
+
+using namespace crepe;
+using namespace std;
+
+int main(int argc, char * argv[]) {
+ GameObject game_object(0, "", "", Vector2{0, 0}, 0, 0);
+ Color color(0, 0, 0, 0);
+ Sprite test_sprite = game_object.add_component<Sprite>(
+ make_shared<Texture>("../asset/texture/img.png"), color,
+ FlipSettings{true, true});
+ game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{
+ .position = {0, 0},
+ .max_particles = 100,
+ .emission_rate = 0,
+ .min_speed = 0,
+ .max_speed = 0,
+ .min_angle = 0,
+ .max_angle = 0,
+ .begin_lifespan = 0,
+ .end_lifespan = 0,
+ .force_over_time = Vector2{0, 0},
+ .boundary{
+ .width = 0,
+ .height = 0,
+ .offset = Vector2{0, 0},
+ .reset_on_exit = false,
+ },
+ .sprite = test_sprite,
+ });
+
+ return 0;
+}
diff --git a/src/example/proxy.cpp b/src/example/proxy.cpp
index 9f54f96..0afff41 100644
--- a/src/example/proxy.cpp
+++ b/src/example/proxy.cpp
@@ -5,15 +5,15 @@
#include <crepe/ValueBroker.h>
#include <crepe/api/Config.h>
-#include <crepe/util/log.h>
#include <crepe/util/Proxy.h>
+#include <crepe/util/log.h>
using namespace std;
using namespace crepe;
-void test_ro_ref(const int & val) { }
-void test_rw_ref(int & val) { }
-void test_ro_val(int val) { }
+void test_ro_ref(const int & val) {}
+void test_rw_ref(int & val) {}
+void test_ro_val(int val) {}
int main() {
auto & cfg = Config::get_instance();
@@ -21,18 +21,19 @@ int main() {
int real_value = 0;
- ValueBroker<int> broker {
- [&real_value] (const int & target) {
- dbg_logf("set %s to %s", to_string(real_value).c_str(), to_string(target).c_str());
+ ValueBroker<int> broker{
+ [&real_value](const int & target) {
+ dbg_logf("set %s to %s", to_string(real_value).c_str(),
+ to_string(target).c_str());
real_value = target;
},
- [&real_value] () -> const int & {
+ [&real_value]() -> const int & {
dbg_logf("get %s", to_string(real_value).c_str());
return real_value;
},
};
- Proxy<int> proxy { broker };
+ Proxy<int> proxy{broker};
broker.set(54);
proxy = 84;
@@ -43,4 +44,3 @@ int main() {
return 0;
}
-
diff --git a/src/example/rendering.cpp b/src/example/rendering.cpp
index d554a8a..827ad07 100644
--- a/src/example/rendering.cpp
+++ b/src/example/rendering.cpp
@@ -1,3 +1,4 @@
+#include "api/Camera.h"
#include <crepe/ComponentManager.h>
#include <crepe/api/GameObject.h>
#include <crepe/system/RenderSystem.h>
@@ -23,26 +24,28 @@ int main() {
auto obj1 = GameObject(1, "name", "tag", Vector2{500, 0}, 1, 0.1);
auto obj2 = GameObject(2, "name", "tag", Vector2{800, 0}, 1, 0.1);
- auto & mgr = AssetManager::get_instance();
// Normal adding components
{
Color color(0, 0, 0, 0);
obj.add_component<Sprite>(
make_shared<Texture>("../asset/texture/img.png"), color,
- FlipSettings{true, true});
+ FlipSettings{false, false});
+ obj.add_component<Camera>(Color::get_red());
}
-
{
Color color(0, 0, 0, 0);
- auto img = mgr.cache<Texture>("../asset/texture/second.png");
- obj1.add_component<Sprite>(img, color, FlipSettings{true, true});
+ obj1.add_component<Sprite>(
+ make_shared<Texture>("../asset/texture/second.png"), color,
+ FlipSettings{true, true});
}
+ /*
{
Color color(0, 0, 0, 0);
auto img = mgr.cache<Texture>("../asset/texture/second.png");
obj2.add_component<Sprite>(img, color, FlipSettings{true, true});
}
+ */
auto & sys = crepe::RenderSystem::get_instance();
auto start = std::chrono::steady_clock::now();
diff --git a/src/example/savemgr.cpp b/src/example/savemgr.cpp
index c8dd2bc..436fb5a 100644
--- a/src/example/savemgr.cpp
+++ b/src/example/savemgr.cpp
@@ -4,21 +4,21 @@
*/
#include <cassert>
-#include <crepe/util/log.h>
-#include <crepe/util/Proxy.h>
-#include <crepe/api/SaveManager.h>
#include <crepe/api/Config.h>
+#include <crepe/api/SaveManager.h>
+#include <crepe/util/Proxy.h>
+#include <crepe/util/log.h>
using namespace crepe;
// unrelated setup code
-int _ = [] () {
+int _ = []() {
// make sure all log messages get printed
auto & cfg = Config::get_instance();
cfg.log.level = LogLevel::TRACE;
return 0; // satisfy compiler
-} ();
+}();
int main() {
const char * key = "mygame.test";
@@ -27,7 +27,7 @@ int main() {
dbg_logf("has key = %s", mgr.has(key) ? "true" : "false");
ValueBroker<int> prop = mgr.get<int>(key, 0);
- Proxy<int> val = mgr.get<int>(key, 0);
+ Proxy<int> val = mgr.get<int>(key, 0);
dbg_logf("val = %d", mgr.get<int>(key).get());
prop.set(1);
@@ -42,4 +42,3 @@ int main() {
return 0;
}
-
diff --git a/src/makefile b/src/makefile
index b9c44c8..5f80204 100644
--- a/src/makefile
+++ b/src/makefile
@@ -1,104 +1,6 @@
.PHONY: FORCE
-# STEPS FOR BIG CLEANUP
-#
-# 1. Change TODO to your name (in capitals) for each file in the list below
-# that is yours (or you are going to fix)
-# 2. Update the name between parentheses below this list (see comment) to your
-# name
-# 3. Create a git commit at this point (ensure `git status` reports "working
-# tree clean")
-# 4. Run `make format` in the REPOSITORY ROOT DIRECTORY (NOT HERE), and start
-# fixing reported errors or miscorrections manually until everything works
-# again.
-# 5. Once everything is working again, create another git commit, and create a
-# pull request. Make sure to ask someone to review the code standards for
-# each ENTIRE FILE in this pull request.
-
-LOEK += crepe/Asset.cpp
-LOEK += crepe/Asset.h
-TODO += crepe/Collider.cpp
-TODO += crepe/Collider.h
-MAX += crepe/Component.cpp
-MAX += crepe/Component.h
-MAX += crepe/ComponentManager.cpp
-MAX += crepe/ComponentManager.h
-MAX += crepe/ComponentManager.hpp
-MAX += crepe/api/Metadata.cpp
-MAX += crepe/api/Metadata.h
-TODO += crepe/Particle.cpp
-TODO += crepe/Particle.h
-TODO += crepe/Position.h
-TODO += crepe/api/AssetManager.cpp
-TODO += crepe/api/AssetManager.h
-TODO += crepe/api/AssetManager.hpp
-LOEK += crepe/api/AudioSource.cpp
-LOEK += crepe/api/AudioSource.h
-LOEK += crepe/api/BehaviorScript.cpp
-LOEK += crepe/api/BehaviorScript.h
-LOEK += crepe/api/BehaviorScript.hpp
-TODO += crepe/api/CircleCollider.h
-TODO += crepe/api/Color.cpp
-TODO += crepe/api/Color.h
-LOEK += crepe/api/Config.h
-MAX += crepe/api/GameObject.cpp
-MAX += crepe/api/GameObject.h
-MAX += crepe/api/GameObject.hpp
-TODO += crepe/api/ParticleEmitter.cpp
-TODO += crepe/api/ParticleEmitter.h
-TODO += crepe/api/Vector2.h
-TODO += crepe/api/Vector2.cpp
-JARO += crepe/api/Rigidbody.cpp
-JARO += crepe/api/Rigidbody.h
-LOEK += crepe/api/Script.cpp
-LOEK += crepe/api/Script.h
-LOEK += crepe/api/Script.hpp
-TODO += crepe/api/Sprite.cpp
-TODO += crepe/api/Sprite.h
-TODO += crepe/api/Texture.cpp
-TODO += crepe/api/Texture.h
-MAX += crepe/api/Transform.cpp
-MAX += crepe/api/Transform.h
-TODO += crepe/facade/SDLContext.cpp
-TODO += crepe/facade/SDLContext.h
-LOEK += crepe/facade/Sound.cpp
-LOEK += crepe/facade/Sound.h
-LOEK += crepe/facade/SoundContext.cpp
-LOEK += crepe/facade/SoundContext.h
-TODO += crepe/system/CollisionSystem.cpp
-TODO += crepe/system/CollisionSystem.h
-TODO += crepe/system/ParticleSystem.cpp
-TODO += crepe/system/ParticleSystem.h
-JARO += crepe/system/PhysicsSystem.cpp
-JARO += crepe/system/PhysicsSystem.h
-TODO += crepe/system/RenderSystem.cpp
-TODO += crepe/system/RenderSystem.h
-LOEK += crepe/system/ScriptSystem.cpp
-LOEK += crepe/system/ScriptSystem.h
-LOEK += crepe/system/System.h
-LOEK += crepe/util/LogColor.cpp
-LOEK += crepe/util/LogColor.h
-LOEK += crepe/util/fmt.cpp
-LOEK += crepe/util/fmt.h
-LOEK += crepe/util/log.cpp
-LOEK += crepe/util/log.h
-TODO += example/asset_manager.cpp
-LOEK += example/audio_internal.cpp
-TODO += example/components_internal.cpp
-MAX += example/ecs.cpp
-LOEK += example/log.cpp
-TODO += example/particle.cpp
-JARO += example/physics.cpp
-TODO += example/rendering.cpp
-LOEK += example/script.cpp
-LOEK += test/audio.cpp
-LOEK += test/dummy.cpp
-JARO += test/PhysicsTest.cpp
-
-FMT := $(JARO) #<<< CHANGE THIS TO YOUR NAME FOR STEP 2
+FMT := $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp')
format: FORCE
clang-tidy -p build/compile_commands.json --fix-errors $(FMT)
-# FMT += $(shell git ls-files '*.c' '*.cpp' '*.h' '*.hpp')
-# TODO: re-enable linter after all corrections
-
diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp
new file mode 100644
index 0000000..a9d5e6c
--- /dev/null
+++ b/src/test/AudioTest.cpp
@@ -0,0 +1,24 @@
+#include "system/AudioSystem.h"
+#include <gtest/gtest.h>
+
+#include <crepe/ComponentManager.h>
+#include <crepe/api/AudioSource.h>
+
+using namespace std;
+using namespace crepe;
+using namespace testing;
+
+class AudioTest : public Test {
+public:
+ ComponentManager component_manager{};
+ AudioSystem system {component_manager};
+
+ void SetUp() override {
+ auto & mgr = this->component_manager;
+ GameObject entity = mgr.new_object("name");
+ }
+};
+
+TEST_F(AudioTest, Default) {
+}
+
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 0e4eaed..f63a733 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -1,6 +1,6 @@
target_sources(test_main PUBLIC
- dummy.cpp
- # audio.cpp
- PhysicsTest.cpp
+ # PhysicsTest.cpp
+ # ParticleTest.cpp
+ AudioTest.cpp
)
diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp
new file mode 100644
index 0000000..6fe3133
--- /dev/null
+++ b/src/test/ParticleTest.cpp
@@ -0,0 +1,206 @@
+#include "api/Vector2.h"
+#include <crepe/ComponentManager.h>
+#include <crepe/Particle.h>
+#include <crepe/api/Config.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/ParticleEmitter.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Transform.h>
+#include <crepe/system/ParticleSystem.h>
+#include <gtest/gtest.h>
+#include <math.h>
+
+using namespace std;
+using namespace std::chrono_literals;
+using namespace crepe;
+
+class ParticlesTest : public ::testing::Test {
+protected:
+ ParticleSystem particle_system;
+ void SetUp() override {
+ ComponentManager & mgr = ComponentManager::get_instance();
+ std::vector<std::reference_wrapper<Transform>> transforms
+ = mgr.get_components_by_id<Transform>(0);
+ if (transforms.empty()) {
+
+ GameObject game_object(0, "", "", Vector2{0, 0}, 0, 0);
+
+ Color color(0, 0, 0, 0);
+ Sprite test_sprite = game_object.add_component<Sprite>(
+ make_shared<Texture>("../asset/texture/img.png"), color,
+ FlipSettings{true, true});
+
+ game_object.add_component<ParticleEmitter>(ParticleEmitter::Data{
+ .position = {0, 0},
+ .max_particles = 100,
+ .emission_rate = 0,
+ .min_speed = 0,
+ .max_speed = 0,
+ .min_angle = 0,
+ .max_angle = 0,
+ .begin_lifespan = 0,
+ .end_lifespan = 0,
+ .force_over_time = Vector2{0, 0},
+ .boundary{
+ .width = 0,
+ .height = 0,
+ .offset = Vector2{0, 0},
+ .reset_on_exit = false,
+ },
+ .sprite = test_sprite,
+ });
+ }
+ transforms = mgr.get_components_by_id<Transform>(0);
+ Transform & transform = transforms.front().get();
+ transform.position.x = 0.0;
+ transform.position.y = 0.0;
+ transform.rotation = 0.0;
+ std::vector<std::reference_wrapper<ParticleEmitter>> rigidbodies
+ = mgr.get_components_by_id<ParticleEmitter>(0);
+ ParticleEmitter & emitter = rigidbodies.front().get();
+ emitter.data.position = {0, 0};
+ emitter.data.emission_rate = 0;
+ emitter.data.min_speed = 0;
+ emitter.data.max_speed = 0;
+ emitter.data.min_angle = 0;
+ emitter.data.max_angle = 0;
+ emitter.data.begin_lifespan = 0;
+ emitter.data.end_lifespan = 0;
+ emitter.data.force_over_time = Vector2{0, 0};
+ emitter.data.boundary = {0, 0, Vector2{0, 0}, false};
+ for (auto & particle : emitter.data.particles) {
+ particle.active = false;
+ }
+ }
+};
+
+TEST_F(ParticlesTest, spawnParticle) {
+ Config::get_instance().physics.gravity = 1;
+ ComponentManager & mgr = ComponentManager::get_instance();
+ ParticleEmitter & emitter
+ = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+ emitter.data.end_lifespan = 5;
+ emitter.data.boundary.height = 100;
+ emitter.data.boundary.width = 100;
+ emitter.data.max_speed = 0.1;
+ emitter.data.max_angle = 0.1;
+ emitter.data.max_speed = 10;
+ emitter.data.max_angle = 10;
+ particle_system.update();
+ //check if nothing happend
+ EXPECT_EQ(emitter.data.particles[0].active, false);
+ emitter.data.emission_rate = 1;
+ //check particle spawnes
+ particle_system.update();
+ EXPECT_EQ(emitter.data.particles[0].active, true);
+ particle_system.update();
+ EXPECT_EQ(emitter.data.particles[1].active, true);
+ particle_system.update();
+ EXPECT_EQ(emitter.data.particles[2].active, true);
+ particle_system.update();
+ EXPECT_EQ(emitter.data.particles[3].active, true);
+
+ for (auto & particle : emitter.data.particles) {
+ // Check velocity range
+ EXPECT_GE(particle.velocity.x, emitter.data.min_speed);
+ // Speed should be greater than or equal to min_speed
+ EXPECT_LE(particle.velocity.x, emitter.data.max_speed);
+ // Speed should be less than or equal to max_speed
+ EXPECT_GE(particle.velocity.y, emitter.data.min_speed);
+ // Speed should be greater than or equal to min_speed
+ EXPECT_LE(particle.velocity.y, emitter.data.max_speed);
+ // Speed should be less than or equal to max_speed
+
+ // Check angle range
+ EXPECT_GE(particle.angle, emitter.data.min_angle);
+ // Angle should be greater than or equal to min_angle
+ EXPECT_LE(particle.angle, emitter.data.max_angle);
+ // Angle should be less than or equal to max_angle
+ }
+}
+
+TEST_F(ParticlesTest, moveParticleHorizontal) {
+ Config::get_instance().physics.gravity = 1;
+ ComponentManager & mgr = ComponentManager::get_instance();
+ ParticleEmitter & emitter
+ = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+ emitter.data.end_lifespan = 100;
+ emitter.data.boundary.height = 100;
+ emitter.data.boundary.width = 100;
+ emitter.data.min_speed = 1;
+ emitter.data.max_speed = 1;
+ emitter.data.max_angle = 0;
+ emitter.data.emission_rate = 1;
+ for (int a = 1; a < emitter.data.boundary.width / 2; a++) {
+ particle_system.update();
+ EXPECT_EQ(emitter.data.particles[0].position.x, a);
+ }
+}
+
+TEST_F(ParticlesTest, moveParticleVertical) {
+ Config::get_instance().physics.gravity = 1;
+ ComponentManager & mgr = ComponentManager::get_instance();
+ ParticleEmitter & emitter
+ = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+ emitter.data.end_lifespan = 100;
+ emitter.data.boundary.height = 100;
+ emitter.data.boundary.width = 100;
+ emitter.data.min_speed = 1;
+ emitter.data.max_speed = 1;
+ emitter.data.min_angle = 90;
+ emitter.data.max_angle = 90;
+ emitter.data.emission_rate = 1;
+ for (int a = 1; a < emitter.data.boundary.width / 2; a++) {
+ particle_system.update();
+ EXPECT_EQ(emitter.data.particles[0].position.y, a);
+ }
+}
+
+TEST_F(ParticlesTest, boundaryParticleReset) {
+ Config::get_instance().physics.gravity = 1;
+ ComponentManager & mgr = ComponentManager::get_instance();
+ ParticleEmitter & emitter
+ = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+ emitter.data.end_lifespan = 100;
+ emitter.data.boundary.height = 10;
+ emitter.data.boundary.width = 10;
+ emitter.data.boundary.reset_on_exit = true;
+ emitter.data.min_speed = 1;
+ emitter.data.max_speed = 1;
+ emitter.data.min_angle = 90;
+ emitter.data.max_angle = 90;
+ emitter.data.emission_rate = 1;
+ for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) {
+ particle_system.update();
+ }
+ EXPECT_EQ(emitter.data.particles[0].active, false);
+}
+
+TEST_F(ParticlesTest, boundaryParticleStop) {
+ Config::get_instance().physics.gravity = 1;
+ ComponentManager & mgr = ComponentManager::get_instance();
+ ParticleEmitter & emitter
+ = mgr.get_components_by_id<ParticleEmitter>(0).front().get();
+ emitter.data.end_lifespan = 100;
+ emitter.data.boundary.height = 10;
+ emitter.data.boundary.width = 10;
+ emitter.data.boundary.reset_on_exit = false;
+ emitter.data.min_speed = 1;
+ emitter.data.max_speed = 1;
+ emitter.data.min_angle = 90;
+ emitter.data.max_angle = 90;
+ emitter.data.emission_rate = 1;
+ for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) {
+ particle_system.update();
+ }
+ const double TOLERANCE = 0.01;
+ EXPECT_NEAR(emitter.data.particles[0].velocity.x, 0, TOLERANCE);
+ EXPECT_NEAR(emitter.data.particles[0].velocity.y, 0, TOLERANCE);
+ if (emitter.data.particles[0].velocity.x != 0)
+ EXPECT_NEAR(std::abs(emitter.data.particles[0].position.x),
+ emitter.data.boundary.height / 2, TOLERANCE);
+ if (emitter.data.particles[0].velocity.y != 0)
+ EXPECT_NEAR(std::abs(emitter.data.particles[0].position.y),
+ emitter.data.boundary.width / 2, TOLERANCE);
+}
diff --git a/src/test/audio.cpp b/src/test/audio.cpp
deleted file mode 100644
index d6ff689..0000000
--- a/src/test/audio.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace std::chrono_literals;
-
-// using namespace crepe;
-
-// TODO: mock internal audio class
-
-TEST(audio, play) { ASSERT_TRUE(true); }
diff --git a/src/test/dummy.cpp b/src/test/dummy.cpp
deleted file mode 100644
index a00a9c6..0000000
--- a/src/test/dummy.cpp
+++ /dev/null
@@ -1,3 +0,0 @@
-#include <gtest/gtest.h>
-
-TEST(dummy, foo) { ASSERT_TRUE(1); }