از صفر تا پونگ: اولین پروژه SFML من

من این عبارت را داشتم بهبود منظم مدرسه (Kagaku Joutatsu) در پیشینه دسک تاپ من سالها. به راحتی ترجمه شده ، به این معنی است که برای به دست آوردن درک واقعی ، باید با آشنا شروع کنید – اساسی ترین اصول. یادگیری انسان با تقلید آغاز می شود: نوزادان از والدین خود تقلید می کنند ، نوازندگان قطعات کلاسیک بازی می کنند و نقاشان مطالعات کارشناسی ارشد را انجام می دهند. آنها می گویند تقلید صادقانه ترین شکل چاپلوسی است ، اما همچنین یکی از مؤثرترین راهها برای یادگیری چیز جدید است.
چرا پونگ؟
در اوایل دهه 1970 ، به آلن آلکورن توسط رئیس خود ، نولان بوشلن ، یک کار تمرینی داده شد: یک بازی ورزشی ساده را برای ادیسه مگناوکس آن زمان محبوب بازآفرینی کنید. اجرای آلکورن چنان جلا داد که بوشلن تصمیم گرفت آن را به صورت تجاری آزاد کند. این بازی – پرشور – یک ضربه برک آوت ، به راه اندازی آتاری به عنوان یک نام خانوادگی و Trailblazer در صنعت بازی های ویدیویی تازه کار کمک می کند.
به یک معنا ، پنگ از ابتدا یک کلون بود. این تقلید از “تنیس روی میز” مگناوکس ، که خود یک دیجیتالی در ورزش واقعی بود. بنابراین کلونینگ پنگ احساس مشتق نمی کند. در هر صورت ، احساس می کنم ادای احترام می کنم.
چرا حالا؟
این ایده از زمانی که برای اولین بار با CS50 روبرو شدم در ذهنم لگد می زد آشنایی با توسعه بازیبشر این دوره از یک رویکرد مطالعه موردی برای بازسازی بازی های کلاسیک در LUA استفاده می کند و به دانشجویان می دهد که در معرض اصول اصلی طراحی بازی قرار بگیرند. من این مفهوم را دوست داشتم اما نمی خواستم لوا را در بالای هر چیز دیگری که یاد می گرفتم انتخاب کنم.
با این حال ، این ایده با من گیر کرده است. من فهمیدم که برنامه نویسی این بازی ها به زبانی متفاوت مزایای دو برابر را ارائه می دهد: درک عمیق تر از مکانیک بازی و نحو زبان. مصرف کمتر منفعل ، تمرین عمدی تر. دو پرنده ، یک سنگ.
این ما را اکنون به ارمغان می آورد. تجربه من با هر دو C ++ و SFML این بود که سخاوتمندانه ، حداقل. من چند فصل از LearnCpp.com و یک فیلم YouTube را در مورد پیوند SFML به ویژوال استودیو تماشا کرد. این در مورد آن بود
اما من آمریکایی هستم – اعتماد به نفس بدون درآمد عملاً سرگرمی ملی است.
🔗 لینک پروژه
آیا می خواهید دنبال کنید یا به کد نگاه کنید؟
👉 repo github: bikurastudios / pong-clone
ساخته شده با SFML 3.0 در ویژوال استودیو 2022
15 ساعت کار بیش از یک هفته عصرها
تجزیه
تنظیم پنجره
اولین قدم گرفتن و اجرای پنجره بازی اولین قدم بود. این بیشتر از آموزش کپی از آموزش ها بود ، نه یک چالش کدگذاری اصلی.
sf::RenderWindow window(sf::VideoMode(800, 600), "Pong");
window.setFramerateLimit(60);
دستگیره
بعدی: Paddles. من مستطیل های سفید ساده را ترسیم کردم و آنها را روی صفحه قرار دادم.
sf::RectangleShape paddle(sf::Vector2f(10.f, 100.f));
paddle.setPosition(50.f, 250.f);
paddle.setFillColor(sf::Color::White);
(بله ، اعداد جادویی قبلاً شروع به خزیدن کرده اند.)
حرکت
برای رسیدگی به ورودی بدون اتصال آن به نرخ فریم ، من معرفی کردم deltaTime
و آن را با سرعت دلخواه ضرب کرد.
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
paddle.move(0.f, -paddleSpeed * deltaTime);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
paddle.move(0.f, paddleSpeed * deltaTime);
مکانیک توپ
منطق توپ شبیه به بالشتک ها بود – آن را تنظیم کنید ، آن را موقعیت کنید ، سرعت را اختصاص دهید.
sf::CircleShape ball(10.f);
ball.setPosition(400.f, 300.f);
sf::Vector2f ballVelocity(200.f, 200.f);
ball.move(ballVelocity * deltaTime);
در کد من ، ballVelocity
از نظر فنی شکسته است من سعی کردم جهت شروع را تصادفی کنم اما هرگز ژنراتور شماره تصادفی را بذر نکردم. بنابراین توپ هر بار در همان جهت حرکت می کند – به طور جدی “تصادفی”.
تشخیص برخورد
اینجاست که همه چیز کثیف شد.
من احتمالاً چهار ساعت به عقب و جلو بین فیلم های YouTube و انجمن ها صرف تلاش برای نوشتن یک عملکرد برخورد می کردم که خطاها را به چپ و راست نمی اندازد. حتی کوپیلوت گیر کرده بود ، و من با یک هوش مصنوعی توهم آور مبنی بر اینکه آیا یک عملکرد وجود داشته است یا نه ، درگیر دعوا شدم. سرانجام متوجه شدم که کد مربوط به تقاطع ها برای نسخه SFML 3.0 به روز شده است. بنابراین ، آنچه قبلاً مانند این تایپ می شد:
if (playerRect.getGlobalBounds().intersects(targetRect.getGlobalBounds()))
Collision;
اکنون بیشتر شبیه به این است:
if (playerRect.getGlobalBounds().findIntersection(targetRect.getGlobalBounds()))
Collision;
من احتمالاً باید این را سریعتر فهمیده بودم ، اما شما زندگی می کنید و یاد می گیرید.
به هر حال ، این محافظ صفحه نمایش DVD است که من وقتی فهمیدم که واقعاً قرار است انجام دهم ، ساخته ام:
سرانجام ، من در برخورد اساسی AABB (جعبه محدودیت محور) مستقر شدم. این کاربردی است ، هرچند کامل نیست. گاهی اوقات توپ به سمت بالشتک می چرخد و قبل از خاموش شدن به شدت شتاب می یابد.
sf::FloatRect leftPaddleBounds = paddle.getGlobalBounds();
sf::FloatRect rightPaddleBounds = paddle2.getGlobalBounds();
sf::FloatRect ballBounds = ball.getGlobalBounds();
if (const std::optional intersection = ballBounds.findIntersection(leftPaddleBounds)) {
sound_leftPaddle.play();
ballVelocity.x *= -1.1f;
}
if (const std::optional intersection = ballBounds.findIntersection(rightPaddleBounds)) {
sound_rightPaddle.play();
ballVelocity.x *= -1.1f;
}
وقتی راحت تر از برخورد با برخورد برخورد می کنم ، این موضوع را دوباره مرور خواهیم کرد.
سیستم امتیاز دهی
اگر توپ خارج از صفحه می رود ، یک امتیاز کسب کنید و تنظیم مجدد کنید:
if (ball.getPosition().x < 0.f) {
player2Score += 1;
resetBall();
}
if (ball.getPosition().x > window.getSize().x) {
player1Score += 1;
resetBall();
}
سپس متن نمایش داده شده را به روز کنید:
player2ScoreString = std::to_string(player2Score);
scoreTextLeft.setString(player2ScoreString);
player1ScoreString = std::to_string(player1Score);
scoreTextRight.setString(player1ScoreString);
قابلیت مکث
یک سیستم مکث مناسب به یک دستگاه دولتی احتیاج دارد ، اما من از یک بولی ساده استفاده کردم:
bool isPaused = false;
if (!isPaused) {
// game loop
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::P)) {
isPaused = !isPaused;
}
این ظریف نیست ، اما کار می کند.
بازنشانی بازی
من فقط پس از برخورد یک بازیکن به 10 امتیاز اجازه می دهم. کی startNewGame
است ، true
، همه چیز بازنشانی می کند:
if (startNewGame) {
player1Score = 0;
player2Score = 0;
// Update score text
// Reset positions
ball.setPosition({400.f, 300.f});
paddle.setPosition({50.f, 250.f});
paddle2.setPosition({725.f, 250.f});
// Reassign velocity
directionX = randomDirection();
directionY = randomDirection();
ballVelocity = {200.f * directionX, 200.f * directionY};
startNewGame = false;
}
در آینده ، من یک ویژگی تنظیم مجدد را می خواهم که در هر زمان کار کند.
Buggier از دفترچه راهنمای یک متخصص شناس
یک لیست اشکال غیر تجربی:
-
مستندات منسوخ برای ایجاد پنجره و ورودی
-
بالشتک های خارج از صفحه
-
توپ در صفحه نمایش با سرعت ترمینال
-
چند خط
if
اظهارات بدون{}
-
تعاریف عملکرد در داخل
main()
-
سردرگمی با
sf::Vector2f
استدلال -
جعبه های محدود از نظر مفهومی روشن اما ضعیف اجرا شده است
-
مشکل در بارگیری پرونده های قلم
-
عدم به روزرسانی متن نمره
-
ضرب و شتم دولت بیش از حد
در اینجا نمونه ای بد از منطق از اوایل به بعد آورده شده است:
if (keyPressed->scancode == sf::Keyboard::Scancode::Space) {
if (Pause == true)
Pause = false;
if (Pause == false)
Pause = true;
}
سرانجام ساده شد:
if (keyPressed->scancode == sf::Keyboard::Scancode::Space)
Pause = !Pause;
خلاصه بررسی کد
من هیچ برنامه نویسی در حلقه فوری خود ندارم ، بنابراین کد خود را به یک هوش مصنوعی انداختم و به آن گفتم که به من یک بررسی بدهد. در اینجا نتایج وجود دارد:
موضوعات مهم:
-
main()
600+ خط بدون تجزیه بود -
منطق بازی ، ورودی و ارائه همه در
main()
-
کد تکراری (مرطوب)
-
رسیدگی به رویداد ناکارآمد
مسائل جزئی:
-
کد اظهار نظر بیش از حد
-
استفاده بیش از حد از اعداد جادویی
-
بدون استفاده از خطای دارایی
-
منطق سخت و سخت به دلیل کمبود ضامن
پس چه چیزی یاد گرفتیم؟
در کل ، این یک پروژه اول محکم بود. من یک تن یاد گرفتم و با چیزی قابل پخش دور شدم. مهمتر از همه ، من به خودم ثابت کردم که می توانم یک بازی را شروع و به پایان برسانم. این افزایش اعتماد به نفس به تنهایی باعث شد کل روند ارزش آن را داشته باشد.
این همه برای این هفته ، مردمی است. هفته بعد وقتی که به پرنده Flappy نگاه می کنیم می بینیم.
سایونارا