/
del00n
/
Space_Lab
Обзор
Документация
Войти
/
del00n
/
Space_Lab
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Engine.h
701 строка
24 KB
del00n
upload files
22 янв 2026, 18:05
Верифицирован
22 янв 2026, 18:05
11207a5
Код
Авторство
О чём код?
#pragma once #include <SFML/Graphics.hpp> #include <vector> #include <cmath> #include <algorithm> #include <string> #include <utility> struct Vec2 { float x{}, y{}; Vec2() = default; Vec2(float X, float Y) : x(X), y(Y) {} Vec2 operator+(const Vec2& o) const { return { x + o.x, y + o.y }; } Vec2 operator-(const Vec2& o) const { return { x - o.x, y - o.y }; } Vec2 operator*(float k) const { return { x * k, y * k }; } Vec2 operator/(float k) const { return { x / k, y / k }; } Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; } float len() const { return std::sqrt(x * x + y * y); } Vec2 norm() const { float l = len(); return l ? (*this) / l : Vec2(); } float dot(const Vec2& o) const { return x * o.x + y * o.y; } }; class Body { public: Body(Vec2 p, float m, float r) : pos(p), mass(m), radius(r) {} const Vec2& getPos() const { return pos; } float getMass() const { return mass; } float getRadius() const { return radius; } void draw(sf::RenderWindow& w) const { sf::CircleShape s(radius); s.setOrigin(radius, radius); s.setPosition(pos.x, pos.y); s.setFillColor({ 120,120,255 }); w.draw(s); } private: Vec2 pos; float mass, radius; }; class Spacecraft { public: explicit Spacecraft(Vec2 p) : pos(p), rotation(0.f) {} void setMass(float m) { mass = m; } float getMass() const { return mass; } bool isCrashed() const { return crashed; } bool isOutOfBounds() const { return outOfBounds; } float getMaxThrust() const { return maxThrust; } float getFuel() const { return fuel; } const Vec2& getPos() const { return pos; } const Vec2& getVel() const { return vel; } float getRotation() const { return rotation; } void setMaxThrust(float t) { maxThrust = t; } void applyForce(const Vec2& F, float dt) { Vec2 a = F / mass; vel += a * dt; pos += vel * dt; } void burnFuel(float thrustLen, float dt) { const float k = 0.02f; fuel -= thrustLen * k * dt; if (fuel < 0) fuel = 0; } void setRotation(float r) { rotation = r; } void markCrashed() { crashed = true; } void markOutOfBounds() { outOfBounds = true; } void draw(sf::RenderWindow& w) const { sf::ConvexShape ship(4); ship.setPoint(0, { 0.f, -10.f }); ship.setPoint(1, { 6.f, 6.f }); ship.setPoint(2, { 0.f, 3.f }); ship.setPoint(3, { -6.f, 6.f }); ship.setFillColor({ 240,240,240 }); ship.setOutlineThickness(1.f); ship.setOutlineColor(sf::Color::Black); ship.setPosition(pos.x, pos.y); ship.setRotation(rotation); w.draw(ship); } private: Vec2 pos, vel{}; float rotation = 0.f; float mass = 5.f, maxThrust = 300.f; float fuel = 400.f; bool crashed = false; bool outOfBounds = false; }; struct TrajectoryPoint { Vec2 pos; float time; }; static constexpr float SCENE_WIDTH = 800.f; static constexpr float SCENE_HEIGHT = 600.f; class Autopilot { public: Vec2 start{}, finish{}; float Tfly = 40.f; int numWaypoints = 5; std::vector<Vec2> waypoints; int currentWaypoint = 0; float Kp = 400.0f; float Ki = 80.0f; float Kd = 60.0f; float baseTargetVel = 100.0f; float minTargetVel = 35.0f; float maxTargetVel = 400.0f; float gravityThreshold = 15.0f; float currentAdaptiveVel = 100.0f; float baseMaxThrust = 150.0f; float currentMaxThrust = 150.0f; float maxSafeVel = 60.0f; float emergencyBrakePower = 500.0f; float rotationSpeed = 180.0f; bool useOptimalTrajectory = true; std::vector<TrajectoryPoint> predictedTrajectory; Vec2 integral_error{}; Vec2 prev_error{}; bool landingSequenceStarted = false; void setCourse(Vec2 A, Vec2 B, float T, int waypointCount = 5) { start = A; finish = B; Tfly = T; numWaypoints = std::clamp(waypointCount, 2, 10); currentWaypoint = 0; integral_error = {}; prev_error = {}; currentMaxThrust = baseMaxThrust; landingSequenceStarted = false; } static bool isPointSafe(Vec2 pos, const std::vector<Body>& bodies) { const float SAFETY_DIST = 100.f; const float BOUNDARY_MARGIN = 50.f; if (pos.x < BOUNDARY_MARGIN || pos.x > SCENE_WIDTH - BOUNDARY_MARGIN || pos.y < BOUNDARY_MARGIN || pos.y > SCENE_HEIGHT - BOUNDARY_MARGIN) { return false; } for (const auto& b : bodies) { if ((pos - b.getPos()).len() < SAFETY_DIST + b.getRadius()) { return false; } } return true; } static bool isPathSafe(Vec2 a, Vec2 b, const std::vector<Body>& bodies) { const int CHECKS = 20; for (int i = 0; i <= CHECKS; ++i) { Vec2 check = a + (b - a) * (i / float(CHECKS)); if (!isPointSafe(check, bodies)) { return false; } } return true; } static float calculatePathLength(const std::vector<Vec2>& path) { float length = 0.f; for (size_t i = 1; i < path.size(); ++i) { length += (path[i] - path[i-1]).len(); } return length; } void optimizeWaypoints(const std::vector<Body>& bodies) { if (!useOptimalTrajectory) return; waypoints.clear(); waypoints.push_back(start); std::vector<std::vector<Vec2>> candidates(std::max(0, numWaypoints - 2)); for (int wp = 1; wp < numWaypoints - 1; ++wp) { float t = wp / float(numWaypoints - 1); Vec2 centerLine = start + (finish - start) * t; for (float offset = -150; offset <= 150; offset += 50) { Vec2 perpDir = (finish - start).norm(); perpDir = Vec2(-perpDir.y, perpDir.x); Vec2 candidate = centerLine + perpDir * offset; if (isPointSafe(candidate, bodies)) { candidates[wp - 1].push_back(candidate); } } } for (int wp = 1; wp < numWaypoints - 1; ++wp) { if (!candidates[wp - 1].empty()) { float t = wp / float(numWaypoints - 1); Vec2 centerLine = start + (finish - start) * t; Vec2 best = candidates[wp - 1][0]; float bestDist = (best - centerLine).len(); for (const auto& cand : candidates[wp - 1]) { float dist = (cand - centerLine).len(); if (dist < bestDist) { bestDist = dist; best = cand; } } waypoints.push_back(best); } else { float t = wp / float(numWaypoints - 1); waypoints.push_back(start + (finish - start) * t); } } waypoints.push_back(finish); predictedTrajectory.clear(); for (size_t i = 0; i < waypoints.size(); ++i) { predictedTrajectory.push_back({waypoints[i], (float)i}); } } float computeAdaptiveTargetVel(Vec2 shipPos, const std::vector<Body>& bodies, float shipMass, float G) { float totalGravityAccel = 0.0f; for (const auto& b : bodies) { Vec2 d = b.getPos() - shipPos; float r2 = d.x * d.x + d.y * d.y; if (r2 > 1e-3f) { float r = std::sqrt(r2); float accel = G * b.getMass() / r2; totalGravityAccel += accel; } } float adaptiveVel = baseTargetVel; if (totalGravityAccel > gravityThreshold) { float gravityFactor = totalGravityAccel / (gravityThreshold * 4.0f); gravityFactor = std::min(gravityFactor, 1.0f); adaptiveVel = baseTargetVel * (1.0f - gravityFactor * 0.5f); } else { float gravityFactor = totalGravityAccel / gravityThreshold; adaptiveVel = baseTargetVel + (maxTargetVel - baseTargetVel) * gravityFactor * 0.7f; } adaptiveVel = std::max(minTargetVel, std::min(maxTargetVel, adaptiveVel)); const float alpha = 0.6f; currentAdaptiveVel = currentAdaptiveVel * (1.0f - alpha) + adaptiveVel * alpha; return currentAdaptiveVel; } void updateDynamicThrust(float deviation) { if (deviation > 10.0f) { currentMaxThrust = baseMaxThrust * 1.2f; } else if (deviation > 5.0f) { currentMaxThrust = baseMaxThrust * 1.08f; } else if (deviation > 2.0f) { currentMaxThrust = baseMaxThrust * 1.01f; } else { currentMaxThrust = baseMaxThrust; } } Vec2 getClosestPointOnLine(Vec2 shipPos, Vec2 lineStart, Vec2 lineEnd) const { Vec2 lineVec = lineEnd - lineStart; Vec2 shipVec = shipPos - lineStart; float lineLen2 = lineVec.dot(lineVec); if (lineLen2 < 1e-6f) return lineStart; float t = shipVec.dot(lineVec) / lineLen2; t = std::max(0.0f, std::min(1.0f, t)); return lineStart + lineVec * t; } float getDistanceToLine(Vec2 shipPos, Vec2 lineStart, Vec2 lineEnd) const { Vec2 closest = getClosestPointOnLine(shipPos, lineStart, lineEnd); return (shipPos - closest).len(); } static float normalizeAngle(float angle) { while (angle > 180.0f) angle -= 360.0f; while (angle < -180.0f) angle += 360.0f; return angle; } float getDesiredRotation(Vec2 thrustDir) const { if (thrustDir.len() < 0.01f) return 0.0f; float thrustAngle = std::atan2(thrustDir.y, thrustDir.x) * 180.0f / 3.14159f - 90.0f + 180.0f; return thrustAngle; } std::pair<Vec2, float> computeThrustAndRotation(Vec2 shipPos, Vec2 shipVel, float maxThrust, float mass, const std::vector<Body>& bodies, float G, float dt) { if (currentWaypoint >= (int)waypoints.size() - 1 && currentWaypoint > 0) { Vec2 finalPlanet = waypoints.back(); Vec2 toFinish = finalPlanet - shipPos; float distToFinish = toFinish.len(); float currentSpeed = shipVel.len(); const float LANDING_START_DIST = 150.0f; if (distToFinish < LANDING_START_DIST && currentSpeed < 50.0f) { landingSequenceStarted = true; Vec2 thrustDir = toFinish.norm(); Vec2 brakingThrust = thrustDir * (currentMaxThrust * 0.3f); float desiredRot = getDesiredRotation(brakingThrust); return { brakingThrust, desiredRot }; } else if (distToFinish < LANDING_START_DIST) { Vec2 brakingForce = shipVel.norm() * (-emergencyBrakePower * 0.5f); return { brakingForce, getDesiredRotation(brakingForce) }; } } float currentSpeed = shipVel.len(); if (currentSpeed > maxSafeVel) { Vec2 brakingForce = shipVel.norm() * (-emergencyBrakePower); return { brakingForce, getDesiredRotation(brakingForce) }; } if (currentWaypoint >= (int)waypoints.size()) { return { Vec2(), 0.0f }; } while (currentWaypoint + 1 < (int)waypoints.size()) { Vec2 currentWP = waypoints[currentWaypoint]; Vec2 nextWP = waypoints[currentWaypoint + 1]; Vec2 toShip = shipPos - currentWP; Vec2 wpDir = nextWP - currentWP; float projection = toShip.dot(wpDir.norm()); float wpDist = wpDir.len(); if (projection > wpDist) { currentWaypoint++; } else { break; } } Vec2 currentWP = waypoints[currentWaypoint]; Vec2 nextWP = (currentWaypoint + 1 < (int)waypoints.size()) ? waypoints[currentWaypoint + 1] : waypoints[currentWaypoint]; float distToCurrentWP = (shipPos - currentWP).len(); float distToLine = getDistanceToLine(shipPos, currentWP, nextWP); if (distToLine < distToCurrentWP && distToCurrentWP > 20.0f) { Vec2 closestPointOnLine = getClosestPointOnLine(shipPos, currentWP, nextWP); Vec2 lineDir = (nextWP - currentWP).norm(); float projDist = (closestPointOnLine - currentWP).len(); float lineLen = (nextWP - currentWP).len(); if (projDist >= 0 && projDist <= lineLen) { Vec2 errorPos = closestPointOnLine - shipPos; float distToLineVal = errorPos.len(); Vec2 dirToTarget = (distToLineVal > 0.01f) ? errorPos.norm() : lineDir; updateDynamicThrust(distToLineVal); Vec2 P = dirToTarget * Kp * distToLineVal; integral_error += errorPos * 0.016f; integral_error = integral_error.norm() * std::min(integral_error.len(), 500.0f); Vec2 I = integral_error * Ki; Vec2 deriv_error = (errorPos - prev_error) / 0.016f; Vec2 D = deriv_error * Kd; prev_error = errorPos; Vec2 pid_force = P + I + D; if (distToLineVal > 3.0f) { float aggression = 1.0f + std::min(distToLineVal / 10.0f, 2.0f); pid_force = pid_force * aggression; } float forceLen = pid_force.len(); if (forceLen > currentMaxThrust) { pid_force = pid_force.norm() * currentMaxThrust; } float desiredRotation = getDesiredRotation(pid_force); return { pid_force, desiredRotation }; } } Vec2 closestPointOnLine = getClosestPointOnLine(shipPos, currentWP, nextWP); Vec2 lineDir = (nextWP - currentWP).norm(); Vec2 errorPos = closestPointOnLine - shipPos; distToLine = errorPos.len(); Vec2 dirToTarget = lineDir; if (distToLine > 5.0f) { dirToTarget = errorPos.norm(); } updateDynamicThrust(distToLine); Vec2 P = dirToTarget * Kp * distToLine; integral_error += errorPos * 0.016f; integral_error = integral_error.norm() * std::min(integral_error.len(), 500.0f); Vec2 I = integral_error * Ki; Vec2 deriv_error = (errorPos - prev_error) / 0.016f; Vec2 D = deriv_error * Kd; prev_error = errorPos; Vec2 pid_force = P + I + D; if (distToLine > 3.0f) { float aggression = 1.0f + std::min(distToLine / 10.0f, 2.0f); pid_force = pid_force * aggression; } float forceLen = pid_force.len(); if (forceLen > currentMaxThrust) { pid_force = pid_force.norm() * currentMaxThrust; } float desiredRotation = getDesiredRotation(pid_force); return { pid_force, desiredRotation }; } Vec2 computeThrust(Vec2 shipPos, Vec2 shipVel, float maxThrust, float mass, const std::vector<Body>& bodies, float G) { auto [force, _] = computeThrustAndRotation(shipPos, shipVel, maxThrust, mass, bodies, G, 0.016f); return force; } }; class World { public: void addBody(const Body& b) { bodies.push_back(b); } void setShip(const Spacecraft& s) { ship = s; } Spacecraft& getShip() { return ship; } const std::vector<Body>& getBodies() const { return bodies; } const Vec2& lastFg() const { return Fg_last; } const Vec2& lastFth() const { return Fth_last; } bool hasReachedFinish() const { return reachedFinish; } bool isLandingSequence() const { return ap_ref ? ap_ref->landingSequenceStarted : false; } const std::vector<TrajectoryPoint>& getPredictedTrajectory() const { static const std::vector<TrajectoryPoint> empty; return ap_ref ? ap_ref->predictedTrajectory : empty; } Autopilot* ap_ref = nullptr; void update(Autopilot& ap, float dt, float simTime) { ap_ref = ≈ static bool optimized = false; if (!optimized && simTime < 0.1f) { ap.optimizeWaypoints(bodies); optimized = true; } Fg_last = {}; for (auto& b : bodies) { Vec2 d = b.getPos() - ship.getPos(); float r2 = d.x * d.x + d.y * d.y; if (r2 < 1e-3f) continue; Fg_last += d.norm() * (G * shipMass * b.getMass() / r2); } auto [thrustVec, desiredRot] = ap.computeThrustAndRotation(ship.getPos(), ship.getVel(), ship.getMaxThrust(), shipMass, bodies, G, dt); Fth_last = thrustVec; if (ship.getFuel() <= 0) Fth_last = {}; ship.burnFuel(Fth_last.len(), dt); ship.applyForce(Fg_last + Fth_last, dt); float currentRot = ship.getRotation(); float rotDiff = desiredRot - currentRot; rotDiff = Autopilot::normalizeAngle(rotDiff); float maxRotChange = ap.rotationSpeed * dt; float rotChange = std::max(-maxRotChange, std::min(maxRotChange, rotDiff)); ship.setRotation(currentRot + rotChange); for (auto& b : bodies) if ((ship.getPos() - b.getPos()).len() < b.getRadius()) ship.markCrashed(); if (ship.getPos().x < 0 || ship.getPos().x > SCENE_WIDTH || ship.getPos().y < 0 || ship.getPos().y > SCENE_HEIGHT) { ship.markOutOfBounds(); } Vec2 toFinish = ap.finish - ship.getPos(); float distToFinish = toFinish.len(); float speedToFinish = ship.getVel().dot(toFinish.norm()); if (distToFinish < 30.0f && speedToFinish < 20.0f) { reachedFinish = true; } } void updateManual(Autopilot& ap, float dt, float simTime, const Vec2& engineThrust, float desiredRot, const Vec2& externalForce = {}) { ap_ref = ≈ Fg_last = {}; for (auto& b : bodies) { Vec2 d = b.getPos() - ship.getPos(); float r2 = d.x * d.x + d.y * d.y; if (r2 < 1e-3f) continue; Fg_last += d.norm() * (G * shipMass * b.getMass() / r2); } Fth_last = engineThrust; if (ship.getFuel() <= 0) Fth_last = {}; ship.burnFuel(Fth_last.len(), dt); ship.applyForce(Fg_last + externalForce + Fth_last, dt); float currentRot = ship.getRotation(); float rotDiff = desiredRot - currentRot; rotDiff = Autopilot::normalizeAngle(rotDiff); float maxRotChange = ap.rotationSpeed * dt; float rotChange = std::max(-maxRotChange, std::min(maxRotChange, rotDiff)); ship.setRotation(currentRot + rotChange); for (auto& b : bodies) if ((ship.getPos() - b.getPos()).len() < b.getRadius()) ship.markCrashed(); if (ship.getPos().x < 0 || ship.getPos().x > SCENE_WIDTH || ship.getPos().y < 0 || ship.getPos().y > SCENE_HEIGHT) { ship.markOutOfBounds(); } Vec2 toFinish = ap.finish - ship.getPos(); float distToFinish = toFinish.len(); float speedToFinish = ship.getVel().dot(toFinish.norm()); if (distToFinish < 30.0f && speedToFinish < 20.0f) { reachedFinish = true; } } void drawBodies(sf::RenderWindow& w, sf::Font& font) const { for (size_t i = 0; i < bodies.size(); ++i) { bodies[i].draw(w); sf::Text num; num.setFont(font); num.setString(std::to_string(i + 1)); num.setCharacterSize(22); num.setFillColor(sf::Color::White); num.setStyle(sf::Text::Bold); sf::FloatRect bounds = num.getLocalBounds(); num.setOrigin(bounds.width / 2.f, bounds.height / 2.f); num.setPosition(bodies[i].getPos().x, bodies[i].getPos().y - 8); w.draw(num); } } private: std::vector<Body> bodies; Spacecraft ship{ {0,0} }; Vec2 Fg_last{}, Fth_last{}; const float G = 100.f, shipMass = 5.f; bool reachedFinish = false; }; class LandingScene { public: struct LandingState { enum State { DESCENDING, LANDED, CRASHED }; State state = DESCENDING; std::string message = ""; }; LandingScene(Vec2 planetPos, float planetRadius, Spacecraft initShip) : planet(planetPos, 100.0f, planetRadius), ship(initShip) { ship.setRotation(180.0f); } void update(float dt) { if (landingState.state != LandingState::DESCENDING) return; Vec2 toShip = ship.getPos() - planet.getPos(); float distToPlanet = toShip.len(); Vec2 gravityForce{}; if (distToPlanet > planet.getRadius() + 5.0f) { float gravAccel = 150.0f / (distToPlanet * distToPlanet); gravityForce = toShip.norm() * (-gravAccel * ship.getMass()); } Vec2 thrustDir = toShip.norm(); float currentSpeed = ship.getVel().len(); float targetSpeed = std::max(0.0f, (distToPlanet - planet.getRadius() - 30.0f) * 0.5f); float speedError = currentSpeed - targetSpeed; float thrustMagnitude = 0.0f; if (speedError > 2.0f) { thrustMagnitude = std::min(speedError * 50.0f, 300.0f); } else if (distToPlanet > planet.getRadius() + 40.0f) { thrustMagnitude = 50.0f; } Vec2 thrustForce = thrustDir * thrustMagnitude; ship.applyForce(gravityForce + thrustForce, dt); ship.burnFuel(thrustMagnitude, dt); if (distToPlanet <= planet.getRadius() + 5.0f) { if (currentSpeed < 15.0f) { landingState.state = LandingState::LANDED; landingState.message = "SUCCESSFUL LANDING!"; } else { landingState.state = LandingState::CRASHED; landingState.message = "CRASH! Speed too high: " + std::to_string((int)currentSpeed) + " m/s"; } } if (ship.getPos().x < 0 || ship.getPos().x > SCENE_WIDTH || ship.getPos().y < 0 || ship.getPos().y > SCENE_HEIGHT) { landingState.state = LandingState::CRASHED; landingState.message = "LEFT LANDING ZONE"; } } void draw(sf::RenderWindow& w, sf::Font& font) { sf::CircleShape planetShape(planet.getRadius()); planetShape.setOrigin(planet.getRadius(), planet.getRadius()); planetShape.setPosition(planet.getPos().x, planet.getPos().y); planetShape.setFillColor(sf::Color(100, 150, 100)); planetShape.setOutlineColor(sf::Color::Green); planetShape.setOutlineThickness(2.0f); w.draw(planetShape); ship.draw(w); sf::Text statusText; statusText.setFont(font); statusText.setCharacterSize(20); statusText.setPosition(10, 10); if (landingState.state == LandingState::DESCENDING) { float dist = (ship.getPos() - planet.getPos()).len() - planet.getRadius(); statusText.setString("DESCENDING...\nDistance: " + std::to_string((int)std::max(0.0f, dist)) + " m\n" + "Speed: " + std::to_string((int)ship.getVel().len()) + " m/s\n" + "Fuel: " + std::to_string((int)ship.getFuel()) + "%"); statusText.setFillColor(sf::Color::Yellow); } else if (landingState.state == LandingState::LANDED) { statusText.setString(landingState.message); statusText.setFillColor(sf::Color::Green); } else { statusText.setString(landingState.message); statusText.setFillColor(sf::Color::Red); } w.draw(statusText); } bool isFinished() const { return landingState.state != LandingState::DESCENDING; } bool isSuccessful() const { return landingState.state == LandingState::LANDED; } const Spacecraft& getShip() const { return ship; } LandingState getLandingState() const { return landingState; } private: Body planet; Spacecraft ship; LandingState landingState; };