diff --git a/.github/format_check_diff.sh b/.github/format_check_diff.sh new file mode 100755 index 0000000..791e32c --- /dev/null +++ b/.github/format_check_diff.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +[[ ! $(git --version) ]] && exit 1 + +output=$(git diff) + +if [[ "$output" != "" ]]; then + echo "One or more source files are not formatted!" + exit 1 +fi + +echo "All source files are formatted" +exit diff --git a/.github/workflows/format_check.yml b/.github/workflows/format_check.yml new file mode 100644 index 0000000..c0e4aaa --- /dev/null +++ b/.github/workflows/format_check.yml @@ -0,0 +1,11 @@ +name: ci +on: [push] +jobs: + format-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: format code + run: tools/format_code.sh + - name: check diff + run: .github/format_check_diff.sh diff --git a/DogTales/CMakeLists.txt b/DogTales/CMakeLists.txt index 2e047f5..1fa7a61 100644 --- a/DogTales/CMakeLists.txt +++ b/DogTales/CMakeLists.txt @@ -1,5 +1,5 @@ set(generated_header_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") -configure_file(src/build_version.hpp.in "${generated_header_dir}/src/build_version.hpp" @ONLY) +configure_file(dog/build_version.hpp.in "${generated_header_dir}/dog/build_version.hpp" @ONLY) add_executable(${PROJECT_NAME}) @@ -13,6 +13,6 @@ target_include_directories(${PROJECT_NAME} PRIVATE "${generated_header_dir}" ) -file(GLOB_RECURSE sources LIST_DIRECTORIES false CONFIGURE_DEPENDS "src/*.?pp") +file(GLOB_RECURSE sources LIST_DIRECTORIES false CONFIGURE_DEPENDS "dog/*.?pp") target_sources(${PROJECT_NAME} PRIVATE ${sources}) diff --git a/DogTales/src/build_version.hpp.in b/DogTales/dog/build_version.hpp.in similarity index 100% rename from DogTales/src/build_version.hpp.in rename to DogTales/dog/build_version.hpp.in diff --git a/DogTales/dog/components/physics.cpp b/DogTales/dog/components/physics.cpp new file mode 100644 index 0000000..d1609da --- /dev/null +++ b/DogTales/dog/components/physics.cpp @@ -0,0 +1,24 @@ +#include + +namespace dog::component { + +void Physics::tick(bave::Seconds dt) { + static constexpr bave::Seconds ft{0.005}; // 5ms + + if (dt.count() > tick_limit_v.count()) { + return; + } // return for unexpected dt values, particularly during the beginning of the state + + for (dt += m_residue; dt > ft; dt -= ft) { integrate(ft); } + m_residue = dt; +} + +void Physics::integrate(bave::Seconds dt) { + acceleration.y += gravity * dt.count(); + velocity = (velocity + (acceleration / mass) * dt.count()) * friction; + + velocity = glm::clamp(velocity, -max_velocity_v, max_velocity_v); + + position += velocity * dt.count(); +} +} // namespace dog::component diff --git a/DogTales/dog/components/physics.hpp b/DogTales/dog/components/physics.hpp new file mode 100644 index 0000000..15ba386 --- /dev/null +++ b/DogTales/dog/components/physics.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +namespace dog::component { +class Physics { + + static constexpr glm::vec2 default_friction{0.99f}; + static constexpr float default_gravity{-1000.f}; + static constexpr float default_mass{1.f}; + + static constexpr glm::vec2 max_velocity_v{1000.f}; + static constexpr bave::Seconds tick_limit_v{100ms}; + + bave::Seconds m_residue{}; + + public: + Physics(glm::vec2 friction = default_friction, float gravity = default_gravity, float mass = default_mass) + : friction(friction), gravity(gravity), mass(mass) {} + + glm::vec2 position{}; + glm::vec2 velocity{}; + glm::vec2 acceleration{}; + + glm::vec2 friction{}; + float gravity{}; + float mass{}; + + void tick(bave::Seconds dt); + void integrate(bave::Seconds dt); +}; +} // namespace dog::component diff --git a/DogTales/src/dogtales.cpp b/DogTales/dog/dogtales.cpp similarity index 91% rename from DogTales/src/dogtales.cpp rename to DogTales/dog/dogtales.cpp index feccd3d..67ec791 100644 --- a/DogTales/src/dogtales.cpp +++ b/DogTales/dog/dogtales.cpp @@ -1,5 +1,6 @@ -#include +#include +namespace dog { DogTales::DogTales(bave::App& app) : bave::Driver(app) {} void DogTales::tick() { @@ -23,3 +24,4 @@ void DogTales::on_key(bave::KeyInput const& key_input) { void DogTales::set_viewport_to_world_space() const { get_app().get_render_device().render_view.viewport = world_space_v; } +} // namespace dog diff --git a/DogTales/src/dogtales.hpp b/DogTales/dog/dogtales.hpp similarity index 85% rename from DogTales/src/dogtales.hpp rename to DogTales/dog/dogtales.hpp index 85d9b9b..68e1141 100644 --- a/DogTales/src/dogtales.hpp +++ b/DogTales/dog/dogtales.hpp @@ -1,7 +1,8 @@ #pragma once #include -#include +#include +namespace dog { class DogTales : public bave::Driver { static constexpr glm::vec2 world_space_v{1280.0f, 720.0f}; @@ -17,3 +18,4 @@ class DogTales : public bave::Driver { public: explicit DogTales(bave::App& app); }; +} // namespace dog diff --git a/DogTales/src/fatal_error.hpp b/DogTales/dog/fatal_error.hpp similarity index 89% rename from DogTales/src/fatal_error.hpp rename to DogTales/dog/fatal_error.hpp index 4111c08..b713643 100644 --- a/DogTales/src/fatal_error.hpp +++ b/DogTales/dog/fatal_error.hpp @@ -2,9 +2,11 @@ #include #include +namespace dog { class FatalError : public std::runtime_error { public: template explicit FatalError(fmt::format_string fmt, Args&&... args) : std::runtime_error(fmt::format(fmt, std::forward(args)...)) {} }; +} // namespace dog diff --git a/DogTales/src/main.cpp b/DogTales/dog/main.cpp similarity index 92% rename from DogTales/src/main.cpp rename to DogTales/dog/main.cpp index 3f86ea4..27d52e3 100644 --- a/DogTales/src/main.cpp +++ b/DogTales/dog/main.cpp @@ -1,8 +1,8 @@ #include #include -#include -#include -#include +#include +#include +#include #include namespace { @@ -41,7 +41,7 @@ auto run_app(int const argc, char const* const* argv) -> int { auto app = bave::DesktopApp{create_info}; - app.set_bootloader([](bave::App& app) { return std::make_unique(app); }); + app.set_bootloader([](bave::App& app) { return std::make_unique(app); }); return static_cast(app.run()); } diff --git a/DogTales/src/player.cpp b/DogTales/dog/player.cpp similarity index 82% rename from DogTales/src/player.cpp rename to DogTales/dog/player.cpp index af190d1..b9601b6 100644 --- a/DogTales/src/player.cpp +++ b/DogTales/dog/player.cpp @@ -1,22 +1,24 @@ -#include +#include +namespace dog { Player::Player(glm::vec2 const world_space) : m_world_space(world_space) { m_sprite.set_size(size_v); } void Player::tick(bave::Seconds const dt) { - // Update the player's movement based on velocity. - m_sprite.transform.position += m_vel * dt.count(); + + m_physics.tick(dt); + m_sprite.transform.position = m_physics.position; handle_wall_collision(); + m_physics.position = m_sprite.transform.position; } void Player::draw(bave::Shader& shader) const { m_sprite.draw(shader); } void Player::update_movement(glm::vec2 const& direction) { // Normalize the direction vector - glm::vec2 normalized_direction = glm::normalize(direction); + // glm::vec2 normalized_direction = glm::normalize(direction); // Apply speed to the normalized direction vector - m_vel = normalized_direction * speed_v; } void Player::handle_input(bave::KeyInput const& key_input) { @@ -41,10 +43,15 @@ void Player::handle_wall_collision() { auto const bounce_rect = bave::Rect<>::from_size(m_world_space - m_sprite.get_size(), glm::vec2{0.0f}); // if the sprite's position exceeds the play area, the corresponding velocity component needs to flip. - if (position.x < bounce_rect.top_left().x || position.x > bounce_rect.bottom_right().x) { m_vel.x *= -1.0f; } - if (position.y > bounce_rect.top_left().y || position.y < bounce_rect.bottom_right().y) { m_vel.y *= -1.0f; } + if (position.x < bounce_rect.top_left().x || position.x > bounce_rect.bottom_right().x) { + m_physics.velocity.x *= -0.9f; + } + if (position.y > bounce_rect.top_left().y || position.y < bounce_rect.bottom_right().y) { + m_physics.velocity.y *= -0.9f; + } // clamp the position to the play area. // bottom_left() gives us the minimum x and y whereas top_right() gives us the maximum. position = glm::clamp(position, bounce_rect.bottom_left(), bounce_rect.top_right()); } +} // namespace dog diff --git a/DogTales/src/player.hpp b/DogTales/dog/player.hpp similarity index 83% rename from DogTales/src/player.hpp rename to DogTales/dog/player.hpp index 9e972fc..453602d 100644 --- a/DogTales/src/player.hpp +++ b/DogTales/dog/player.hpp @@ -1,8 +1,9 @@ #pragma once #include #include -#include +#include "components/physics.hpp" +namespace dog { class Player { static constexpr glm::vec2 speed_v{500.0f, 500.0f}; static constexpr glm::vec2 size_v{50.0f, 90.0f}; @@ -11,7 +12,7 @@ class Player { bave::Sprite m_sprite{}; - glm::vec2 m_vel{}; + component::Physics m_physics{}; void handle_wall_collision(); @@ -24,3 +25,4 @@ class Player { void draw(bave::Shader& shader) const; void update_movement(glm::vec2 const& direction); }; +} // namespace dog diff --git a/DogTales/src/services/service.hpp b/DogTales/dog/services/service.hpp similarity index 79% rename from DogTales/src/services/service.hpp rename to DogTales/dog/services/service.hpp index 068e10e..1db9402 100644 --- a/DogTales/src/services/service.hpp +++ b/DogTales/dog/services/service.hpp @@ -1,5 +1,7 @@ #pragma once #include +namespace dog { /// \brief Base class for all services. class IService : public bave::Polymorphic {}; +} // namespace dog diff --git a/DogTales/src/services/services.hpp b/DogTales/dog/services/services.hpp similarity index 88% rename from DogTales/src/services/services.hpp rename to DogTales/dog/services/services.hpp index eadf55d..6a416d5 100644 --- a/DogTales/src/services/services.hpp +++ b/DogTales/dog/services/services.hpp @@ -1,13 +1,14 @@ #pragma once #include #include -#include -#include +#include +#include #include #include #include #include +namespace dog { /// \brief Concept constraining Type to a subclass of IService. template concept ServiceT = std::derived_from; @@ -27,10 +28,10 @@ class Services { /// \pre service must not be null, From must not already be bound. template To> void bind(std::unique_ptr service) { - if (!service) { throw FatalError{"Attempt to bind null service"}; } + if (!service) { throw dog::FatalError{"Attempt to bind null service"}; } static auto const index = std::type_index{typeid(From)}; auto lock = std::scoped_lock{m_mutex}; - if (m_services.contains(index)) { throw FatalError{"Attempt to bind duplicate service"}; } + if (m_services.contains(index)) { throw dog::FatalError{"Attempt to bind duplicate service"}; } m_services.insert_or_assign(index, std::move(service)); } @@ -73,7 +74,7 @@ class Services { template [[nodiscard]] auto get() const -> Type& { auto ret = find(); - if (!ret) { throw FatalError{"Service not found"}; } + if (!ret) { throw dog::FatalError{"Service not found"}; } return *ret; } @@ -88,3 +89,4 @@ class Services { std::unordered_map> m_services{}; mutable std::mutex m_mutex{}; }; +} // namespace dog diff --git a/DogTales/src/tests/test.cpp b/DogTales/dog/tests/test.cpp similarity index 98% rename from DogTales/src/tests/test.cpp rename to DogTales/dog/tests/test.cpp index 5e244b6..d8beca5 100644 --- a/DogTales/src/tests/test.cpp +++ b/DogTales/dog/tests/test.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include diff --git a/DogTales/src/tests/test.hpp b/DogTales/dog/tests/test.hpp similarity index 100% rename from DogTales/src/tests/test.hpp rename to DogTales/dog/tests/test.hpp diff --git a/DogTales/src/tests/test_services.cpp b/DogTales/dog/tests/test_services.cpp similarity index 87% rename from DogTales/src/tests/test_services.cpp rename to DogTales/dog/tests/test_services.cpp index 257f792..ab0e066 100644 --- a/DogTales/src/tests/test_services.cpp +++ b/DogTales/dog/tests/test_services.cpp @@ -1,7 +1,8 @@ #include -#include -#include +#include +#include +namespace dog { namespace { namespace one { struct Foo : IService { @@ -37,7 +38,7 @@ ADD_TEST(Services_GetException) { auto thrown = false; try { [[maybe_unused]] auto& foo = services.get(); - } catch (FatalError const&) { thrown = true; } + } catch (dog::FatalError const&) { thrown = true; } EXPECT(thrown); } @@ -50,7 +51,7 @@ ADD_TEST(Services_DuplicateException) { auto thrown = false; try { services.bind(std::make_unique()); - } catch (FatalError const&) { thrown = true; } + } catch (dog::FatalError const&) { thrown = true; } EXPECT(thrown); } @@ -72,3 +73,4 @@ ADD_TEST(Services_BindSubclass) { EXPECT(services.get().get_value() == 42); } } // namespace +} // namespace dog diff --git a/tools/format_code.sh b/tools/format_code.sh new file mode 100755 index 0000000..d4268b3 --- /dev/null +++ b/tools/format_code.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +[[ ! $(clang-format --version) ]] && exit 1 + +script_path=${0%/*} +tools_root=${script_path%/*} +project_root=$tools_root/.. + +if [[ "$0" != "$project_root" ]] && [[ "$project_root" != "" ]]; then + cd "$project_root" || exit 1 + echo "-- Changed pwd to $(pwd)" +fi + +files=$(find DogTales -name "*.?pp") +if [[ "$files" == "" ]]; then + echo "-- No source files found" + exit +fi + +clang-format -i $files +echo -e "-- Formatted Files:\n$files\n" + +exit