Smoother movement
Interpolation & Extrapolation still todo.
This commit is contained in:
@@ -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:
|
||||
|
@@ -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) {}
|
||||
};
|
@@ -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); }
|
||||
};
|
@@ -177,3 +177,7 @@ unsigned long Scene::LastTickTime() const {
|
||||
return last_tick_time;
|
||||
}
|
||||
|
||||
u8 Scene::GetTickRate() const {
|
||||
return tick_rate;
|
||||
}
|
||||
|
||||
|
@@ -1,59 +1,162 @@
|
||||
#include <Engine/types/entity/mixins/Movable.h>
|
||||
#include <Engine/Globals.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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 };
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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());
|
||||
}
|
Reference in New Issue
Block a user