Мини-скринсейвер с летающими шариками
Это просто интересная программа, с кодом которой можно поэкспериментировать и на работу которой можно позалипать.
Если кратко:
Создаются 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;