cs381/as6/components/PhysicsComponent.cpp

147 lines
4.6 KiB
C++

#include "components/PhysicsComponent.hpp"
#include "Entity.hpp"
#include "components/GravityWellComponent.hpp"
#include "components/NullZoneComponent.hpp"
#include "components/StatsComponent.hpp"
#include "components/TransformComponent.hpp"
#include <algorithm>
#include <cmath>
void PhysicsComponent::Setup() {}
void PhysicsComponent::ClampVelocity(float &vxRef, float &vyRef) const {
const float speed = std::sqrt(vxRef * vxRef + vyRef * vyRef);
if (speed > speedCap && speed > 0.0f) {
const float scale = speedCap / speed;
vxRef *= scale;
vyRef *= scale;
}
}
void PhysicsComponent::ClampVelocity(double &vxRef, double &vyRef) const {
const double speed = std::sqrt(vxRef * vxRef + vyRef * vyRef);
if (speed > static_cast<double>(speedCap) && speed > 0.0) {
const double scale = static_cast<double>(speedCap) / speed;
vxRef *= scale;
vyRef *= scale;
}
}
void PhysicsComponent::IntegratePosition(float &xRef, float &yRef, float vxValue, float vyValue,
float dt) {
xRef += vxValue * dt;
yRef += vyValue * dt;
}
void PhysicsComponent::IntegratePosition(double &xRef, double &yRef, double vxValue, double vyValue,
double dt) {
xRef += vxValue * dt;
yRef += vyValue * dt;
}
bool PhysicsComponent::IsInsideNullZone(double xPos) const {
if (!context || !context->entities) {
return false;
}
for (auto &other : *context->entities) {
if (!other || other.get() == entity) {
continue;
}
auto zone = other->GetComponent<NullZoneComponent>();
auto zoneTransform = other->GetComponent<TransformComponent>();
if (!zone || !zoneTransform) {
continue;
}
const double left = static_cast<double>(zoneTransform->get().x);
const double right = left + static_cast<double>(zone->get().width);
if (xPos >= left && xPos <= right) {
return true;
}
}
return false;
}
bool PhysicsComponent::ComputeWellDeltaVelocity(double px, double py, double dt, double &dvx,
double &dvy, bool ignoreWellActive) const {
dvx = 0.0;
dvy = 0.0;
if (!context || !context->wellEntity) {
return false;
}
auto wellTransform = context->wellEntity->GetComponent<TransformComponent>();
auto wellGravity = context->wellEntity->GetComponent<GravityWellComponent>();
if (!wellTransform || !wellGravity) {
return false;
}
if (!ignoreWellActive && !wellGravity->get().active) {
return false;
}
// Probe cannot receive well acceleration when meter is depleted.
if (context && entity == context->probeEntity && context->statsEntity) {
auto stats = context->statsEntity->GetComponent<StatsComponent>();
if (stats && stats->get().value <= 0.0f) {
return false;
}
}
if (IsInsideNullZone(px)) {
return false;
}
const double dx = static_cast<double>(wellTransform->get().x) - px;
const double dy = static_cast<double>(wellTransform->get().y) - py;
const double dist = std::sqrt(dx * dx + dy * dy);
if (dist <= 0.0001) {
return false;
}
const double clampedDist = std::max(dist, static_cast<double>(wellGravity->get().minDist));
const double force = static_cast<double>(wellGravity->get().mass) / (clampedDist * clampedDist);
dvx = (dx / dist) * force * dt;
dvy = (dy / dist) * force * dt;
return true;
}
void PhysicsComponent::SimulateStep(double &xRef, double &yRef, double &vxRef, double &vyRef,
double dt, bool ignoreWellActive) const {
double dvx = 0.0;
double dvy = 0.0;
ComputeWellDeltaVelocity(xRef, yRef, dt, dvx, dvy, ignoreWellActive);
vxRef += dvx;
vyRef += dvy;
ClampVelocity(vxRef, vyRef);
IntegratePosition(xRef, yRef, vxRef, vyRef, dt);
}
void PhysicsComponent::Update(float dt) {
auto transform = entity->GetComponent<TransformComponent>();
if (!transform) {
return;
}
double x = static_cast<double>(transform->get().x);
double y = static_cast<double>(transform->get().y);
double vxLocal = static_cast<double>(vx);
double vyLocal = static_cast<double>(vy);
SimulateStep(x, y, vxLocal, vyLocal, static_cast<double>(dt), false);
transform->get().x = static_cast<float>(x);
transform->get().y = static_cast<float>(y);
vx = static_cast<float>(vxLocal);
vy = static_cast<float>(vyLocal);
}
void PhysicsComponent::Cleanup() {}