diff --git a/graphics/truck.ase b/graphics/truck.ase index b473881..cfb71a5 100644 Binary files a/graphics/truck.ase and b/graphics/truck.ase differ diff --git a/resources/truck.png b/resources/truck.png index 75f4a46..f74a107 100644 Binary files a/resources/truck.png and b/resources/truck.png differ diff --git a/src/core/assets/asset_db.cpp b/src/core/assets/asset_db.cpp index 6fbc03e..615faa6 100644 --- a/src/core/assets/asset_db.cpp +++ b/src/core/assets/asset_db.cpp @@ -29,9 +29,7 @@ void AssetDB::load(std::string asset_name) { } void AssetDB::index_assets() { - SDL_Log("Indexing assets"); for(std::filesystem::directory_entry const &itr : std::filesystem::recursive_directory_iterator("resources")) { - SDL_Log("Indexing %s", itr.path().c_str()); if(itr.is_directory()) continue; else if(!itr.path().has_extension()) @@ -43,7 +41,6 @@ void AssetDB::index_assets() { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to add asset %s, asset by that name is already indexed", name.c_str()); } else { this->assets.insert({name, std::make_shared(itr.path())}); - SDL_Log("Indexed resource %s as texture", name.c_str()); } } } diff --git a/src/core/collision.cpp b/src/core/collision.cpp index 14c0791..b256b92 100644 --- a/src/core/collision.cpp +++ b/src/core/collision.cpp @@ -39,7 +39,7 @@ void CollisionWorld::check_collisions_for(CollisionShape *shape, std::vector::iterator iter{begin}; iter != this->shapes.end(); ++iter) { CollisionShape *other{*iter}; - if(other != shape && CollisionShape::shapes_overlap(shape, other)) { + if(other != shape && other->get_owner() != shape->get_owner() && CollisionShape::shapes_overlap(shape, other)) { if((shape->get_mask() & other->get_layers()) != 0x0u) shape->get_owner()->add_overlap(shape, other); if((shape->get_layers() & other->get_mask()) != 0x0u) diff --git a/src/core/collision_shape.cpp b/src/core/collision_shape.cpp index a1ca256..3b96849 100644 --- a/src/core/collision_shape.cpp +++ b/src/core/collision_shape.cpp @@ -24,13 +24,16 @@ Shape Shape::make_box(float h_extent, float v_extent) { }; } -CollisionShape::CollisionShape(std::string const &name, Node *owner, Shape shape) +CollisionShape::CollisionShape(std::string const &name, Shape shape) : Node2D(name) -, world{CanvasEngine::get_singleton()->get_collision_world()} { - this->shape = shape; -} +, world{CanvasEngine::get_singleton()->get_collision_world()} +, shape{shape} {} void CollisionShape::_added() { + this->ce::Node2D::_added(); + ce::Transform transform{this->get_global_transform()}; + if(!this->is_inside_tree()) + return; Node *parent{this->get_parent()}; while(parent != nullptr) { if(CollidableNode *as_collidable{dynamic_cast(parent)}) { @@ -40,16 +43,18 @@ void CollisionShape::_added() { } } SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s does not have a parent that is a CollidableNode to serve as the owner", this->get_name().c_str()); - #ifdef DEBUG - abort(); - #endif if(this->is_registered) this->deregister_with_world(); +#ifdef DEBUG + abort(); +#endif } void CollisionShape::_removed() { - if(this->is_registered) + this->ce::Node2D::_removed(); + if(this->is_registered) { this->deregister_with_world(); + } } bool CollisionShape::can_collide(CollisionShape const *lhs, CollisionShape const *rhs) { @@ -71,9 +76,9 @@ bool CollisionShape::shapes_overlap(CollisionShape const *lhs, CollisionShape co ? overlap_circle_circle(lshape.circle, lhst, rshape.circle, rhst) : overlap_circle_aabb(lshape.circle, lhst, rshape.box, rhst); } else if(lhs->shape.shape == Shape::AABB) { - return rhs->shape.shape == Shape::CIRCLE - ? overlap_circle_aabb(rshape.circle, rhst, lshape.box, lhst) - : overlap_aabb_aabb(lshape.box, lhst, rshape.box, rhst); + return rhs->shape.shape == Shape::AABB + ? overlap_aabb_aabb(lshape.box, lhst, rshape.box, rhst) + : overlap_circle_aabb(lshape.circle, lhst, rshape.box, rhst); } else { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Attempt to compare shapes with a shape that is not of a valid shape. This should never happen."); #ifdef DEBUG @@ -97,16 +102,9 @@ bool CollisionShape::overlap_circle_aabb(ShapeCircle lhs, Transform lhst, ShapeA } bool CollisionShape::overlap_aabb_aabb(ShapeAABB lhs, Transform lhst, ShapeAABB rhs, Transform rhst) { - // calculate size of bounding box encompassing both. If it's smaller than the size of both summed, the two are overlapping. - float const bound_x{std::max({ - std::abs((lhst.position.x - lhs.h_extent) - (rhst.position.x + rhs.h_extent)), - lhs.h_extent, rhs.h_extent - })}; - float const bound_y{std::max({ - std::abs((lhst.position.y - lhs.v_extent) - (rhst.position.y + rhs.v_extent)), - lhs.v_extent, rhs.v_extent - })}; - return bound_x < (lhs.h_extent + rhs.h_extent) && bound_y < (lhs.v_extent + rhs.v_extent); + ce::Vecf const diff{lhst.position - rhst.position}; + return std::abs(diff.x) <= (lhs.h_extent + rhs.h_extent) + && std::abs(diff.y) <= (lhs.v_extent + rhs.v_extent); } CollidableNode *CollisionShape::get_owner() const { diff --git a/src/core/collision_shape.hpp b/src/core/collision_shape.hpp index 2debd46..80c80b1 100644 --- a/src/core/collision_shape.hpp +++ b/src/core/collision_shape.hpp @@ -29,7 +29,7 @@ class CollisionShape : public Node2D { CollisionWorld &world; bool is_registered{false}; public: - CollisionShape(std::string const &name, Node *owner, Shape shape); + CollisionShape(std::string const &name, Shape shape); virtual void _added() override; virtual void _removed() override; diff --git a/src/core/level.cpp b/src/core/level.cpp index fde5b59..25dbd03 100644 --- a/src/core/level.cpp +++ b/src/core/level.cpp @@ -17,6 +17,7 @@ void Level::deinstantiate() { void Level::propagate_tick(double const &delta_time) { this->root->propagate_tick(delta_time); + this->root->propagate_post_tick(); } void Level::propagate_draw(SDL_Renderer *render) { diff --git a/src/core/math/vector.cpp b/src/core/math/vector.cpp index 843de6e..fbd4c2f 100644 --- a/src/core/math/vector.cpp +++ b/src/core/math/vector.cpp @@ -71,6 +71,12 @@ bool Vecf::is_nan() const { return std::isnan(this->x) || std::isnan(this->y); } +Vecf Vecf::clamp_magnitude(float max) const { + float const old_magnitude{this->magnitude()}; + float const new_magnitude{std::min(max, old_magnitude)}; + return (old_magnitude != 0.f ? ((*this) / old_magnitude) : ce::Vecf::ZERO) * new_magnitude; +} + void Vecf::scale(float x, float y) { this->x *= x; this->y *= y; diff --git a/src/core/math/vector.hpp b/src/core/math/vector.hpp index a299ad9..13f5db6 100644 --- a/src/core/math/vector.hpp +++ b/src/core/math/vector.hpp @@ -37,6 +37,7 @@ struct Vecf { //! returns true if either the x or y element is NaN bool is_nan() const; + Vecf clamp_magnitude(float max) const; //! scale vector member-wise void scale(float x, float y); //! scale vector member-wise @@ -62,20 +63,20 @@ bool operator==(Vecf const &lhs, Vecf const &rhs) { static inline Vecf operator+(Vecf const &lhs, Vecf const &rhs) { - return Vecf(lhs.x + rhs.x, lhs.y + rhs.y); + return Vecf{lhs.x + rhs.x, lhs.y + rhs.y}; } static inline Vecf operator-(Vecf const &lhs, Vecf const &rhs) { - return Vecf(lhs.x - rhs.x, lhs.y - rhs.y); + return Vecf{lhs.x - rhs.x, lhs.y - rhs.y}; } static inline Vecf operator*(Vecf const &v, float f) { - return Vecf(v.x * f, v.y * f); + return Vecf{v.x * f, v.y * f}; } static inline Vecf operator/(Vecf const &v, float f) { - return Vecf(v.x / f, v.y / f); + return Vecf{v.x / f, v.y / f}; } static inline diff --git a/src/core/node.cpp b/src/core/node.cpp index 2e9d965..8b109f0 100644 --- a/src/core/node.cpp +++ b/src/core/node.cpp @@ -1,5 +1,6 @@ #include "node.hpp" #include +#include #include #include #include @@ -52,8 +53,6 @@ void Node::set_name(std::string const &name) { void Node::flag_for_deletion() { this->request_deletion = true; - this->tick = false; - this->visible = false; } bool Node::requests_deletion() const { @@ -84,6 +83,10 @@ bool Node::is_inside_tree() const { return this->inside_tree; } +Node::ChildrenVector &Node::get_children() { + return this->children; +} + void Node::set_level(ce::Level *level) { // parent level needs to match assert(this->parent == nullptr || this->parent->get_level() == level); @@ -136,12 +139,12 @@ void Node::propagate_tick(double const &delta_time) { void Node::propagate_post_tick() { // clean up children queued for deletion - for(size_t i{0}; i < this->children.size(); ++i) { + for(size_t i{0}; i < this->children.size();) { ChildrenVector::value_type &value{this->children[i]}; if(value.second->requests_deletion()) { - value.second->propagate_removed(); - this->child_removed.invoke(value.second.get()); + this->remove_child(value.second.get()); } else { + ++i; value.second->propagate_post_tick(); } } diff --git a/src/core/node.hpp b/src/core/node.hpp index 659a15c..49179fc 100644 --- a/src/core/node.hpp +++ b/src/core/node.hpp @@ -34,7 +34,7 @@ public: public: Node(std::string name); virtual ~Node(); -protected: +public: virtual void _added() {} //!< called the moment after the object is added as a child to another node virtual void _first_tick() {} //!< called the first frame this object is active virtual void _tick(double const &delta_time [[maybe_unused]]) {} //!< called every frame @@ -57,6 +57,7 @@ public: bool is_ticking() const; ce::Level *get_level() const; bool is_inside_tree() const; + ChildrenVector &get_children(); private: void set_level(ce::Level *level); void set_is_inside_tree(bool value); diff --git a/src/core/node2d.cpp b/src/core/node2d.cpp index aa9b9ce..01d9fd6 100644 --- a/src/core/node2d.cpp +++ b/src/core/node2d.cpp @@ -6,6 +6,7 @@ Node2D::Node2D(std::string name) : Node(name) {} void Node2D::_added() { this->parent_node2d = dynamic_cast(this->get_parent()); + this->_update_transform(); } void Node2D::_update_transform() { @@ -13,6 +14,8 @@ void Node2D::_update_transform() { this->global_transform = this->transform * this->parent_node2d->get_global_transform(); else this->global_transform = this->transform; + for(ChildrenVector::value_type &pair : this->get_children()) + pair.second->_update_transform(); } void Node2D::set_transform(Transform const &transform) { @@ -30,10 +33,11 @@ void Node2D::set_global_transform(Transform transform) { Transform parent = this->parent_node2d->get_global_transform(); transform.position -= parent.position.rotated(-parent.rotation); transform.scale_by(parent.scale.reciprocal()); - assert(transform.scale.x != 0.f || transform.scale.y != 0.f); // !!! + assert(transform.scale.x != 0.f || transform.scale.y != 0.f); transform.rotation -= parent.rotation; } this->transform = transform; + this->_update_transform(); } Transform const &Node2D::get_global_transform() const { diff --git a/src/level_1.cpp b/src/level_1.cpp index 75fd8fc..1cd5455 100644 --- a/src/level_1.cpp +++ b/src/level_1.cpp @@ -13,6 +13,6 @@ ce::Node::OwnedPtr Level1::construct() { .scale = ce::Vecf::ONE }); root->create_child(); - root->create_child(0.f); + root->create_child(true); return std::move(root); } diff --git a/src/main.cpp b/src/main.cpp index a96628c..f3ccfa2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,11 @@ #include "core/canvas_engine.hpp" #include "level_1.hpp" +#include ce::CanvasEngine engine{}; int main(int argc [[maybe_unused]], char* argv [[maybe_unused]][]) { + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); std::unique_ptr level{std::make_unique()}; engine.run(level); } diff --git a/src/player.cpp b/src/player.cpp index 9dd6bb5..9f6112d 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -10,11 +10,15 @@ #include "truck.hpp" #include #include +#include Player::Player() : ce::CollidableNode("player", 0x1u, 0x1u) , sprite{this->create_child("bike", "bike")} -, shape{this->create_child("player_col_shape", this, ce::Shape::make_box(1.f, 2.f))} { +, shape{this->create_child("player_col_shape", ce::Shape::make_box(.4f, 0.75f))} { + this->set_global_transform(this->get_global_transform() + .translated({0.f, 2.f}) + ); this->sprite->set_global_transform(this->sprite->get_global_transform() .scaled({1.5f, 1.5f}) ); @@ -24,9 +28,7 @@ Player::Player() new ce::KeyboardScancode(SDL_SCANCODE_A), new ce::KeyboardScancode(SDL_SCANCODE_D) ) - }) - .changed - .connect(ce::Callable::make( + }).changed.connect(ce::Callable::make( this, &Player::_input_horizontal_movement) ); map.bind_input("vertical", { @@ -34,12 +36,12 @@ Player::Player() new ce::KeyboardScancode(SDL_SCANCODE_W), new ce::KeyboardScancode(SDL_SCANCODE_S) ) - }) - .changed - .connect(ce::Callable::make( + }) .changed.connect(ce::Callable::make( this, &Player::_input_vertical_movement) ); - this->overlap_enter.connect(ce::Callable::make(this, &Player::_on_overlap_enter)); + this->overlap_enter.connect(ce::Callable::make( + this, &Player::_on_overlap_enter + )); } void Player::_tick(double const &delta) { @@ -49,9 +51,17 @@ void Player::_tick(double const &delta) { ACCELERATION * delta ); ce::Transform trans{this->get_global_transform().translated(this->velocity * delta)}; - trans.position.x = std::clamp(trans.position.x, -3.f, 3.f); - trans.position.y = std::clamp(trans.position.y, -2.f, 2.f); + trans.position.x = std::clamp(trans.position.x, -LIMITS.x, LIMITS.x); + trans.position.y = std::clamp(trans.position.y, -LIMITS.y, LIMITS.y); this->set_global_transform(trans); + + if(this->invincibility > 0.f) { + this->invincibility -= delta; + this->sprite->set_visible(this->invincibility <= 0.f + ? true + : int(std::floorf(this->invincibility / DAMAGE_FLASH_FREQ)) % 2 != 0 + ); + } } void Player::_input_horizontal_movement(ce::InputValue value) { @@ -63,9 +73,10 @@ void Player::_input_vertical_movement(ce::InputValue value) { } void Player::_on_overlap_enter(ce::CollisionShape *, ce::CollidableNode *other, ce::CollisionShape *) { + if(this->invincibility > 0.f) + return; if(Truck *truck{dynamic_cast(other)}) { - // TODO: Implement damage - this->flag_for_deletion(); + this->invincibility = 2.f; + this->velocity = (this->get_global_transform().position - other->get_global_transform().position).normalized() * DAMAGE_FORCE; } - SDL_Log("overlap"); } diff --git a/src/player.hpp b/src/player.hpp index 2c0321f..e7b3464 100644 --- a/src/player.hpp +++ b/src/player.hpp @@ -11,12 +11,16 @@ class Sprite; class Player : public ce::CollidableNode { ce::Vecf const SPEED{3.f, 2.5f}; - float const ACCELERATION{20.f}; + float const ACCELERATION{10.f}; + ce::Vecf const LIMITS{3.f, 3.f}; + float const DAMAGE_FLASH_FREQ{.1f}; + float const DAMAGE_FORCE{6.f}; ce::Vecf velocity{0.f, 0.f}; ce::Vecf input{0.f, 0.f}; ce::CollisionShape *shape{nullptr}; ce::Sprite *sprite{nullptr}; + float invincibility{0.f}; public: Player(); virtual void _tick(double const &delta) override; diff --git a/src/truck.cpp b/src/truck.cpp index f97b82e..c646b46 100644 --- a/src/truck.cpp +++ b/src/truck.cpp @@ -1,17 +1,29 @@ #include "truck.hpp" -#include "core/callable.hpp" #include "core/collision_shape.hpp" #include "core/sprite.hpp" +#include -Truck::Truck(float x_pos) +Truck::Truck(bool left) : ce::CollidableNode("truck", 0x1u, 0x1u) , sprite{this->create_child("sprite", "truck")} -, shape{this->create_child("truck_col_shape", this, ce::Shape::make_box(2.f, 2.f))} { - this->sprite->set_global_transform(this->get_global_transform() - .scaled({2.5f, 2.5f}) - .translated({x_pos, -3.f}) - ); +, shape{this->create_child("truck_col_shape", ce::Shape::make_box(.5f, 1.f))} +, spawned_left{left} { + this->sprite->set_global_transform(this->get_global_transform() .scaled({2.5f, 2.5f})); + this->set_global_transform(this->get_global_transform().translated({ + .x=this->spawned_left ? -1.4f : 1.4f, + .y=-4.5f + })); } void Truck::_tick(double const &delta) { + wave_time += delta * FREQUENCY; + ce::Transform const transform{this->get_global_transform() + .translated(ce::Vecf{ + .x=std::sin(wave_time) * AMPLITUDE * (this->spawned_left > 0.f ? 1.f : -1.f), + .y=this->RELATIVE_VERTICAL_SPEED + } * float(delta)) + }; + this->set_global_transform(transform); + if(transform.position.y > 4.5f) + this->flag_for_deletion(); } diff --git a/src/truck.hpp b/src/truck.hpp index 9a18560..85642d1 100644 --- a/src/truck.hpp +++ b/src/truck.hpp @@ -9,10 +9,15 @@ namespace ce { }; class Truck : public ce::CollidableNode { + float const AMPLITUDE{4.f}; + float const FREQUENCY{3.f}; + float const RELATIVE_VERTICAL_SPEED{1.5f}; + float wave_time{0.f}; + bool spawned_left{false}; ce::Sprite *sprite{nullptr}; ce::CollisionShape *shape{nullptr}; public: - Truck(float x_pos); + Truck(bool left); virtual void _tick(double const &delta) override; };