diff --git a/include/Engine/types/Scene.h b/include/Engine/types/Scene.h index a03efab..296c1a0 100644 --- a/include/Engine/types/Scene.h +++ b/include/Engine/types/Scene.h @@ -43,6 +43,7 @@ public: [[nodiscard]] bool ShouldTick() const; [[nodiscard]] float TickDeltaTime() const; [[nodiscard]] float FrameDeltaTime() const; + [[nodiscard]] u8 GetTickRate() const; /// @returns the number of microseconds since Jan 1st 1970 that the last tick was finished on. [[nodiscard]] unsigned long LastTickTime() const; public: diff --git a/include/Engine/types/entity/mixins/Movable.h b/include/Engine/types/entity/mixins/Movable.h index f51d4f6..2a409cc 100644 --- a/include/Engine/types/entity/mixins/Movable.h +++ b/include/Engine/types/entity/mixins/Movable.h @@ -7,28 +7,126 @@ namespace Engine { } // Movable Object. +// TODO currently assumes no mass. class Engine::Movable { -protected: - // Assuming 0 radians is up. - float face_angle = 0.0f; +private: + float previous_decay = 0; + float previous_velocity = 0; + float previous_heading = 0; + float previous_face_angle = 0; + float previous_constant_force_heading = 0; + float previous_constant_force_velocity = 0; + float previous_friction = 0; + Vector2 previous_position = Vector2::Zero; + [[nodiscard]] inline float NormalizeRadians(float radians) const; +private: + float decay = 0; + float velocity = 0; + + float heading = 0; + + //float heading_rotational_velocity = 0; + //float heading_rotational_velocity_decay = 0; + + float face_angle = 0; + //float face_angle_rotational_velocity = 0; + //float face_angle_rotational_velocity_decay = 0; + + float constant_force_heading = 0; + float constant_force_velocity = 0; + + float friction = 0; Vector2 position = {0, 0}; -public: - // Movements independent of the face_angle. - void MoveX(float speed); - void MoveY(float speed); - void Move(float angle_rad, float speed); +protected: + /// Instantly changes the direction of travel. + /// @note Radians, Zero is interpreted as to the right. + void SetHeading(float heading_rad); - // Movements dependent on face angle. - void MoveForward(float speed); - void MoveBackward(float speed); - void MoveLeft(float speed); - void MoveRight(float speed); + /** Instantly changes the heading of a constant force to be applied + * to this movable after everything else, Like gravity. */ + /// @note Radians, Zero is interpreted as to the right. + void SetConstantForceHeading(float heading_rad); - void Rotate(float speed); - void SetRotation(float new_rotation); + /// Instantly changes the amount constant velocity For ex, Gravity. + void SetConstantForce(float new_velocity); + + /// Instantly changes the rate at which this object naturally shows down. + /// @note 0 never loses speed, 1 loses speed at the beginning of the next tick. + void SetDecay(float new_decay); + + /// Instantly changes the amount of velocity. + void SetVelocity(float new_velocity); + + /// Instantly changes the amount of friction + /// @note Zero is no friction. 1 is full friction. + void SetFriction(float new_friction); + + /// Instantly move from the current position to a new position. + /// @param reset Whether the other values should be preserved. + void SetPosition(const Vector2& new_position, bool reset = false); + + void SetVisualRotation(float rad_rotation); public: - [[nodiscard]] float GetRotation() const; + /// @returns The direction of travel in radians. + /// @note Radians, Zero is interpreted as to the right. + [[nodiscard]] float GetHeading() const; + + /// @returns The rate at which this object will naturally slow down. + /// @note 0 never loses speed, 1 loses speed on the next update. + [[nodiscard]] float GetDecay() const; + + /// @returns The current velocity / speed. + [[nodiscard]] float GetVelocity() const; + + /// @returns The current friction. + /// @note Zero is no friction. 1 is full friction. + [[nodiscard]] float GetFriction() const; + + /// @returns The current position. [[nodiscard]] Vector2 GetPosition() const; + + /// @returns The direction of travel the constant force will move this object in. + /// @note Radians, Zero is interpreted as to the right. + [[nodiscard]] float GetConstantForceHeading() const; + + /// @returns The current velocity / speed. + [[nodiscard]] float GetConstantForceVelocity() const; + + /// @returns The direction we're *visually* facing. + /// @note Radians, Zero is interpreted as to the right. + [[nodiscard]] float GetVisualRotation() const; public: - explicit Movable(const Vector2& position, float rad_rotation = 0.0f) : position(position), face_angle(rad_rotation) {} + /// Advance state. + void Move(const u8& tick_rate); + + /// Adds velocity in a given direction. + /// @param heading_rad The heading in which to apply the velocity. + /// @param vel The velocity to apply. + /// @note Velocities will smoothly combine. + void AddVelocity(float heading_rad, float vel); + + /// @returns the relative direction in radians for the forward. + [[nodiscard]] float HeadingRelativeForward() const; + [[nodiscard]] float FaceRelativeForward() const; + + /// @returns The direction in radians for the back. + [[nodiscard]] float FaceRelativeBackward() const; + [[nodiscard]] float HeadingRelativeBackward() const; + + /// @returns The relative direction in radians for left. + [[nodiscard]] float FaceRelativeLeft() const; + [[nodiscard]] float HeadingRelativeLeft() const; + + [[nodiscard]] float FaceRelativeRight() const; + [[nodiscard]] float HeadingRelativeRight() const; + //virtual Movable Extrapolate(const unsigned long& last_tick_time, const unsigned long& target_time); +public: + [[nodiscard]] static float WorldUp(); + [[nodiscard]] static float WorldDown(); + [[nodiscard]] static float WorldLeft(); + [[nodiscard]] static float WorldRight(); + + /// @returns an interpolation of this Movable. + //virtual Movable Interpolate(unsigned long previous_tick_time, u8 tick_rate, unsigned long now); + explicit Movable(const Vector2& position, float face_angle = 0.0f) : position(position), face_angle(face_angle) {} }; \ No newline at end of file diff --git a/include/Game/entity/Box.h b/include/Game/entity/Box.h index 872f69f..b878a1e 100644 --- a/include/Game/entity/Box.h +++ b/include/Game/entity/Box.h @@ -11,5 +11,6 @@ public: void Update() final; public: explicit Box(const Vector2& position, unsigned int layer = 1, float rad_rotation = 0.0f) : Engine::InstancedSprite(position, layer, "assets/sprites/Re3D.png", - {0.5, 0.5},rad_rotation, Colors::White, nullptr, "assets/sprites/alpha_mask.png") {} + {0.5, 0.5},rad_rotation, Colors::White, nullptr, "assets/sprites/alpha_mask.png") + { SetDecay(0.10f); SetVisualRotation(WorldRight()); SetPosition({200, 200}, false); } }; \ No newline at end of file diff --git a/src/Engine/types/Scene.cpp b/src/Engine/types/Scene.cpp index 21b014d..31b180b 100644 --- a/src/Engine/types/Scene.cpp +++ b/src/Engine/types/Scene.cpp @@ -177,3 +177,7 @@ unsigned long Scene::LastTickTime() const { return last_tick_time; } +u8 Scene::GetTickRate() const { + return tick_rate; +} + diff --git a/src/Engine/types/entity/Moby.cpp b/src/Engine/types/entity/Moby.cpp index 1178c18..3e75fcc 100644 --- a/src/Engine/types/entity/Moby.cpp +++ b/src/Engine/types/entity/Moby.cpp @@ -1,59 +1,162 @@ #include #include -// TODO Improve the design such that in mixins we wouldn't have to get Globals::CurrentScene -// Maybe pass in the delta time as a parameter or something idk. using namespace J3ML; -void Engine::Movable::MoveX(float speed) { - position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime()); +using namespace Engine; + +float Movable::NormalizeRadians(float radians) const { + radians = std::fmod(radians, Math::Pi * 2); + if (radians < 0) + radians += Math::Pi * 2; + return radians; } -void Engine::Movable::MoveY(float speed) { - position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime()); +void Movable::SetHeading(float new_heading) { + heading = NormalizeRadians(new_heading); } -void Engine::Movable::Move(float angle_rad, float speed) { - position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Cos(angle_rad); - position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Sin(angle_rad); +float Movable::GetHeading() const { + return heading; } -void Engine::Movable::MoveForward(float speed) { - Move(face_angle, speed); +Vector2 Movable::GetPosition() const { + return position; } -void Engine::Movable::MoveBackward(float speed) { - speed = -speed; - position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Cos(face_angle); - position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Sin(face_angle); +float Movable::GetDecay() const { + return decay; } -void Engine::Movable::MoveLeft(float speed) { - position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime()) * -Math::Sin(face_angle); - position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Cos(face_angle); +float Movable::GetVelocity() const { + return velocity; } -void Engine::Movable::MoveRight(float speed) { - position.x = position.x + (speed * Globals::CurrentScene->TickDeltaTime()) * Math::Sin(face_angle); - position.y = position.y + (speed * Globals::CurrentScene->TickDeltaTime()) * -Math::Cos(face_angle); +float Movable::GetFriction() const { + return friction; } -void Engine::Movable::SetRotation(float new_face_angle) { - face_angle = new_face_angle; - - if (face_angle < 0) - face_angle = fmod(face_angle + Math::Pi * 2, Math::Pi * 2); - else if (face_angle >= 2 * M_PI) - face_angle = fmod(face_angle, Math::Pi * 2); +void Movable::SetDecay(float new_decay) { + decay = std::clamp(new_decay, 0.0f, 1.0f); } -void Engine::Movable::Rotate(float speed) { - SetRotation(face_angle + speed * Globals::CurrentScene->TickDeltaTime()); +void Movable::SetFriction(float new_friction) { + friction = std::clamp(new_friction, 0.0f, 1.0f); } -float Engine::Movable::GetRotation() const { +void Movable::SetVelocity(float new_velocity) { + velocity = new_velocity; +} + +void Movable::SetPosition(const Vector2& new_position, bool reset) { + position = new_position; + if (reset) { + heading = 0; + velocity = 0; + friction = 0; + decay = 0; + } +} + +void Movable::SetConstantForceHeading(float heading_rad) { + constant_force_heading = heading_rad; +} + +void Movable::SetConstantForce(float new_velocity) { + constant_force_velocity = new_velocity; +} + +float Movable::GetConstantForceHeading() const { + return constant_force_heading; +} + +float Movable::GetConstantForceVelocity() const { + return constant_force_velocity; +} + +void Movable::SetVisualRotation(float rad_rotation) { + face_angle = rad_rotation; +} + +float Movable::GetVisualRotation() const { return face_angle; } -Vector2 Engine::Movable::GetPosition() const { - return position; -} \ No newline at end of file +void Movable::Move(const u8& tick_rate) { + previous_decay = decay; + previous_velocity = velocity; + previous_heading = heading; + previous_face_angle = face_angle; + previous_constant_force_heading = constant_force_heading; + previous_constant_force_velocity = velocity; + previous_friction = friction; + previous_position = position; + + // Advance. + position.x += ((Math::Cos(heading) * velocity) + (Math::Cos(constant_force_heading) * constant_force_velocity)) * (1.f / tick_rate); + position.y += ((Math::Sin(heading) * velocity) + (Math::Sin(constant_force_heading) * constant_force_velocity)) * (1.f / tick_rate); + + // Apply decay. + velocity = velocity * (1 - decay); + // Apply friction. + velocity = velocity * (1 - friction); +} + +void Movable::AddVelocity(float heading_rad, float vel) { + heading_rad = NormalizeRadians(heading_rad); + + Vector2 v = {velocity * Math::Cos(heading), velocity * Math::Sin(heading)}; + Vector2 add = {vel * Math::Cos(heading_rad), vel * Math::Sin(heading_rad)}; + Vector2 result = {v.x + add.x, v.y + add.y}; + + velocity = Math::Sqrt(result.x * result.x + result.y * result.y); + heading = NormalizeRadians(Math::Atan2(result.y, result.x)); +} + +float Movable::HeadingRelativeForward() const { + return heading; +} + +float Movable::FaceRelativeForward() const { + return face_angle; +} + +float Movable::FaceRelativeBackward() const { + return NormalizeRadians(face_angle + Math::Pi); +} + +float Movable::HeadingRelativeBackward() const { + return NormalizeRadians(heading + Math::Pi); +} + +float Movable::FaceRelativeLeft() const { + return NormalizeRadians(face_angle - Math::HalfPi); +} + +float Movable::HeadingRelativeLeft() const { + return NormalizeRadians(heading - Math::HalfPi); +} + +float Movable::FaceRelativeRight() const { + return NormalizeRadians(face_angle + Math::HalfPi); +} + +float Movable::HeadingRelativeRight() const { + return NormalizeRadians(heading + Math::HalfPi); +} + +float Movable::WorldUp() { + return -Math::Pi / 2; +} + +float Movable::WorldDown() { + return Math::Pi / 2; +} + +float Movable::WorldLeft() { + return Math::Pi; +} + +float Movable::WorldRight() { + return 0; +} + diff --git a/src/Engine/types/entity/Sprite.cpp b/src/Engine/types/entity/Sprite.cpp index b78e20d..8b30789 100644 --- a/src/Engine/types/entity/Sprite.cpp +++ b/src/Engine/types/entity/Sprite.cpp @@ -11,9 +11,9 @@ void Engine::Sprite::Render() { auto* a = GetAlphaMask(); if (t && !a) - return J2D::DrawSprite(t, GetRenderPosition(), face_angle, origin, scale, base_color); + return J2D::DrawSprite(t, GetRenderPosition(), GetVisualRotation(), origin, scale, base_color); if (t && a) - return J2D::DrawSprite(t, a, GetRenderPosition(), face_angle, origin, scale, base_color); + return J2D::DrawSprite(t, a, GetRenderPosition(), GetVisualRotation(), origin, scale, base_color); } Texture* Engine::Sprite::GetTexture() const { @@ -29,7 +29,7 @@ void Engine::Sprite::SetAlphaMask(Texture* new_alpha_mask) { } Vector2 Engine::Sprite::GetRenderPosition() const { - return position - (origin * GetTexture()->GetDimensions()); + return GetPosition() - (origin * GetTexture()->GetDimensions()); } void Engine::Sprite::SetOrigin(const Vector2& new_origin) { diff --git a/src/Engine/types/entity/Trigger.cpp b/src/Engine/types/entity/Trigger.cpp index 021ac4a..d378572 100644 --- a/src/Engine/types/entity/Trigger.cpp +++ b/src/Engine/types/entity/Trigger.cpp @@ -6,12 +6,12 @@ using namespace Engine; void Trigger::Render() { if (ShouldRender()) { auto size = GetBounds().maxPoint - GetBounds().minPoint; - J2D::OutlineRect(Colors::Red, position, size); + J2D::OutlineRect(Colors::Red, GetPosition(), size); } } AABB2D Trigger::GetBounds() const { - return { position, position + size }; + return { GetPosition(), GetPosition() + size }; } diff --git a/src/Game/Entities/Box.cpp b/src/Game/Entities/Box.cpp index 0505462..2531e82 100644 --- a/src/Game/Entities/Box.cpp +++ b/src/Game/Entities/Box.cpp @@ -3,11 +3,14 @@ void Game::Box::Update() { if (Globals::Window->IsKeyDown(Keys::W)) - MoveY(-500); + AddVelocity(WorldUp(), 100); if (Globals::Window->IsKeyDown(Keys::S)) - MoveY(500); + AddVelocity(WorldDown(), 100); if (Globals::Window->IsKeyDown(Keys::A)) - MoveX(-500); + AddVelocity(WorldLeft(), 100); if (Globals::Window->IsKeyDown(Keys::D)) - MoveX(500); + AddVelocity(WorldRight(), 100); + if (Globals::Window->IsKeyDown(Keys::Space)) + AddVelocity(HeadingRelativeBackward(), GetVelocity()); + Move(GetScene()->GetTickRate()); } \ No newline at end of file