代码:
#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;
}
运行结果:




















