代码:
#define _UNICODE
#define UNICODE
#include <array>
#include <cmath>
#include <ctime>
#include <format>
#include <graphics.h>
#include <vector>
typedef struct tagRECTF {
double left;
double top;
double right;
double bottom;
} RECTF, * PRECTF, NEAR* NPRECTF, FAR* LPRECTF;
// 2D Vector
class Vec final {
public:
Vec() : x({ 0, 0 }) {
}
Vec(std::array<double, 2> Init) : x(Init) {
}
public:
auto Length() noexcept -> double {
return sqrt(pow(x[0], 2) + pow(x[1], 2));
}
auto Normalize() noexcept -> Vec {
return (*this) / Length();
}
auto DotProduct(const Vec& Vector) noexcept -> double {
return Vector.x[0] * x[0] + Vector.x[1] * x[1];
}
public:
auto operator*=(const Vec& Value) -> Vec& {
for (auto position = size_t(0); position < 2; ++position) {
x[position] *= Value.x[position];
}
return (*this);
}
auto operator/=(const Vec& Value) -> Vec& {
for (auto position = size_t(0); position < 2; ++position) {
x[position] /= Value.x[position];
}
return (*this);
}
auto operator+=(const Vec& Value) -> Vec& {
for (auto position = size_t(0); position < 2; ++position) {
x[position] += Value.x[position];
}
return (*this);
}
auto operator-=(const Vec& Value) -> Vec& {
for (auto position = size_t(0); position < 2; ++position) {
x[position] -= Value.x[position];
}
return (*this);
}
auto operator*=(const double& Value) -> Vec& {
for (auto position = size_t(0); position < 2; ++position) {
x[position] * Value;
}
return (*this);
}
auto operator/=(const double& Value) -> Vec& {
for (auto position = size_t(0); position < 2; ++position) {
x[position] / Value;
}
return (*this);
}
auto operator+=(const double& Value) -> Vec& {
for (auto position = size_t(0); position < 2; ++position) {
x[position] + Value;
}
return (*this);
}
auto operator-=(const double& Value) -> Vec& {
for (auto position = size_t(0); position < 2; ++position) {
x[position] - Value;
}
return (*this);
}
public:
friend auto operator/(const Vec& Left, const Vec& Right)->Vec;
friend auto operator/(const Vec& Left, const double& Right)->Vec;
friend auto operator*(const Vec& Left, const Vec& Right)->Vec;
friend auto operator*(const Vec& Left, const double& Right)->Vec;
friend auto operator+(const Vec& Left, const Vec& Right)->Vec;
friend auto operator+(const Vec& Left, const double& Right)->Vec;
friend auto operator-(const Vec& Left, const Vec& Right)->Vec;
friend auto operator-(const Vec& Left, const double& Right)->Vec;
public:
std::array<double, 2> x;
};
auto operator/(const Vec& Left, const Vec& Right) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] / Right.x[position];
}
return result;
}
auto operator/(const Vec& Left, const double& Right) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] / Right;
}
return result;
}
auto operator*(const Vec& Left, const Vec& Right) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] * Right.x[position];
}
return result;
}
auto operator*(const Vec& Left, const double& Right) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] * Right;
}
return result;
}
auto operator+(const Vec& Left, const Vec& Right) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] + Right.x[position];
}
return result;
}
auto operator+(const Vec& Left, const double& Right) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] + Right;
}
return result;
}
auto operator-(const Vec& Left, const Vec& Right) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] - Right.x[position];
}
return result;
}
auto operator-(const Vec& Left, const double& Right) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] - Right;
}
return result;
}
auto operator/(const double& Right, const Vec& Left) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Right / Left.x[position];
}
return result;
}
auto operator*(const double& Right, const Vec& Left) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] * Right;
}
return result;
}
auto operator+(const double& Right, const Vec& Left) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Left.x[position] + Right;
}
return result;
}
auto operator-(const double& Right, const Vec& Left) -> Vec {
Vec result;
for (auto position = size_t(0); position < 2; ++position) {
result.x[position] = Right - Left.x[position];
}
return result;
}
class Sprite {
public:
Sprite() = default;
public:
virtual auto Draw() -> void = 0;
// Use SDF for collision judgement
virtual auto SDF(const Vec& Point) -> double = 0;
virtual auto GradientSDF(const Vec& Point) -> Vec = 0;
virtual auto DealSDF(const double& SDF) -> double {
return SDF;
}
virtual auto Particle() -> Vec = 0;
virtual auto RelativeMove(const Vec& Position) -> void = 0;
public:
auto Move(const double& X, const double& Y) -> void {
auto width = boundingBox.right - boundingBox.left;
auto height = boundingBox.bottom - boundingBox.top;
boundingBox = { X, Y, X + width, Y + height };
}
public:
bool lock = true;
RECTF boundingBox{};
Vec velocity;
};
class RoundSprite : public Sprite {
public:
explicit RoundSprite(const double& Radius) : radius(Radius), Sprite() {
boundingBox = { 0, 0, Radius * 2, Radius * 2 };
}
public:
auto Draw() -> void override {
setfillcolor(WHITE);
solidcircle(boundingBox.left + radius, boundingBox.top + radius, radius);
}
auto SDF(const Vec& Point) -> double override {
Vec centre({ boundingBox.left + radius, boundingBox.top + radius });
return (Point - centre).Length() - radius;
}
auto GradientSDF(const Vec& Point) -> Vec override {
auto base = SDF(Point) + radius;
Vec centre({ boundingBox.left + radius, boundingBox.top + radius });
return Vec({ (Point.x[0] - centre.x[0]) / base, (Point.x[1] - centre.x[1]) / base });
}
auto DealSDF(const double& SDF) -> double override {
return SDF - radius;
}
auto Particle() -> Vec override {
return Vec({ boundingBox.left + radius, boundingBox.top + radius });
}
auto RelativeMove(const Vec& Position) -> void override {
Move(Position.x[0] - radius, Position.x[1] - radius);
}
public:
double radius;
};
class LineSprite : public Sprite {
public:
explicit LineSprite(const Vec& Point1, const Vec& Point2) : point1(Point1), point2(Point2) {
boundingBox = { Point1.x[0], Point1.x[1], Point2.x[0], Point2.x[1] };
}
public:
auto Draw() -> void override {
setlinecolor(WHITE);
setlinestyle(PS_SOLID, 1);
line(point1.x[0], point1.x[1], point2.x[0], point2.x[1]);
}
auto SDF(const Vec& Point) -> double override {
Vec ap = Point - point1;
Vec ab = point2 - point1;
double h = ap.DotProduct(ab) / ab.DotProduct(ab);
h = h >= 1.f ? 1.f : h;
h = h <= 0.f ? 0.f : h;
return (ap - h * ab).Length();
}
auto GradientSDF(const Vec& Point) -> Vec override {
auto origin = SDF(Vec({ Point.x[0], Point.x[1] }));
return Vec({ SDF(Vec({Point.x[0] + 0.0000000001, Point.x[1]})) - origin,
SDF(Vec({Point.x[0], Point.x[1] + 0.0000000001})) - origin });
}
auto Particle() -> Vec override {
return point1 + (point2 - point1) / 2;
}
auto RelativeMove(const Vec& Position) -> void override {
auto width = boundingBox.right - boundingBox.left;
auto height = boundingBox.bottom - boundingBox.top;
point1 = Position;
point2 = point1 + Vec({ width, height });
Move(Position.x[0], Position.x[1]);
}
public:
Vec point1;
Vec point2;
};
class SpriteManager {
public:
SpriteManager() = default;
public:
auto UpdateSprite() -> void {
for (auto& sprite : spriteList) {
if (!sprite->lock) {
for (auto& other : spriteList) {
if (&other == &sprite) {
continue;
}
auto spritePoint = sprite->Particle();
auto sdf = sprite->DealSDF(other->SDF(spritePoint));
if (sdf <= 0.001) {
// Normal vector
auto normal = other->GradientSDF(spritePoint).Normalize();
auto newPosition = spritePoint + normal * abs(sdf);
// Fix the position
sprite->RelativeMove(newPosition);
// Fix the speed
if (sprite->velocity.DotProduct(normal) < 0) {
Vec NVelocity = sprite->velocity.DotProduct(normal) * normal;
Vec TVelocity = sprite->velocity - NVelocity;
Vec newNVelocity = (0.f - NVelocity);
Vec newTVelocity = max(1.f - (NVelocity.Length() / TVelocity.Length()), 1.f) * TVelocity;
sprite->velocity = newNVelocity + newTVelocity;
}
}
}
}
}
for (auto& sprite : spriteList) {
auto newPosition =
Vec({ static_cast<double>(sprite->boundingBox.left), static_cast<double>(sprite->boundingBox.top) }) +
sprite->velocity * timingTick;
sprite->Move(newPosition.x[0], newPosition.x[1]);
}
cleardevice();
for (auto& sprite : spriteList) {
sprite->Draw();
}
}
public:
double timingTick = 1.f;
std::vector<Sprite*> spriteList;
};
int main() {
initgraph(640, 480);
SpriteManager manager;
auto roundSprite1 = new RoundSprite(20.f);
auto roundSprite2 = new RoundSprite(60.f);
auto roundSprite3 = new RoundSprite(70.f);
auto roundSprite4 = new RoundSprite(20.f);
auto roundSprite5 = new RoundSprite(20.f);
auto roundSprite6 = new RoundSprite(20.f);
auto border1 = new LineSprite(Vec({ 0, 0 }), Vec({ static_cast<double>(getwidth()), 0 }));
auto border2 = new LineSprite(Vec({ 0, 0 }), Vec({ 0, static_cast<double>(getheight()) }));
auto border3 = new LineSprite(Vec({ static_cast<double>(getwidth()), 0 }),
Vec({ static_cast<double>(getwidth()), static_cast<double>(getheight()) }));
auto border4 = new LineSprite(Vec({ 0, static_cast<double>(getheight()) }),
Vec({ static_cast<double>(getwidth()), static_cast<double>(getheight()) }));
auto lineSprite = new LineSprite(Vec({ 70, 390 }), Vec({ 100, 190 }));
manager.spriteList.push_back(roundSprite1);
manager.spriteList.push_back(lineSprite);
manager.spriteList.push_back(border1);
manager.spriteList.push_back(border2);
manager.spriteList.push_back(border3);
manager.spriteList.push_back(border4);
manager.spriteList.push_back(roundSprite2);
manager.spriteList.push_back(roundSprite3);
manager.spriteList.push_back(roundSprite4);
manager.spriteList.push_back(roundSprite5);
manager.spriteList.push_back(roundSprite6);
roundSprite2->Move(180.f, 80.f);
roundSprite3->Move(380.f, 180.f);
roundSprite4->Move(300.f, 120.f);
roundSprite5->Move(400.f, 320.f);
roundSprite1->Move(20.f, 20.f);
roundSprite1->velocity.x = { 1.5f, 0.6f };
roundSprite4->velocity.x = { -1.7f, 1.3f };
roundSprite5->velocity.x = { -1.5f, -2.3f };
roundSprite6->velocity.x = { 3.f, -2.3f };
roundSprite4->lock = false;
roundSprite1->lock = false;
roundSprite5->lock = false;
roundSprite6->lock = false;
BeginBatchDraw();
settextcolor(GREEN);
auto fpsCount = int(0);
auto fps = int(0);
auto time = clock();
while (true) {
manager.UpdateSprite();
if (clock() - time >= 1000) {
fpsCount = fps;
fps = 0;
time = clock();
}
++fps;
FlushBatchDraw();
Sleep(2);
}
return 0;
}
运行结果: