Add black hole obstacle
parent
f25a796073
commit
fb506f6f05
|
|
@ -70,6 +70,43 @@ std::shared_ptr<Entity> CreateGravityWell() {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Entity> CreateBlackHole(float x, float y) {
|
||||||
|
auto e = std::make_shared<Entity>();
|
||||||
|
auto &transform = e->AddComponent<TransformComponent>();
|
||||||
|
transform.x = x;
|
||||||
|
transform.y = y;
|
||||||
|
|
||||||
|
auto &scrollable = e->AddComponent<ScrollableComponent>();
|
||||||
|
scrollable.worldX = x;
|
||||||
|
|
||||||
|
auto &well = e->AddComponent<GravityWellComponent>();
|
||||||
|
well.mass = static_cast<float>(1 << 21);
|
||||||
|
well.minDist = 24.0f;
|
||||||
|
well.controlledByMouse = false;
|
||||||
|
well.alwaysActive = true;
|
||||||
|
|
||||||
|
auto &collider = e->AddComponent<ColliderComponent>();
|
||||||
|
collider.radius = 18.0f;
|
||||||
|
|
||||||
|
e->AddComponent<HazardComponent>();
|
||||||
|
|
||||||
|
auto &render = e->AddComponent<RenderComponent>();
|
||||||
|
render.draw = [e]() {
|
||||||
|
auto transform = e->GetComponent<TransformComponent>();
|
||||||
|
if (!transform) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int cx = static_cast<int>(transform->get().x);
|
||||||
|
const int cy = static_cast<int>(transform->get().y);
|
||||||
|
DrawCircle(cx, cy, 18.0f, Color{8, 10, 20, 255});
|
||||||
|
DrawCircleLines(cx, cy, 18.0f, Color{70, 95, 170, 240});
|
||||||
|
DrawCircleLines(cx, cy, 24.0f, Color{95, 120, 200, 120});
|
||||||
|
};
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Entity> CreateStar(float x, float y) {
|
std::shared_ptr<Entity> CreateStar(float x, float y) {
|
||||||
auto e = std::make_shared<Entity>();
|
auto e = std::make_shared<Entity>();
|
||||||
auto &t = e->AddComponent<TransformComponent>();
|
auto &t = e->AddComponent<TransformComponent>();
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
std::shared_ptr<Entity> CreateProbe();
|
std::shared_ptr<Entity> CreateProbe();
|
||||||
std::shared_ptr<Entity> CreateGravityWell();
|
std::shared_ptr<Entity> CreateGravityWell();
|
||||||
|
std::shared_ptr<Entity> CreateBlackHole(float x, float y);
|
||||||
std::shared_ptr<Entity> CreateStar(float x, float y);
|
std::shared_ptr<Entity> CreateStar(float x, float y);
|
||||||
std::shared_ptr<Entity> CreateAsteroid(float x, float y);
|
std::shared_ptr<Entity> CreateAsteroid(float x, float y);
|
||||||
std::shared_ptr<Entity> CreateDebris(float x, float y, float vx, float vy, float ttl = 1.25f);
|
std::shared_ptr<Entity> CreateDebris(float x, float y, float vx, float vy, float ttl = 1.25f);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,11 @@ void GravityWellComponent::Update(float dt) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!controlledByMouse) {
|
||||||
|
active = alwaysActive;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
active = IsMouseButtonDown(MOUSE_BUTTON_LEFT);
|
active = IsMouseButtonDown(MOUSE_BUTTON_LEFT);
|
||||||
|
|
||||||
const Vector2 mouse = GetMousePosition();
|
const Vector2 mouse = GetMousePosition();
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,16 @@ struct GravityWellComponent : public Component {
|
||||||
*/
|
*/
|
||||||
bool active = false;
|
bool active = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, active state is driven by left mouse button and this well follows the cursor.
|
||||||
|
*/
|
||||||
|
bool controlledByMouse = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true and not mouse-controlled, this well applies gravity every frame.
|
||||||
|
*/
|
||||||
|
bool alwaysActive = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lerp factor for how quickly the gravity well follows the mouse position.
|
* Lerp factor for how quickly the gravity well follows the mouse position.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -67,30 +67,20 @@ bool PhysicsComponent::IsInsideNullZone(double xPos) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PhysicsComponent::ComputeWellDeltaVelocity(double px, double py, double dt, double &dvx,
|
bool PhysicsComponent::ComputeGravityDeltaVelocity(double px, double py, double dt, double &dvx,
|
||||||
double &dvy, bool ignoreWellActive) const {
|
double &dvy, bool ignoreWellActive) const {
|
||||||
dvx = 0.0;
|
dvx = 0.0;
|
||||||
dvy = 0.0;
|
dvy = 0.0;
|
||||||
|
|
||||||
if (!context || !context->wellEntity) {
|
if (!context || !context->entities) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wellTransform = context->wellEntity->GetComponent<TransformComponent>();
|
bool probeMeterDepleted = false;
|
||||||
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) {
|
if (context && entity == context->probeEntity && context->statsEntity) {
|
||||||
auto stats = context->statsEntity->GetComponent<StatsComponent>();
|
auto stats = context->statsEntity->GetComponent<StatsComponent>();
|
||||||
if (stats && stats->get().value <= 0.0f) {
|
if (stats && stats->get().value <= 0.0f) {
|
||||||
return false;
|
probeMeterDepleted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,25 +88,49 @@ bool PhysicsComponent::ComputeWellDeltaVelocity(double px, double py, double dt,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const double dx = static_cast<double>(wellTransform->get().x) - px;
|
bool applied = false;
|
||||||
const double dy = static_cast<double>(wellTransform->get().y) - py;
|
for (auto &other : *context->entities) {
|
||||||
const double dist = std::sqrt(dx * dx + dy * dy);
|
if (!other || other.get() == entity) {
|
||||||
if (dist <= 0.0001) {
|
continue;
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
auto wellTransform = other->GetComponent<TransformComponent>();
|
||||||
|
auto wellGravity = other->GetComponent<GravityWellComponent>();
|
||||||
|
if (!wellTransform || !wellGravity) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probeMeterDepleted && wellGravity->get().controlledByMouse) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignoreWellActive && !wellGravity->get().active) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
applied = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const double clampedDist = std::max(dist, static_cast<double>(wellGravity->get().minDist));
|
return applied;
|
||||||
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,
|
void PhysicsComponent::SimulateStep(double &xRef, double &yRef, double &vxRef, double &vyRef,
|
||||||
double dt, bool ignoreWellActive) const {
|
double dt, bool ignoreWellActive) const {
|
||||||
double dvx = 0.0;
|
double dvx = 0.0;
|
||||||
double dvy = 0.0;
|
double dvy = 0.0;
|
||||||
ComputeWellDeltaVelocity(xRef, yRef, dt, dvx, dvy, ignoreWellActive);
|
ComputeGravityDeltaVelocity(xRef, yRef, dt, dvx, dvy, ignoreWellActive);
|
||||||
vxRef += dvx;
|
vxRef += dvx;
|
||||||
vyRef += dvy;
|
vyRef += dvy;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@ struct PhysicsComponent : public Component {
|
||||||
bool IsInsideNullZone(double xPos) const;
|
bool IsInsideNullZone(double xPos) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes velocity delta from well gravity for a position.
|
* Computes summed velocity delta from all gravity sources for a position.
|
||||||
* Returns false when gravity should not apply.
|
* Returns false when no gravity should apply.
|
||||||
*/
|
*/
|
||||||
bool ComputeWellDeltaVelocity(double px, double py, double dt, double &dvx, double &dvy,
|
bool ComputeGravityDeltaVelocity(double px, double py, double dt, double &dvx, double &dvy,
|
||||||
bool ignoreWellActive = false) const;
|
bool ignoreWellActive = false) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs one projected simulation step using well/null-zone rules,
|
* Performs one projected simulation step using well/null-zone rules,
|
||||||
|
|
|
||||||
|
|
@ -33,21 +33,26 @@ void SpawnComponent::Update(float) {
|
||||||
|
|
||||||
while (cursorWX < spawnLimit) {
|
while (cursorWX < spawnLimit) {
|
||||||
const float r = static_cast<float>(GetRandomValue(0, 99));
|
const float r = static_cast<float>(GetRandomValue(0, 99));
|
||||||
if (r < 55.0f) {
|
if (r < 50.0f) {
|
||||||
const float y = static_cast<float>(GetRandomValue(48, GetScreenHeight() - 48));
|
const float y = static_cast<float>(GetRandomValue(48, GetScreenHeight() - 48));
|
||||||
auto star = CreateStar(cursorWX, y);
|
auto star = CreateStar(cursorWX, y);
|
||||||
star->SetContext(context);
|
star->SetContext(context);
|
||||||
context->entities->push_back(star);
|
context->entities->push_back(star);
|
||||||
} else if (r < 88.0f) {
|
} else if (r < 78.0f) {
|
||||||
const float y = static_cast<float>(GetRandomValue(42, GetScreenHeight() - 42));
|
const float y = static_cast<float>(GetRandomValue(42, GetScreenHeight() - 42));
|
||||||
auto asteroid = CreateAsteroid(cursorWX, y);
|
auto asteroid = CreateAsteroid(cursorWX, y);
|
||||||
asteroid->SetContext(context);
|
asteroid->SetContext(context);
|
||||||
context->entities->push_back(asteroid);
|
context->entities->push_back(asteroid);
|
||||||
} else {
|
} else if (r < 95.0f) {
|
||||||
const float width = static_cast<float>(GetRandomValue(70, 140));
|
const float width = static_cast<float>(GetRandomValue(70, 140));
|
||||||
auto zone = CreateNullZone(cursorWX, width);
|
auto zone = CreateNullZone(cursorWX, width);
|
||||||
zone->SetContext(context);
|
zone->SetContext(context);
|
||||||
context->entities->push_back(zone);
|
context->entities->push_back(zone);
|
||||||
|
} else {
|
||||||
|
const float y = static_cast<float>(GetRandomValue(42, GetScreenHeight() - 42));
|
||||||
|
auto blackHole = CreateBlackHole(cursorWX, y);
|
||||||
|
blackHole->SetContext(context);
|
||||||
|
context->entities->push_back(blackHole);
|
||||||
}
|
}
|
||||||
|
|
||||||
const float gap =
|
const float gap =
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue