Мини-скринсейвер с летающими шариками
Это просто интересная программа, с кодом которой можно поэкспериментировать и на работу которой можно позалипать.
Если кратко:
Создаются n шариков (в коде ниже — 20), поле, по которому они будут перемещаться и, по желанию, препятствие внутри поля, от которого шарики будут также отталкиваться.
Можно менять размеры и положение поля, препятствия, размер, цвет и количество самих шариков. Начнём.
Описание кода
Я буду писать на C++, поэтому для начала подключаем библиотеки:
#include <windows.h> #include <stdlib.h> #include <math.h> #include <ddraw.h> using namespace std; const int ballsCount = 20;
Создаём структуры Шар, Поле и Препятствие:
struct Ball {
float x, y; // координаты шарика
float dx, dy; // изменение координат по х и у
float R, G, B; // доли красного, зелёного и синего цветов
float r; // радиус шарика
};
struct Field {
float x, y;
float width, height;
};
struct Obstacle {
float x, y;
float width, height;
};Функция, убирающая курсор:
void ShowConsoleCursor(bool showFlag)
{
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursorInfo;
GetConsoleCursorInfo(out, &cursorInfo);
cursorInfo.bVisible = showFlag;
SetConsoleCursorInfo(out, &cursorInfo);
}Главная функция:
Создаём наши поле и препятствие и задаём их параметры (можете изменить их на любые другие положительные числа.
Obstacle obstacle; obstacle.x = 200; obstacle.y = 90; obstacle.width = 20; obstacle.height = 90; Field board; board.x = 1; board.y = 1; board.width = 350; board.height = 250;
Создаём массив из 20 шариков и прописываем их параметры:
Ball balls[ballsCount];
for (int i = 0; i < ballsCount; i++) {
balls[i].r = 10 + rand() % 10; // радиус от 10 до 19
balls[i].x = 0 + rand() % (int)board.width;
balls[i].y = 0 + rand() % (int)board.height;
balls[i].dx = 5;
balls[i].dy = 5;
balls[i].R = rand() % 256; // интенсивность цветов от 0 до 255
balls[i].G = rand() % 256;
balls[i].B = rand() % 256;
}Это всё отвечает за рисование в консоли. Можете не обращать внимания:
RECT rect; HWND window = GetConsoleWindow(); HDC handler = GetDC(window); GetClientRect(window, &rect); HPEN fieldPen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255)); HBRUSH fieldBrush = CreateSolidBrush(RGB(0, 0, 0)); HPEN ballPen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255)); HBRUSH ballBrush = CreateSolidBrush(RGB(0, 0, 0)); // фикс морганий HDC memDC = CreateCompatibleDC(handler); HBITMAP hBitmap = CreateCompatibleBitmap(handler, rect.right, rect.bottom); SelectObject(memDC, hBitmap); PatBlt(memDC, 0, 0, rect.right, rect.left, BLACKNESS); // удаляем _ из консоли ShowConsoleCursor(false); int fps = 70;
Далее в вечном цикле while(true) последует ещё немного графики:
SelectObject(memDC, fieldPen); SelectObject(memDC, fieldBrush); Rectangle(memDC, board.x, board.y, board.x + board.width, board.y + board.height); // Obstacle Rectangle(memDC, obstacle.x, obstacle.y, obstacle.x + obstacle.width, obstacle.y + obstacle.height);
Сдвинем каждый шарик и оттолкнём от стенки, если он до неё долетел:
for (int i = 0; i < ballsCount; i++) {
balls[i].x += balls[i].dx;
balls[i].y += balls[i].dy;
if (balls[i].x + balls[i].r > board.width) // правая стенка
{
balls[i].x -= balls[i].x - board.width + balls[i].r;
balls[i].dx = -balls[i].dx;
}
if (balls[i].y + balls[i].r > board.height) // верхняя стенка
{
balls[i].y -= balls[i].y - board.height + balls[i].r;
balls[i].dy = -balls[i].dy;
balls[i].dy += rand() % 1;
}
if (balls[i].x - balls[i].r < 0) // левая стенка
{
balls[i].x = balls[i].r;
balls[i].dx = -balls[i].dx;
balls[i].dx += rand() % 1;
}
if (balls[i].y - balls[i].r < 0) // нижняя стенка
{
balls[i].y = balls[i].r;
balls[i].dy = -balls[i].dy;
balls[i].dy += rand() % 1;
}
if ((balls[i].x + balls[i].r > obstacle.x) && (balls[i].x + balls[i].r < obstacle.x + obstacle.width)
&& (balls[i].y >= obstacle.y) && (balls[i].y <= obstacle.y + obstacle.height)) // левая стенка препятствия
{
balls[i].x = balls[i].x - (balls[i].x - obstacle.x) - balls[i].r;
balls[i].dx = -balls[i].dx;
}
if ((balls[i].x >= obstacle.x) && (balls[i].x <= obstacle.x + obstacle.width) && (balls[i].y + balls[i].r > obstacle.y)
&& (balls[i].y + balls[i].r < obstacle.y + obstacle.height)) // верхняя
{
balls[i].y = balls[i].y - (balls[i].y - obstacle.y) - balls[i].r;
balls[i].dy = -balls[i].dy;
balls[i].dy += rand() % 1;
}
if ((balls[i].x - balls[i].r > obstacle.x) && (balls[i].x - balls[i].r < obstacle.x + obstacle.width) && (balls[i].y >= obstacle.y)
&& (balls[i].y <= obstacle.y + obstacle.height)) // правая
{
balls[i].x = balls[i].x + (obstacle.x + obstacle.width - balls[i].x) + balls[i].r;
balls[i].dx = -balls[i].dx;
}
if ((balls[i].x >= obstacle.x) && (balls[i].x <= obstacle.x + obstacle.width) && (balls[i].y - balls[i].r > obstacle.y)
&& (balls[i].y - balls[i].r < obstacle.y + obstacle.height)) // нижняя
{
balls[i].y = balls[i].y + (obstacle.y + obstacle.height - balls[i].y) + balls[i].r;
balls[i].dy = -balls[i].dy;
balls[i].dy += rand() % 1;
}Ещё немного графики в том же цикле for:
ballPen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255)); ballBrush = CreateSolidBrush(RGB(balls[i].R, balls[i].G, balls[i].B)); SelectObject(memDC, ballPen); SelectObject(memDC, ballBrush); Ellipse(memDC, balls[i].x - balls[i].r, balls[i].y - balls[i].r, balls[i].x + balls[i].r, balls[i].y + balls[i].r); DeleteObject(ballPen); DeleteObject(ballBrush);
Оставшийся код тоже посвящён графике, не буду его разбирать, приведу ниже
код целиком:
#include <windows.h>
#include <stdlib.h>
#include <math.h>
#include <ddraw.h>
using namespace std;
const int ballsCount = 20;
struct Ball {
float x, y; // координаты шарика
float dx, dy; // изменение координат по х и у
float R, G, B; // доли красного, зелёного и синего цветов
float r; // радиус шарика
};
struct Field {
float x, y;
float width, height;
};
struct Obstacle {
float x, y;
float width, height;
};
void ShowConsoleCursor(bool showFlag)
{
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursorInfo;
GetConsoleCursorInfo(out, &cursorInfo);
cursorInfo.bVisible = showFlag;
SetConsoleCursorInfo(out, &cursorInfo);
}
int main()
{
Obstacle obstacle;
obstacle.x = 200;
obstacle.y = 90;
obstacle.width = 20;
obstacle.height = 90;
Field board;
board.x = 1;
board.y = 1;
board.width = 350;
board.height = 250;
Ball balls[ballsCount];
for (int i = 0; i < ballsCount; i++) {
balls[i].r = 10 + rand() % 10;
balls[i].x = 0 + rand() % (int)board.width;
balls[i].y = 0 + rand() % (int)board.height;
balls[i].dx = 5;
balls[i].dy = 5;
balls[i].R = rand() % 256;
balls[i].G = rand() % 256;
balls[i].B = rand() % 256;
}
RECT rect;
HWND window = GetConsoleWindow();
HDC handler = GetDC(window);
GetClientRect(window, &rect);
HPEN fieldPen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255));
HBRUSH fieldBrush = CreateSolidBrush(RGB(0, 0, 0));
HPEN ballPen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255));
HBRUSH ballBrush = CreateSolidBrush(RGB(0, 0, 0));
// фикс морганий
HDC memDC = CreateCompatibleDC(handler);
HBITMAP hBitmap = CreateCompatibleBitmap(handler, rect.right, rect.bottom);
SelectObject(memDC, hBitmap);
PatBlt(memDC, 0, 0, rect.right, rect.left, BLACKNESS);
// удаляем _ из консоли
ShowConsoleCursor(false);
int fps = 70;
while (true) {
SelectObject(memDC, fieldPen);
SelectObject(memDC, fieldBrush);
Rectangle(memDC, board.x, board.y, board.x + board.width, board.y + board.height);
// Obstacle
Rectangle(memDC, obstacle.x, obstacle.y, obstacle.x + obstacle.width, obstacle.y + obstacle.height);
for (int i = 0; i < ballsCount; i++) {
balls[i].x += balls[i].dx;
balls[i].y += balls[i].dy;
if (balls[i].x + balls[i].r > board.width) // правая стенка
{
balls[i].x -= balls[i].x - board.width + balls[i].r;
balls[i].dx = -balls[i].dx;
}
if (balls[i].y + balls[i].r > board.height) // верхняя стенка
{
balls[i].y -= balls[i].y - board.height + balls[i].r;
balls[i].dy = -balls[i].dy;
balls[i].dy += rand() % 1;
}
if (balls[i].x - balls[i].r < 0) // левая стенка
{
balls[i].x = balls[i].r;
balls[i].dx = -balls[i].dx;
balls[i].dx += rand() % 1;
}
if (balls[i].y - balls[i].r < 0) // нижняя стенка
{
balls[i].y = balls[i].r;
balls[i].dy = -balls[i].dy;
balls[i].dy += rand() % 1;
}
if ((balls[i].x + balls[i].r > obstacle.x) && (balls[i].x + balls[i].r < obstacle.x + obstacle.width)
&& (balls[i].y >= obstacle.y) && (balls[i].y <= obstacle.y + obstacle.height)) // левая стенка препятствия
{
balls[i].x = balls[i].x - (balls[i].x - obstacle.x) - balls[i].r;
balls[i].dx = -balls[i].dx;
}
if ((balls[i].x >= obstacle.x) && (balls[i].x <= obstacle.x + obstacle.width) && (balls[i].y + balls[i].r > obstacle.y)
&& (balls[i].y + balls[i].r < obstacle.y + obstacle.height)) // верхняя
{
balls[i].y = balls[i].y - (balls[i].y - obstacle.y) - balls[i].r;
balls[i].dy = -balls[i].dy;
balls[i].dy += rand() % 1;
}
if ((balls[i].x - balls[i].r > obstacle.x) && (balls[i].x - balls[i].r < obstacle.x + obstacle.width) && (balls[i].y >= obstacle.y)
&& (balls[i].y <= obstacle.y + obstacle.height)) // правая
{
balls[i].x = balls[i].x + (obstacle.x + obstacle.width - balls[i].x) + balls[i].r;
balls[i].dx = -balls[i].dx;
}
if ((balls[i].x >= obstacle.x) && (balls[i].x <= obstacle.x + obstacle.width) && (balls[i].y - balls[i].r > obstacle.y)
&& (balls[i].y - balls[i].r < obstacle.y + obstacle.height)) // нижняя
{
balls[i].y = balls[i].y + (obstacle.y + obstacle.height - balls[i].y) + balls[i].r;
balls[i].dy = -balls[i].dy;
balls[i].dy += rand() % 1;
}
ballPen = CreatePen(PS_SOLID, 2, RGB(255, 255, 255));
ballBrush = CreateSolidBrush(RGB(balls[i].R, balls[i].G, balls[i].B));
SelectObject(memDC, ballPen);
SelectObject(memDC, ballBrush);
Ellipse(memDC, balls[i].x - balls[i].r, balls[i].y - balls[i].r, balls[i].x + balls[i].r, balls[i].y + balls[i].r);
DeleteObject(ballPen);
DeleteObject(ballBrush);
}
BitBlt(handler, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY);
Sleep(2000 / fps);
}
ReleaseDC(window, handler);
DeleteObject(memDC);
}Бонус)
Если вставить этот код в начале цикла for, шарики будут переливаться разными цветами:
if (balls[i].R >= 255)
balls[i].R = 0;
if (balls[i].G >= 255)
balls[i].G = 0;
if (balls[i].B >= 255)
balls[i].B = 0;
balls[i].R += 1 + rand() % 10;
balls[i].G += 1 + rand() % 5;
balls[i].B += 1 + rand() % 4;