- gf25051 的博客
《咸鱼概要 · 游戏》行星绕日
- @ 2026-7-23 14:26:28
运行源代码需要EasyX库,运行exe不用
#include <graphics.h>
#include <bits/stdc++.h>
#include <windows.h>
#include <stdio.h>
using namespace std;
struct Planet {
double x, y;
double vx, vy;
double radius;
double mass;
COLORREF color;
vector<double> trailX;
vector<double> trailY;
bool destroyed;
bool isAsteroid;
};
struct Sun {
double x, y;
double radius;
double mass;
COLORREF color;
};
struct BlackHole {
double x, y;
double radius;
double mass;
bool active;
COLORREF color;
};
const int WIDTH = 800;
const int HEIGHT = 600;
Sun sun;
vector<Planet> planets;
vector<BlackHole> blackHoles;
double G;
double timeStep = 0.0001;
double timeScale = 50.0;
double scale = 0.3;
double mapScale = 1.0;
double viewOffsetX = 0;
double viewOffsetY = 0;
int planetCount = 7;
const double SOFTENING = 0.1;
bool menuOpen = false;
bool manualOpen = false;
bool paused = false;
int selectedPlanet = -1;
bool placingBlackHole = false;
const int MAX_TRAIL = 10000000;
const int MAX_BLACK_HOLES = 100;
void initSun() {
sun.x = WIDTH / 2;
sun.y = HEIGHT / 2;
sun.radius = 25 * scale;
sun.mass = 10000.0;
sun.color = YELLOW;
}
void initBlackHoles() {
blackHoles.clear();
}
void initPlanets() {
planets.clear();
struct PlanetData {
double dist;
double radius;
double mass;
COLORREF color;
double angle;
double eccentricity;
double angleOffset;
};
PlanetData data[] = {
{100 * scale, 4 * scale, 100.0, RGB(200, 50, 50), 0, 0.0, 0.0},
{140 * scale, 5 * scale, 150.0, RGB(50, 200, 50), 1.2, 0.02, 0.3},
{180 * scale, 5 * scale, 200.0, RGB(50, 50, 200), 2.5, -0.01, -0.2},
{220 * scale, 6 * scale, 250.0, RGB(200, 200, 50), 3.8, 0.015, 0.5},
{260 * scale, 6 * scale, 300.0, RGB(200, 50, 200), 4.9, -0.02, -0.4},
{300 * scale, 7 * scale, 400.0, RGB(50, 200, 200), 0.7, 0.01, 0.6},
{340 * scale, 7 * scale, 500.0, RGB(200, 150, 50), 1.8, -0.015, -0.3},
{120 * scale, 4 * scale, 110.0, RGB(255, 100, 100), 3.5, 0.02, 0.1},
{160 * scale, 5 * scale, 180.0, RGB(100, 255, 100), 4.2, -0.02, -0.1},
{200 * scale, 5 * scale, 220.0, RGB(100, 100, 255), 1.0, 0.025, 0.4},
{240 * scale, 6 * scale, 280.0, RGB(255, 255, 100), 2.8, -0.015, -0.5},
{280 * scale, 6 * scale, 350.0, RGB(255, 100, 255), 4.0, 0.02, 0.3},
{320 * scale, 7 * scale, 450.0, RGB(100, 255, 255), 5.5, -0.01, -0.2},
{360 * scale, 7 * scale, 550.0, RGB(255, 200, 100), 1.3, 0.025, 0.5},
{130 * scale, 4 * scale, 130.0, RGB(255, 150, 150), 4.5, -0.03, 0.1},
{170 * scale, 5 * scale, 160.0, RGB(150, 255, 150), 0.8, 0.02, -0.3},
{210 * scale, 5 * scale, 210.0, RGB(150, 150, 255), 5.8, -0.015, 0.6},
{250 * scale, 6 * scale, 270.0, RGB(255, 255, 150), 3.0, 0.025, -0.4},
{290 * scale, 6 * scale, 330.0, RGB(255, 150, 255), 1.0, -0.02, 0.2},
{330 * scale, 7 * scale, 430.0, RGB(150, 255, 255), 4.0, 0.01, -0.5},
{370 * scale, 7 * scale, 580.0, RGB(255, 220, 150), 2.5, -0.025, 0.4},
{110 * scale, 4 * scale, 120.0, RGB(200, 100, 200), 5.2, 0.02, -0.1},
{155 * scale, 5 * scale, 140.0, RGB(100, 200, 100), 1.8, -0.025, 0.5},
{195 * scale, 5 * scale, 190.0, RGB(100, 100, 200), 4.8, 0.015, -0.2},
{230 * scale, 6 * scale, 240.0, RGB(200, 200, 100), 0.5, -0.02, 0.3},
{270 * scale, 6 * scale, 300.0, RGB(200, 100, 200), 3.7, 0.025, -0.6},
{310 * scale, 7 * scale, 420.0, RGB(100, 200, 200), 5.9, -0.015, 0.1},
{350 * scale, 7 * scale, 530.0, RGB(200, 180, 100), 1.2, 0.02, -0.3},
{380 * scale, 8 * scale, 600.0, RGB(255, 180, 180), 4.5, -0.02, 0.5},
{400 * scale, 8 * scale, 650.0, RGB(180, 255, 180), 2.8, 0.015, -0.4}
};
int count = (planetCount > 30) ? 30 : planetCount;
if (count < 1) count = 7;
for (int i = 0; i < count; i++) {
Planet p;
double angle = data[i].angle;
double r = data[i].dist;
double eccentricity = data[i].eccentricity;
p.x = sun.x + r * cos(angle);
p.y = sun.y + r * sin(angle);
double circularSpeed = sqrt(G * sun.mass / r);
double speedFactor = 1.0 + eccentricity * 0.3;
if (speedFactor < 0.85) speedFactor = 0.85;
if (speedFactor > 1.15) speedFactor = 1.15;
double v = circularSpeed * speedFactor;
p.vx = -v * sin(angle + data[i].angleOffset * 0.15);
p.vy = v * cos(angle + data[i].angleOffset * 0.15);
p.radius = data[i].radius;
p.mass = data[i].mass;
p.color = data[i].color;
p.trailX.reserve(10000);
p.trailY.reserve(10000);
p.trailX.clear();
p.trailY.clear();
p.destroyed = false;
p.isAsteroid = false;
planets.push_back(p);
}
}
void spawnAsteroid() {
if ((int)planets.size() >= 50) return;
Planet a;
a.isAsteroid = true;
a.radius = 2 * scale;
a.mass = 10.0;
a.color = RGB(150, 150, 150);
a.destroyed = false;
a.trailX.reserve(10000);
a.trailY.reserve(10000);
a.trailX.clear();
a.trailY.clear();
double angle = (rand() % 360) * 3.14159 / 180.0;
double dist = 150 * scale + (rand() % 300) * scale;
a.x = sun.x + dist * cos(angle);
a.y = sun.y + dist * sin(angle);
double circularSpeed = sqrt(G * sun.mass / dist);
double speed = circularSpeed * (0.6 + (rand() % 80) / 100.0);
double dirAngle = atan2(sun.y - a.y, sun.x - a.x) + (rand() % 60 - 30) * 3.14159 / 180.0;
a.vx = speed * cos(dirAngle);
a.vy = speed * sin(dirAngle);
planets.push_back(a);
}
void handleCollisions() {
int n = planets.size();
for (int i = 0; i < n; i++) {
if (planets[i].destroyed) continue;
for (int j = i + 1; j < n; j++) {
if (planets[j].destroyed) continue;
Planet& a = planets[i];
Planet& b = planets[j];
double dx = b.x - a.x;
double dy = b.y - a.y;
double distSq = dx * dx + dy * dy;
double minDist = a.radius + b.radius;
if (distSq < minDist * minDist && distSq > 0) {
double dist = sqrt(distSq);
double nx = dx / dist;
double ny = dy / dist;
double overlap = minDist - dist;
a.x -= nx * overlap * 0.5;
a.y -= ny * overlap * 0.5;
b.x += nx * overlap * 0.5;
b.y += ny * overlap * 0.5;
double dvx = a.vx - b.vx;
double dvy = a.vy - b.vy;
double dvn = dvx * nx + dvy * ny;
if (dvn < 0) {
double restitution = 0.3;
double invMass = 1.0 / a.mass + 1.0 / b.mass;
double impulse = (1 + restitution) * dvn / invMass;
a.vx -= impulse * nx / a.mass;
a.vy -= impulse * ny / a.mass;
b.vx += impulse * nx / b.mass;
b.vy += impulse * ny / b.mass;
if (a.isAsteroid || b.isAsteroid) {
if (a.isAsteroid && !b.isAsteroid) {
b.color = RGB(255, 200, 100);
} else if (!a.isAsteroid && b.isAsteroid) {
a.color = RGB(255, 200, 100);
}
}
}
}
}
}
}
void computeGravityAndUpdate() {
double currentTimeStep = timeStep * timeScale;
if (currentTimeStep > 0.02) currentTimeStep = 0.02;
if (currentTimeStep < 0.000001) currentTimeStep = 0.000001;
int subSteps = (int)(0.002 / currentTimeStep);
if (subSteps < 1) subSteps = 1;
if (subSteps > 800) subSteps = 800;
double subTimeStep = currentTimeStep / subSteps;
int n = planets.size();
int bhCount = blackHoles.size();
for (int step = 0; step < subSteps; step++) {
vector<double> ax(n), ay(n);
for (int i = 0; i < n; i++) {
if (planets[i].destroyed) {
ax[i] = 0;
ay[i] = 0;
continue;
}
double ax_total = 0, ay_total = 0;
Planet& p = planets[i];
double dx_sun = sun.x - p.x;
double dy_sun = sun.y - p.y;
double distSq_sun = dx_sun * dx_sun + dy_sun * dy_sun;
if (distSq_sun < 0.01) distSq_sun = 0.01;
double dist_sun = sqrt(distSq_sun);
double accel_sun = G * sun.mass / distSq_sun;
ax_total += accel_sun * dx_sun / dist_sun;
ay_total += accel_sun * dy_sun / dist_sun;
for (int k = 0; k < bhCount; k++) {
if (!blackHoles[k].active) continue;
double dx_bh = blackHoles[k].x - p.x;
double dy_bh = blackHoles[k].y - p.y;
double distSq_bh = dx_bh * dx_bh + dy_bh * dy_bh;
if (distSq_bh < 0.01) distSq_bh = 0.01;
double dist_bh = sqrt(distSq_bh);
double accel_bh = G * blackHoles[k].mass / distSq_bh;
ax_total += accel_bh * dx_bh / dist_bh;
ay_total += accel_bh * dy_bh / dist_bh;
}
for (int j = 0; j < n; j++) {
if (i == j || planets[j].destroyed) continue;
Planet& p2 = planets[j];
double dx = p2.x - p.x;
double dy = p2.y - p.y;
double distSq = dx * dx + dy * dy;
if (distSq < 0.01) distSq = 0.01;
double dist = sqrt(distSq);
double accel = G * p2.mass / distSq;
ax_total += accel * dx / dist;
ay_total += accel * dy / dist;
}
ax[i] = ax_total;
ay[i] = ay_total;
}
for (int i = 0; i < n; i++) {
if (planets[i].destroyed) continue;
Planet& p = planets[i];
p.x += p.vx * subTimeStep + 0.5 * ax[i] * subTimeStep * subTimeStep;
p.y += p.vy * subTimeStep + 0.5 * ay[i] * subTimeStep * subTimeStep;
}
vector<double> ax_new(n), ay_new(n);
for (int i = 0; i < n; i++) {
if (planets[i].destroyed) {
ax_new[i] = 0;
ay_new[i] = 0;
continue;
}
double ax_total = 0, ay_total = 0;
Planet& p = planets[i];
double dx_sun = sun.x - p.x;
double dy_sun = sun.y - p.y;
double distSq_sun = dx_sun * dx_sun + dy_sun * dy_sun;
if (distSq_sun < 0.01) distSq_sun = 0.01;
double dist_sun = sqrt(distSq_sun);
double accel_sun = G * sun.mass / distSq_sun;
ax_total += accel_sun * dx_sun / dist_sun;
ay_total += accel_sun * dy_sun / dist_sun;
for (int k = 0; k < bhCount; k++) {
if (!blackHoles[k].active) continue;
double dx_bh = blackHoles[k].x - p.x;
double dy_bh = blackHoles[k].y - p.y;
double distSq_bh = dx_bh * dx_bh + dy_bh * dy_bh;
if (distSq_bh < 0.01) distSq_bh = 0.01;
double dist_bh = sqrt(distSq_bh);
double accel_bh = G * blackHoles[k].mass / distSq_bh;
ax_total += accel_bh * dx_bh / dist_bh;
ay_total += accel_bh * dy_bh / dist_bh;
}
for (int j = 0; j < n; j++) {
if (i == j || planets[j].destroyed) continue;
Planet& p2 = planets[j];
double dx = p2.x - p.x;
double dy = p2.y - p.y;
double distSq = dx * dx + dy * dy;
if (distSq < 0.01) distSq = 0.01;
double dist = sqrt(distSq);
double accel = G * p2.mass / distSq;
ax_total += accel * dx / dist;
ay_total += accel * dy / dist;
}
ax_new[i] = ax_total;
ay_new[i] = ay_total;
}
for (int i = 0; i < n; i++) {
if (planets[i].destroyed) continue;
Planet& p = planets[i];
p.vx += 0.5 * (ax[i] + ax_new[i]) * subTimeStep;
p.vy += 0.5 * (ay[i] + ay_new[i]) * subTimeStep;
}
handleCollisions();
for (int i = 0; i < n; i++) {
if (planets[i].destroyed) continue;
Planet& p = planets[i];
double dx = p.x - sun.x;
double dy = p.y - sun.y;
double distSq = dx * dx + dy * dy;
double dist = sqrt(distSq);
if (dist < sun.radius + p.radius) {
p.destroyed = true;
continue;
}
for (int k = 0; k < bhCount; k++) {
if (!blackHoles[k].active) continue;
double dx_bh = p.x - blackHoles[k].x;
double dy_bh = p.y - blackHoles[k].y;
double distSq_bh = dx_bh * dx_bh + dy_bh * dy_bh;
double dist_bh = sqrt(distSq_bh);
if (dist_bh < blackHoles[k].radius + p.radius) {
p.destroyed = true;
blackHoles[k].mass += p.mass * 0.3;
blackHoles[k].radius = 5 * scale * (1 + blackHoles[k].mass / 500.0);
break;
}
}
if (p.destroyed) continue;
double speed = sqrt(p.vx * p.vx + p.vy * p.vy);
if (speed > 1000000.0) {
p.destroyed = true;
}
}
for (int k = 0; k < bhCount; k++) {
if (!blackHoles[k].active) continue;
blackHoles[k].mass *= 0.99995;
if (blackHoles[k].mass < 1.0) {
blackHoles[k].active = false;
} else {
blackHoles[k].radius = 5 * scale * (1 + blackHoles[k].mass / 500.0);
}
}
}
}
void updatePositions() {
for (int i = 0; i < (int)planets.size(); i++) {
Planet& p = planets[i];
if (p.destroyed) continue;
p.trailX.push_back(p.x);
p.trailY.push_back(p.y);
if (p.trailX.size() > MAX_TRAIL) {
int removeCount = MAX_TRAIL / 10;
p.trailX.erase(p.trailX.begin(), p.trailX.begin() + removeCount);
p.trailY.erase(p.trailY.begin(), p.trailY.begin() + removeCount);
}
}
}
void clearTrails() {
for (int i = 0; i < (int)planets.size(); i++) {
planets[i].trailX.clear();
planets[i].trailY.clear();
planets[i].destroyed = false;
planets[i].isAsteroid = false;
double r = sqrt((planets[i].x - sun.x) * (planets[i].x - sun.x) + (planets[i].y - sun.y) * (planets[i].y - sun.y));
if (r < 1.0) r = 100 * scale;
double v = sqrt(G * sun.mass / r);
double angle = atan2(planets[i].y - sun.y, planets[i].x - sun.x);
planets[i].vx = -v * sin(angle);
planets[i].vy = v * cos(angle);
}
selectedPlanet = -1;
}
void clearBlackHoles() {
for (int i = 0; i < (int)blackHoles.size(); i++) {
blackHoles[i].active = false;
}
blackHoles.clear();
}
void drawMinimap() {
int mx = WIDTH - 110, my = 10, mw = 100, mh = 100;
setfillcolor(RGB(0, 0, 0));
setlinecolor(RGB(60, 60, 60));
fillrectangle(mx, my, mx + mw, my + mh);
setlinecolor(RGB(60, 60, 60));
rectangle(mx, my, mx + mw, my + mh);
double mapSize = 500 * scale;
double sx = mx + 50;
double sy = my + 50;
for (int i = 0; i < (int)planets.size(); i++) {
if (planets[i].destroyed) continue;
double dx = (planets[i].x - sun.x) / mapSize * 40;
double dy = (planets[i].y - sun.y) / mapSize * 40;
setfillcolor(planets[i].color);
fillcircle((int)(sx + dx), (int)(sy + dy), 1);
}
setfillcolor(RGB(255, 200, 50));
fillcircle((int)sx, (int)sy, 3);
for (int k = 0; k < (int)blackHoles.size(); k++) {
if (!blackHoles[k].active) continue;
double dx = (blackHoles[k].x - sun.x) / mapSize * 40;
double dy = (blackHoles[k].y - sun.y) / mapSize * 40;
setfillcolor(RGB(80, 0, 0));
fillcircle((int)(sx + dx), (int)(sy + dy), 2);
setlinecolor(RGB(120, 0, 0));
circle((int)(sx + dx), (int)(sy + dy), 3);
}
settextcolor(RGB(80, 80, 80));
settextstyle(9, 0, "宋体");
outtextxy(mx + 5, my + mh - 12, "小地图");
}
void drawManual() {
int mx = 50, my = 30, mw = 700, mh = 540;
setfillcolor(RGB(10, 10, 30));
setlinecolor(RGB(150, 150, 255));
fillrectangle(mx, my, mx + mw, my + mh);
setlinecolor(RGB(200, 200, 255));
rectangle(mx, my, mx + mw, my + mh);
settextcolor(RGB(255, 255, 200));
settextstyle(22, 0, "宋体");
outtextxy(mx + 250, my + 10, "=== 使用手册 ===");
settextstyle(14, 0, "宋体");
int y = my + 50;
settextcolor(RGB(200, 255, 200));
outtextxy(mx + 20, y, "【基本操作】");
y += 28;
settextcolor(RGB(200, 200, 255));
outtextxy(mx + 30, y, "W A S D - 移动视角");
y += 24;
outtextxy(mx + 30, y, "M - 视角回到太阳");
y += 24;
outtextxy(mx + 30, y, "上/下方向键 - 调整时间流速");
y += 24;
outtextxy(mx + 30, y, "左/右方向键 - 调整地图缩放");
y += 24;
outtextxy(mx + 30, y, "Shift+右 - 时间流速设为 10000x");
y += 24;
outtextxy(mx + 30, y, "Shift+左 - 时间流速设为 1x");
y += 24;
outtextxy(mx + 30, y, "空格键 - 清除所有轨迹");
y += 24;
outtextxy(mx + 30, y, "F - 发射小行星");
y += 24;
outtextxy(mx + 30, y, "Enter - 重开模拟");
y += 24;
outtextxy(mx + 30, y, "ESC - 暂停/继续");
y += 24;
outtextxy(mx + 30, y, "E - 打开/关闭控制菜单");
y += 24;
outtextxy(mx + 30, y, "Q - 打开/关闭本手册");
y += 30;
settextcolor(RGB(200, 255, 200));
outtextxy(mx + 20, y, "【菜单模式 (按E打开)】");
y += 28;
settextcolor(RGB(200, 200, 255));
outtextxy(mx + 30, y, "B - 开启/关闭黑洞放置");
y += 24;
outtextxy(mx + 30, y, "N - 清除所有黑洞");
y += 24;
outtextxy(mx + 30, y, "S - 增大太阳半径");
y += 24;
outtextxy(mx + 30, y, "X - 减小太阳半径");
y += 24;
outtextxy(mx + 30, y, "点击行星 - 选择行星(黄色高亮)");
y += 24;
outtextxy(mx + 30, y, "菜单中点击+- - 调整速度/质量");
y += 24;
outtextxy(mx + 30, y, "R - 重置所有轨道");
y += 30;
settextcolor(RGB(255, 200, 150));
outtextxy(mx + 20, y, "提示: 菜单打开时自动暂停");
y += 24;
outtextxy(mx + 20, y, " 黑洞最多100个, 吞噬行星会增大");
settextcolor(RGB(200, 200, 255));
outtextxy(mx + 20, mh + my - 30, "按 Q 或 ESC 关闭手册");
}
void drawMenu() {
int menuX = 420;
int menuY = 30;
int menuW = 360;
int menuH = 540;
setfillcolor(RGB(20, 20, 30));
setlinecolor(RGB(100, 100, 150));
fillrectangle(menuX, menuY, menuX + menuW, menuY + menuH);
setlinecolor(RGB(200, 200, 255));
rectangle(menuX, menuY, menuX + menuW, menuY + menuH);
settextcolor(RGB(200, 200, 255));
settextstyle(18, 0, "宋体");
outtextxy(menuX + 100, menuY + 15, "=== 控制菜单 ===");
settextstyle(14, 0, "宋体");
int y = menuY + 55;
outtextxy(menuX + 20, y, "[F] 发射小行星");
y += 28;
outtextxy(menuX + 20, y, "[B] 放置黑洞 (点击放置, 最多100个)");
y += 28;
outtextxy(menuX + 20, y, "[N] 清除所有黑洞");
y += 28;
outtextxy(menuX + 20, y, "[S] 增大太阳 [X] 减小太阳");
y += 28;
outtextxy(menuX + 20, y, "[点击行星] 选择行星 (黄色高亮)");
y += 28;
outtextxy(menuX + 20, y, "[R] 重置轨道");
y += 28;
outtextxy(menuX + 20, y, "[空格] 清除轨迹");
y += 28;
outtextxy(menuX + 20, y, "[Enter] 重开模拟");
y += 28;
outtextxy(menuX + 20, y, "[E] 关闭菜单");
y += 30;
settextcolor(RGB(255, 255, 100));
char info[100];
if (selectedPlanet >= 0 && selectedPlanet < (int)planets.size() && !planets[selectedPlanet].destroyed) {
sprintf(info, "选中行星: %d", selectedPlanet + 1);
outtextxy(menuX + 20, y, info);
y += 26;
double speed = sqrt(planets[selectedPlanet].vx * planets[selectedPlanet].vx +
planets[selectedPlanet].vy * planets[selectedPlanet].vy);
sprintf(info, "速度: %.2f", speed);
outtextxy(menuX + 20, y, info);
outtextxy(menuX + 160, y, "[+]");
outtextxy(menuX + 200, y, "[-]");
y += 26;
sprintf(info, "质量: %.1f", planets[selectedPlanet].mass);
outtextxy(menuX + 20, y, info);
outtextxy(menuX + 160, y, "[+]");
outtextxy(menuX + 200, y, "[-]");
y += 26;
if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) {
POINT p;
GetCursorPos(&p);
ScreenToClient(GetHWnd(), &p);
if (p.x >= menuX + 155 && p.x <= menuX + 175 && p.y >= y - 50 && p.y <= y - 26) {
planets[selectedPlanet].vx *= 1.05;
planets[selectedPlanet].vy *= 1.05;
Sleep(200);
}
if (p.x >= menuX + 195 && p.x <= menuX + 215 && p.y >= y - 50 && p.y <= y - 26) {
planets[selectedPlanet].vx *= 0.95;
planets[selectedPlanet].vy *= 0.95;
Sleep(200);
}
if (p.x >= menuX + 155 && p.x <= menuX + 175 && p.y >= y - 24 && p.y <= y) {
planets[selectedPlanet].mass *= 1.1;
planets[selectedPlanet].radius *= 1.05;
Sleep(200);
}
if (p.x >= menuX + 195 && p.x <= menuX + 215 && p.y >= y - 24 && p.y <= y) {
planets[selectedPlanet].mass *= 0.9;
planets[selectedPlanet].radius *= 0.95;
if (planets[selectedPlanet].mass < 1.0) planets[selectedPlanet].mass = 1.0;
Sleep(200);
}
}
} else {
outtextxy(menuX + 20, y, "未选中行星 (点击行星选择)");
y += 26;
}
y += 10;
sprintf(info, "太阳半径: %.1f", sun.radius);
outtextxy(menuX + 20, y, info);
y += 26;
int activeBH = 0;
for (int i = 0; i < (int)blackHoles.size(); i++) {
if (blackHoles[i].active) activeBH++;
}
sprintf(info, "黑洞: %d / %d", activeBH, MAX_BLACK_HOLES);
outtextxy(menuX + 20, y, info);
y += 35;
if (placingBlackHole) {
settextcolor(RGB(255, 200, 0));
outtextxy(menuX + 20, y, "点击画面放置黑洞");
}
}
void drawScene() {
cleardevice();
double screenCenterX = WIDTH / 2;
double screenCenterY = HEIGHT / 2;
double sx = screenCenterX + (sun.x - WIDTH/2) / mapScale + viewOffsetX;
double sy = screenCenterY + (sun.y - HEIGHT/2) / mapScale + viewOffsetY;
setfillcolor(sun.color);
setlinecolor(RGB(255, 200, 50));
fillcircle((int)sx, (int)sy, (int)(sun.radius / mapScale));
setlinecolor(RGB(255, 220, 100));
circle((int)sx, (int)sy, (int)((sun.radius + 8 * scale) / mapScale));
setlinecolor(RGB(255, 200, 50));
circle((int)sx, (int)sy, (int)((sun.radius + 16 * scale) / mapScale));
for (int i = 0; i < (int)planets.size(); i++) {
const Planet& p = planets[i];
int trailSize = p.trailX.size();
if (trailSize < 2) continue;
setcolor(p.color);
setlinecolor(p.color);
setlinestyle(PS_SOLID, 1);
int step = trailSize > 50000 ? 2 : 1;
for (int j = step; j < trailSize; j += step) {
double x1 = screenCenterX + (p.trailX[j-step] - WIDTH/2) / mapScale + viewOffsetX;
double y1 = screenCenterY + (p.trailY[j-step] - HEIGHT/2) / mapScale + viewOffsetY;
double x2 = screenCenterX + (p.trailX[j] - WIDTH/2) / mapScale + viewOffsetX;
double y2 = screenCenterY + (p.trailY[j] - HEIGHT/2) / mapScale + viewOffsetY;
line((int)x1, (int)y1, (int)x2, (int)y2);
}
}
for (int i = 0; i < (int)planets.size(); i++) {
const Planet& p = planets[i];
if (p.destroyed) continue;
setfillcolor(p.color);
if (i == selectedPlanet && menuOpen) {
setlinecolor(RGB(255, 255, 0));
setlinestyle(PS_SOLID, 2);
} else {
setlinecolor(WHITE);
setlinestyle(PS_SOLID, 1);
}
double px = screenCenterX + (p.x - WIDTH/2) / mapScale + viewOffsetX;
double py = screenCenterY + (p.y - HEIGHT/2) / mapScale + viewOffsetY;
fillcircle((int)px, (int)py, (int)(p.radius / mapScale));
setcolor(p.color);
circle((int)px, (int)py, (int)((p.radius + 2 * scale) / mapScale));
}
for (int k = 0; k < (int)blackHoles.size(); k++) {
if (!blackHoles[k].active) continue;
double bx = screenCenterX + (blackHoles[k].x - WIDTH/2) / mapScale + viewOffsetX;
double by = screenCenterY + (blackHoles[k].y - HEIGHT/2) / mapScale + viewOffsetY;
double br = blackHoles[k].radius / mapScale;
for (int r = (int)br; r > 0; r -= 2) {
setfillcolor(RGB(0, 0, 0));
setlinecolor(RGB(50, 50, 50));
fillcircle((int)bx, (int)by, r);
}
setfillcolor(RGB(0, 0, 0));
setlinecolor(RGB(100, 0, 0));
fillcircle((int)bx, (int)by, (int)br);
setcolor(RGB(200, 0, 0));
circle((int)bx, (int)by, (int)(br * 1.5));
setcolor(RGB(150, 0, 0));
circle((int)bx, (int)by, (int)(br * 2.0));
}
drawMinimap();
settextcolor(WHITE);
settextstyle(14, 0, "宋体");
char info1[100];
sprintf(info1, "G = %.0f 时间流速: %.2fx 行星: %d", G, timeScale, (int)planets.size());
outtextxy(10, 10, info1);
char info2[100];
sprintf(info2, "WASD移动 M回太阳 F小行星 Enter重开");
outtextxy(10, 30, info2);
char info3[100];
sprintf(info3, "ESC暂停 Shift+右10000x Shift+左1x E菜单 Q手册");
outtextxy(10, 50, info3);
int destroyedCount = 0;
for (int i = 0; i < (int)planets.size(); i++) {
if (planets[i].destroyed) destroyedCount++;
}
char info4[100];
sprintf(info4, "毁灭: %d", destroyedCount);
outtextxy(10, 70, info4);
if (paused && !menuOpen && !manualOpen) {
settextcolor(RGB(255, 200, 100));
outtextxy(10, 90, "=== 暂停 ===");
}
if (manualOpen) {
drawManual();
} else if (menuOpen) {
drawMenu();
settextcolor(RGB(255, 200, 100));
settextstyle(14, 0, "宋体");
outtextxy(10, 110, "=== 暂停 ===");
}
}
int main() {
system("title 行星绕日模拟程序");
char input[20];
char countInput[20];
printf("请输入引力常数 G : ");
scanf("%s", input);
G = atof(input);
if (G < 10) G = 100;
if (G > 100000000) G = 500;
printf("请输入行星数量 (1-50): ");
scanf("%s", countInput);
planetCount = atoi(countInput);
if (planetCount < 1) planetCount = 7;
if (planetCount > 50) planetCount = 50;
initgraph(WIDTH, HEIGHT);
HWND hwnd = GetHWnd();
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
srand((unsigned)time(NULL));
initSun();
initBlackHoles();
initPlanets();
BeginBatchDraw();
while (true) {
if (GetAsyncKeyState(VK_UP) & 0x8000) {
timeScale *= 1.02;
if (timeScale > 10000.0) timeScale = 10000.0;
}
if (GetAsyncKeyState(VK_DOWN) & 0x8000) {
timeScale /= 1.02;
if (timeScale < 0.01) timeScale = 0.01;
}
if (GetAsyncKeyState(VK_LEFT) & 0x8000) {
mapScale /= 1.05;
if (mapScale < 0.1) mapScale = 0.1;
}
if (GetAsyncKeyState(VK_RIGHT) & 0x8000) {
mapScale *= 1.05;
if (mapScale > 100.0) mapScale = 100.0;
}
if (GetAsyncKeyState('W') & 0x8000) {
viewOffsetY += 8;
}
if (GetAsyncKeyState('S') & 0x8000) {
viewOffsetY -= 8;
}
if (GetAsyncKeyState('A') & 0x8000) {
viewOffsetX += 8;
}
if (GetAsyncKeyState('D') & 0x8000) {
viewOffsetX -= 8;
}
if (GetAsyncKeyState('M') & 0x8000) {
viewOffsetX = 0;
viewOffsetY = 0;
mapScale = 1.0;
Sleep(150);
}
if (GetAsyncKeyState('F') & 0x8000) {
spawnAsteroid();
Sleep(200);
}
if (GetAsyncKeyState(VK_RETURN) & 0x8000) {
planets.clear();
blackHoles.clear();
viewOffsetX = 0;
viewOffsetY = 0;
mapScale = 1.0;
timeScale = 50.0;
paused = false;
menuOpen = false;
manualOpen = false;
selectedPlanet = -1;
placingBlackHole = false;
initSun();
initBlackHoles();
initPlanets();
Sleep(300);
}
if ((GetAsyncKeyState(VK_RSHIFT) & 0x8000) && (GetAsyncKeyState(VK_RIGHT) & 0x8000)) {
timeScale = 10000.0;
}
if ((GetAsyncKeyState(VK_RSHIFT) & 0x8000) && (GetAsyncKeyState(VK_LEFT) & 0x8000)) {
timeScale = 1.0;
}
if (GetAsyncKeyState(VK_SPACE) & 0x8000) {
clearTrails();
Sleep(200);
}
if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) {
if (manualOpen) {
manualOpen = false;
Sleep(200);
} else if (menuOpen) {
menuOpen = false;
selectedPlanet = -1;
Sleep(200);
} else {
paused = !paused;
Sleep(200);
}
}
if (GetAsyncKeyState('Q') & 0x8000) {
manualOpen = !manualOpen;
if (manualOpen) { menuOpen = false; }
Sleep(200);
}
if (GetAsyncKeyState(0x45) & 0x8000) {
menuOpen = !menuOpen;
if (menuOpen) { manualOpen = false; }
if (!menuOpen) selectedPlanet = -1;
Sleep(200);
}
if (menuOpen) {
if (GetAsyncKeyState(VK_LBUTTON) & 0x8000 && !placingBlackHole) {
POINT p;
GetCursorPos(&p);
ScreenToClient(GetHWnd(), &p);
if (p.x < 420) {
double screenCenterX = WIDTH / 2;
double screenCenterY = HEIGHT / 2;
double clickX = WIDTH/2 + (p.x - screenCenterX - viewOffsetX) * mapScale;
double clickY = HEIGHT/2 + (p.y - screenCenterY - viewOffsetY) * mapScale;
double minDist = 20 * scale;
selectedPlanet = -1;
for (int i = 0; i < (int)planets.size(); i++) {
if (planets[i].destroyed) continue;
double dx = planets[i].x - clickX;
double dy = planets[i].y - clickY;
double dist = sqrt(dx * dx + dy * dy);
if (dist < minDist) {
minDist = dist;
selectedPlanet = i;
}
}
}
Sleep(200);
}
if (GetAsyncKeyState('B') & 0x8000) {
placingBlackHole = !placingBlackHole;
Sleep(200);
}
if (GetAsyncKeyState('N') & 0x8000) {
clearBlackHoles();
Sleep(200);
}
if (GetAsyncKeyState('S') & 0x8000) {
sun.radius *= 1.1;
if (sun.radius > 100 * scale) sun.radius = 100 * scale;
Sleep(100);
}
if (GetAsyncKeyState('X') & 0x8000) {
sun.radius /= 1.1;
if (sun.radius < 5 * scale) sun.radius = 5 * scale;
Sleep(100);
}
if (GetAsyncKeyState('R') & 0x8000) {
clearTrails();
clearBlackHoles();
Sleep(200);
}
if (GetAsyncKeyState(VK_LBUTTON) & 0x8000 && placingBlackHole) {
int activeBH = 0;
for (int i = 0; i < (int)blackHoles.size(); i++) {
if (blackHoles[i].active) activeBH++;
}
if (activeBH < MAX_BLACK_HOLES) {
POINT p;
GetCursorPos(&p);
ScreenToClient(GetHWnd(), &p);
double screenCenterX = WIDTH / 2;
double screenCenterY = HEIGHT / 2;
BlackHole bh;
bh.x = WIDTH/2 + (p.x - screenCenterX - viewOffsetX) * mapScale;
bh.y = HEIGHT/2 + (p.y - screenCenterY - viewOffsetY) * mapScale;
bh.active = true;
bh.radius = 5 * scale;
bh.mass = 500.0;
bh.color = RGB(0, 0, 0);
blackHoles.push_back(bh);
}
Sleep(300);
}
}
if (!menuOpen && !manualOpen && !paused) {
computeGravityAndUpdate();
updatePositions();
}
drawScene();
FlushBatchDraw();
Sleep(5);
}
EndBatchDraw();
closegraph();
return 0;
}