برنامه نویسی خلاقانه-چه طور یک حشره کدنویسی کنیم
امروز در اینستاگرام به این حشره بامزه برخوردم و با خودم گفتم جالب میشه اگه مثلشو کد بزنم.
در این مطلب با استفاده از زبان ++C و OLC Pixel Game Engine این حشره رو کد میزنیم.
برای افرادی که به تازگی برنامه نویسی رو شروع کردند احتمالا تمرین خوب و جذابی خواهد بود
کد نهایی این برنامه رو میتونید در آخر مطلب پیدا کنید.
شروع کنیم؟
ابتدا هدر های مورد نیاز رو اضافه میکنیم. با توجه به اینکه تعریف توابع در خود فایل olcPixelGameEngine.h موجود هست (به صورت شرطی)، فقط در یکی از فایل هایی که کامپایل میکنیم، باید عبارت OLC_PGE_APPLICATION رو قبل از اینکلود کردن define کنیم.
مرحله بعدی ساخت یک کلاس -اینجا اسمشو گذاشتم Ebug- و ارث بری اون کلاس به صورت پابلیک از کلاس اصلی هست. متغیر sAppName نام برنامه خواهد بود که بالای صفحه نمایش داده میشه.
دو تابع OnUserCreate و OnUserUpdate حتما باید به همین شکل override بشن و مقدار true رو برگردونن تا برنامه اجرا بشه. در تابع اصلی main با استفاده از تابع construct اندازه صفحه و تراکم پیکسل ها رو مشخص میکنیم.
#include <cmath> // for sqrt function #define OLC_PGE_APPLICATION #include "olcPixelGameEngine.h" class Ebug : public olc::PixelGameEngine { public: Ebug() { sAppName = "ebug"; } ~Ebug() { } private: bool OnUserCreate() override { return true; } bool OnUserUpdate(float fElapsedTime) override { return true; } }; int main() { Ebug ebug; ebug.Construct(600, 500, 1, 1); ebug.Start(); return 0; }
برنامه رو با استفاده از دستور زیر کامپایل میکنیم:
g++ -o ebug.exe ebug.cpp -luser32 -lgdi32 -lopengl32 -lgdiplus -lShlwapi -ldwmapi -lstdc++fs -static -std=c++17
و اجرا:
بوممم یک صفحه سیاه! چندان جذاب به نظر نمیرسه. اما نگران نباشین به زودی جزئیات بیشتری به آن اضافه میکنیم.
اکنون یک struct برای نگه داری مختصات نقطه ها و تعدادی متغیر برای سایر اطلاعات در مورد نقطه های روی صفحه به کلاس Ebug اضافه میکنیم.
struct Pos { int x = 0; int y = 0; }; class Ebug : public olc::PixelGameEngine { public: Ebug() { sAppName = "ebug"; } ~Ebug() { } private: bool OnUserCreate() override { return true; } bool OnUserUpdate(float fElapsedTime) override { return true; } private: const int startX = 20; const int startY = 20; const int space = 50; const int row = 13; const int col = 11; Pos* position = nullptr; // array Pos nearToMouse; };
حال در تابع OnUserCreate(این تابع برخلاف تابع OnUserUpdate فقط یک بار اجرا میشود) به تعداد نقطه های روی صفخه شئی میسازیم و در خانه های آرایه آن ها را مقدار دهی میکنیم. سپس با استفاده از تابع FillCircle آن ها را بر روی صفحه رسم میکنیم.
bool OnUserCreate() override { position = new Pos[row * col]; int index = 0; for (int i = 0; i < row; ++i) { for (int j = 0; j < col; ++j) { Pos pos; pos.x = startX + space * i; pos.y = startY + space * j; position[index] = pos; ++index; FillCircle(pos.x, pos.y, 1, olc::WHITE); } } return true; }
کامپایل و سپس اجرا:
به نظر میرسه تا اینجا خوب پیش اومدیم! مرحله بعدی اینه که هر جا که نشانگر موس حرکت میکنه نزدیک ترین نقطه رو بهش پیدا کنیم و یک مربع شامل نقاط پیدا رسم کنیم و بقیه نقاط پنهان باشن.
ایده کلی اینه که در هر بار فراخوانی تابع OnUserUpdate ابتدا صفحه رو پاک میکنیم، نزدیک ترین نقطه به نشانگر موس رو پیدا میکنیم، همه ی نقاطی که فاصله اشون تا اون نقطه کمتر یا مساوی دو برابر فاصله بین نقاط ضرب در رادیکال 2 بود رو رسم میکنیم.
برای به دست آوردن فاصله بین نقاط نیاز به یک تابع کمکی هم داریم که با استفاده از قضیه فیثاغورث با گرفتن مختصات دو نقطه فاصله بین اون ها رو بهمون بده. اسم این تابع رو میذاریم distance.
یادتون نره تابع FillCircle قبلی که توی تابع OnUserCreate نوشته بودید رو کامنت کنید چون دیگه نیازی بهش نداریم.
double distance(int x1, int y1, int x2, int y2) { int t1 = abs(x1 - x2); int t2 = abs(y2 - y1); return sqrt(t1 * t1 + t2 * t2); } bool OnUserUpdate(float fElapsedTime) override { // Clear screen Clear(olc::BLACK); // Find the nearest dot to the mouse for (int i = 1; i < row * col; ++i) { if (distance(position[i].x, position[i].y, GetMouseX(), GetMouseY()) < distance(nearToMouse.x, nearToMouse.y, GetMouseX(), GetMouseY())) { nearToMouse = position[i]; } } // Draw dot square for (int i = 0; i < row * col; ++i) { if (distance(position[i].x, position[i].y, nearToMouse.x, nearToMouse.y) <= space*2*sqrt(2)) { FillCircle(position[i].x, position[i].y, 1); } } return true; }
کامپایل و اجرا:
قدم بعدی رسم بدن حشره است که خب خیلی ساده است. فقط به یک تابع FillCircle که دایره ای در محل موس رسم میکنه نیاز داریم. اما در مورد رسم پاها قضیه کمی فرق میکنه. با کمی دقت متوجه میشیم که پاها نباید روی نقاط خیلی نزدیک قرار بگیرن چون حشره جلوه ای غیر طبیعی پیدا میکنه.
پس کاری که ما باید بکنیم، مرتب کردن لیست نقطه ها بر اساس فاصلشون تا نشانگر موس، بیخیال شدن 4 تا از نزدیک نقطه ها ( یا 5 تاشون اگه میخواین پاها بیشتر کش بیان) و سپس رسم یک خط از 8 نقطه نزدیک بعدی (8 پا داریم) به بدن حشره.
برای این کار ابتدا یک تابع مرتب سازی مینویسیم. من اینجا از روش selection sort با کمی تغییر برای مرتب سازی عناصر ارایه استفاده کردم. دقت کنید تابع باید به صورت private ( یا پابلیک فرقی نداره) درون کلاس Ebug قرار بگیره چون میخوایم به تابع های گرفتن مختصات موس دسترسی داشته باشیم.
void sortPosition() { for (int i = 0; i < row * col; ++i) { for (int j = i + 1; j < row * col; ++j) { if (distance(position[j].x, position[j].y, GetMouseX(), GetMouseY()) < distance(position[i].x, position[i].y, GetMouseX(), GetMouseY())) { Pos temp = position[i]; position[i] = position[j]; position[j] = temp; } } } }
حال تابع OnUserUpdate رو تکمیل میکنیم و پاها رو رسم میکنیم:
bool OnUserUpdate(float fElapsedTime) { // Clear screen Clear(olc::BLACK); // Find the nearest dot to the mouse for (int i = 1; i < row * col; ++i) { if (distance(position[i].x, position[i].y, GetMouseX(), GetMouseY()) < distance(nearToMouse.x, nearToMouse.y, GetMouseX(), GetMouseY())) { nearToMouse = position[i]; } } // Draw dot square for (int i = 0; i < row * col; ++i) { if (distance(position[i].x, position[i].y, nearToMouse.x, nearToMouse.y) <= space*2*sqrt(2)) { FillCircle(position[i].x, position[i].y, 1); } } // Draw Spider FillCircle(GetMouseX(), GetMouseY(), 10); // Draw legs sortPosition(); for (int i = 5; i < 13; ++i) { DrawLine(position[i].x, position[i].y, GetMouseX(), GetMouseY()); } return true; }
کامپایل و اجرا:
و تمام! حشره شما آماده است!
این هم کد کامل برنامه حاضر و آماده:
//------------------------------------------------ // ebug.cpp // author: Mehrshad Jafari Farahani //------------------------------------------------ #include <cmath> // For sqrt function #define OLC_PGE_APPLICATION #include "olcPixelGameEngine.h" double distance(int x1, int y1, int x2, int y2) { int t1 = abs(x1 - x2); int t2 = abs(y2 - y1); return sqrt(t1 * t1 + t2 * t2); } struct Pos { int x = 0; int y = 0; }; class Ebug : public olc::PixelGameEngine { public: Ebug() { sAppName = "ebug"; } ~Ebug() { delete[] position; // Don't forget to delete the array } private: bool OnUserCreate() { position = new Pos[row * col]; int index = 0; for (int i = 0; i < row; ++i) { for (int j = 0; j < col; ++j) { Pos pos; pos.x = startX + space * i; pos.y = startY + space * j; position[index] = pos; ++index; } } return true; } bool OnUserUpdate(float fElapsedTime) { // Clear screen Clear(olc::BLACK); // Find the nearest dot to the mouse for (int i = 1; i < row * col; ++i) { if (distance(position[i].x, position[i].y, GetMouseX(), GetMouseY()) < distance(nearToMouse.x, nearToMouse.y, GetMouseX(), GetMouseY())) { nearToMouse = position[i]; } } // Draw dot square for (int i = 0; i < row * col; ++i) { if (distance(position[i].x, position[i].y, nearToMouse.x, nearToMouse.y) <= space*2*sqrt(2)) { FillCircle(position[i].x, position[i].y, 1); } } // Draw Spider FillCircle(GetMouseX(), GetMouseY(), 10); // Draw legs sortPosition(); for (int i = 5; i < 13; ++i) { DrawLine(position[i].x, position[i].y, GetMouseX(), GetMouseY()); } return true; } void sortPosition() { for (int i = 0; i < row * col; ++i) { for (int j = i + 1; j < row * col; ++j) { if (distance(position[j].x, position[j].y, GetMouseX(), GetMouseY()) < distance(position[i].x, position[i].y, GetMouseX(), GetMouseY())) { Pos temp = position[i]; position[i] = position[j]; position[j] = temp; } } } } private: const int startX = 20; const int startY = 20; const int space = 50; const int row = 13; const int col = 11; Pos* position = nullptr; // array Pos nearToMouse; }; int main() { Ebug ebug; ebug.Construct(600, 500, 1, 1); ebug.Start(); return 0; }
خیلی باحال و بامزه بود 😅
من با سی پلاس پلاس کار نکردم تا حالا، خیلی سخت تر از سی شارپه