میزبانی کردن صفحات ایستا در گیت‌لب

وبسایت گیت‌لب (لینک) همچون سایر سرویس‌های مشابه، امکان میزبانی کردن پرونده‌ها و صفحات ایستا را به شما می‌دهد. در این پست، قصد داریم بررسی کنیم که چگونه می‌توان یک صفحه ایستا را در گیت‌لب میزبانی کرد.

ساخت مخزن

پس از ساخت حساب کاربری در گیت‌لب، یک مخزن ایجاد کنید. برای مثال، اگر نام کاربری شما username است، پیشنهاد می‌شود که نام مخزن را username.gitlab.io بگذارید. این مخزن، می‌تواند عمومی یا خصوصی باشد (معمول است که مخازن وبسایت یا وبلاگ شخصی خصوصی ساخته شوند. اما اگر نیاز است که روش ساخت، شیوه‌نامه یا جاوااسکریپت خود را با دیگران شریک شوید، بهتر است مخزن را عمومی کنید).

ساخت پرونده‌های مورد نیاز

پس از این که مخزن ساخته شد، پوشه‌ای روی رایانه خود ایجاد کرده و برای مثال نامش را username.gitlab.io بگذارید. سپس داخل پوشه، پرونده‌ها را به این شکل ایجاد کنید :

username.gitlab.io/
├── .gitlab-ci.yml
└── index.html

توجه کنید که ممکن است پرونده‌های شما بیشتر نیز بشوند، برای مثال پوشه‌ای برای شیوه‌نامه‌ها، پوشه‌ای برای جاوااسکریپت و … ممکن است نیاز داشته باشید. فلذا هرچه کنار index.html نیاز است را درون این پوشه قرار دهید.

ویرایش پرونده gitlab-ci

این پرونده، به شما کمک می‌کند که پرونده‌های ایستای خود را، روی گیت‌لب قرار دهید. به عبارتی، کاری که این پرونده انجام می‌دهد «یکپارچه‌سازی مستمر» یا Continuous Integration است. درون این پرونده، باید مراحل «استقرار» یا Deploy پروژه، تعریف شود. برای این منظور، از YAML استفاده می‌شود.

برای استقرار یک صفحه ایستا، تنها کافیست به این شکل، این پرونده را ویرایش کنید :

pages:
 stage: deploy
 scirpt:
  - mkdir .public
  - cp -r * .public
  - mv .public public
 artifacts:
  paths:
   - public
 only:
  - master

در قسمت اول، می‌گوییم که قرار است روی Gitlab pages چیزی ساخته شود. در قسمت stage مشخص می‌کنیم که قرار است پرونده‌های تولید شده، کجا قرار بگیرند.

در قسمت script ابتدا یک پوشه مخفی می‌سازیم و سپس تمام محتوا را به آن منتقل می‌کنیم. علت مخفی بودن این پوشه، این است که دستور cp تلاشی برای کپی کردن خود پوشه نکند.

پس از آن، این پوشه را از حالت مخفی خارج کرده و سپس، در قسمت artifacts مشخص می‌کنیم که مسیر مورد نظر؛ پوشه public است. پس از انجام تمام مراحل کافی است که در بخش only مشخص نماییم فقط از شاخه master پرونده‌ها را خوانده و به پوشه public منتقل کند.

استفاده از تولید‌کنندگان صفحات ایستا

بسیاری از افراد، از «تولیدکنندگان صفحات ایستا» یا Static Generator ها استفاده می‌نمایند. استفاده از این ابزارها، بسیار سریع و به صرفه بوده و معمول است که جای درگیر شدن با طراحی ایستا، از این‌ها استفاده شود. شما می‌توانید از تولیدکنندگان صفحات ایستا مانند jekyll یا hugo روی گیت‌لب استفاده نمایید. در آموزش بعدی، بررسی می‌کنیم که چگونه می‌توان یک بلاگ با جکیل روی گیت‌لب ایجاد و مدیریت کرد.

Share

نگاهی کلی به کد منبع ماستودون

مدتی پیش، خواستم یک نمونه شخصی از ماستودون بسازم. موقع نصب، ارورهای عجیب و غریب دریافت کردم و خب شروع کردم به جست و جو پیرامون این که چرا اینطوریه و خیلی‌ها واضحا بیان کردند که «مشکل از تصویر داکر PostgresSQL بوده و باید روی اون تصویر کمی تغییر داده شه».

حقیقتا من از PGSQL استفاده نکردم و خیلی هم دلم نمیخواست وارد قضیه بشم و روی داکرفایل و داکر کامپوز و …، تغییر ایجاد کنم. به نظرم همون چیزی که خود توسعه‌دهنده پیشنهاد می‌کنه، معمولا بهترین حالتیه که داره با نرم‌افزار کار می‌کنه. اما یه ایده‌ای به سرم زد و اون هم این بود که چی میشه اگر پشتیبانی از MySQL رو به این بزرگوار اضافه کنم؟ نتیجه این شد که تصمیم گرفتم این مطلب رو بنویسم 😀

ماستودون چیه؟

ماستودون یک شبکه اجتماعی از نوع «فدریتد» (انگلیسی : Federated) محسوب میشه. به این شکل که وقتی شما روی سرور خودتون هم اون رو اجرا کنید، می‌تونید باقی سرورها رو ببینید و تعامل کنید.

مدتی میشه که خیلی از دوستانم، ماستودون رو بعنوان شبکه اجتماعی اصلی استفاده می‌کنند و خیلی کم‌تر به توییتر میان. شاید هم کلا توییتر نیان. یکی از دلایلش همینه که شما، صاحب داده‌های خودت هستی. این یک ایده خیلی خوبه که اگر واقعا حریم خصوصی و گردش اطلاعات براتون مهمه، به سمت ماستودون مهاجرت کنید.

نگاه به کد و چندکلمه‌ای صحبت پیرامون اون

خب از اینجا اصل ماجرا شروع میشه. کد ماستودون با فرمورک روبی آن ریلز (لینک) نوشته شده. این برای من خیلی خوشحال‌کننده بود چون خوندن و تغییر دادن اون کد رو آسون می‌کرد. اما این هم باید در نظر گرفت که کد، به شدت بزرگه و حتی برای حرفه‌ای‌های روبی آن ریلز هم خوندنش کمی دشواره. بهرحال می‌شه یکم حوصله به خرج داد و کد بزرگ رو درک کرد. این مشکل کد نیست. بلکه مشکل اون ساختار MVC محسوب میشه که بعد یه مدتی کد رو به شدت بزرگ می‌کنه و پیمایش و رفع اشکالش رو سخت.

در نگاه اول، کد واقعا ایراد خاصی (جز ایرادات مرسوم پروژه‌های مشابه) رو نداره. اما اولین ایراد جایی خودش رو نشون میده که شما چندین کتابخونه سیستمی اضافی نیاز دارید. برای من libicu و libidn و اینا بود. یکم جا خوردم که چرا باید این‌ها نصب باشه؟ اما با کمی تحقیق در موردش، فهمیدم که یک سری از کتابخونه‌های روبی هستند که وابسته به این بزرگوارهان. در واقع، هردوی این‌ها به شکلی برای پردازش متن استفاده شدند و خب این که پردازش متن، از طرف سیستم انجام بشه خیلی منطقیه. این موضوع فقط کمی باعث بزرگتر شدن پروژه شده. اما خوبی‌هاش، به شدت بیشتر از بدی‌هاشه.

مورد بعدی اروری بود که سر PGSQL گرفتم. خب حقیقتا انتظار این یکی رو داشتم. چون PGSQL و کتابخونه‌های سیستمیش، روی سیستم من نصب نیستند (البته شاید بعدها مجبور شم برای اشکال زدایی و کمک به همین ماستودون، نصبش کنم) و خب اروری که گرفتم دقیقا همین رو می‌گفت. وقتی مطمئن شدم که هیچ کتابخونه دیگری مشکلی نداره، دست به کار شدم که PGSQL رو با MySQL عوض کنم.

پروسه تغییر سیستم مدیریت پایگاه داده

خب معمولش اینه که اول، به پروژه می‌گیم کتابخونه‌های لازم رو نصب کنه. نیاز بود روی سیستم libmysqlclient-dev هم نصب کنم و کردم. بعدش در Gemfile، جم‌های مربوط به مدیریت MySQL رو اضافه کردم و جم‌های مربوط به PGSQL رو به دیدگاه تبدیل کردم و رفتم سراغ مراحل ساخت پروژه.

متاسفانه، اینجا کلی ارور گرفتم سر یک سری جم خاص! جم‌هایی که مرتبط با PGSQL هستند و شدیدا Hard Code شدن رو در کد منبع، شاهد بودیم.

قسمتیش رو دستی حذف کردم به امید این که کار کنه. اما موقع اجرای Migration ها، ارورهای جدیدی گرفتم. قسمت بزرگی از Migration ها رو چک کردم و دیدم که یه جاهایی حتی به صورت دستی دستورات PGSQL نوشته شده. حس می‌کردم که دیگه از اینجا جلوتر رفتن، یه جورایی مسخره کردن خودم باشه. حقیقتا بود، چون اصلا آشنا نبودم به این که اینجا چه اتفاقی داره می‌افته و خب بیخیال اضافه کردن پشتیبانی MySQL به این پروژه شدم.

جمع‌بندی

همونطوری که عرض کردم خدمت شما، بزرگترین اشکال این کد اینه که شدیدا وابسته به PGSQL عه. اگر اینقدر هاردکد نمی‌شد، شاید وضعیت طور دیگری بود و می‌شد پشتیبانی پایگاه‌های دیگر رو بهش اضافه کرد. متاسفانه در issue هایی که در پروژه بود هم دیدم که خیلی صریح پیشنهادات مرتبط با MySQL رو رد کردند و امیدم رو به طور کلی به این پروژه برای اضافه کردن این دیتابیس، از دست دادم.

یک توصیه نهایی بخوام به دوستان توسعه‌دهنده کنم اینه که : سعی کنید حداقل در زمینه دیتابیس، وابستگی رو به حداقل خودش برسونید. اول از همه با این فرض که ممکنه هرلحظه شما به هردلیلی بخواید سیستم رو عوض کنید. پس هرقدر وابستگی کمرنگ‌تر باشه، بیشتر به نفع خودتونه.

مورد بعدی هم اینه که اگر نفر بعدی به هر دلیلی خواست با سیستم متفاوتی پروژه رو اجرا کنه، انقدر سختش نباشه. بهرحال، همه قرار نیست همه چیز رو اونطوری که من و شما دوست داریم، استفاده کنن.

Share

ساختن یک API و چیزهایی که از آن یاد گرفتم

مصادف با عید قربان امسال، پروژه API ترجمه فارسی قرآن کریم رو منتشر کردم. این پروژه، پروژه سختی برای من نبود اما چیزای جالبی ازش یاد گرفتم که خب بد نیست در موردش مطلبی بنویسم. اما قبل از اون، لازمه یک چیزی رو توضیح بدم …

چه شد که این پروژه رو شروع کردم؟

حقیقتش شروع این پروژه به این دلیل بود که شدیدا دنبال ابزار مشابهی می‌گشتم. با این که بعد از این که این پروژه رو دپلوی کردم، یک دوستی در توییتر به من پیشنهاد استفاده از این ابزار رو داد؛ اما بهرحال من ابزار رو توسعه داده بودم و دپلوی هم کرده بودم.

خلاصه که، شروع این پروژه برای من جالب بود. چرا که پروژه به خودی خود سخت نبود اما وسطاش، دقیقا جایی که داشتم فرانتند براش میساختم، چالشی شد. به همین خاطر تصمیم گرفتم که این مطلب رو بنویسم و توضیح بدم که چرا این چالش برای من پیش اومد 🙂

مفهوم تازه : CORS

خب، وقتی که ما بکند و فرانتند پروژه رو از هم جدا می‌کنیم، در واقع داریم دوتا origin مختلف رو به هم وصل می‌کنیم. طبق استانداردهای وب؛ اصولا چنین کاری در مرورگرها؛ بخاطر مسائل امنیتی مجاز دونسته نشده. به همین خاطر، ما مفهوم تازه‌ای به اسم «چند ریشه‌ای» یا Cross-Origin رو خواهیم داشت.

حقیقتا وقتی فرانتند رو با جاوااسکریپت می‌نوشتم؛ دیدم که یک سری ارور بهم میده و توی اون ارورها بهم میگه که بکند من، برای «اشتراک گذاری منابع چندریشه‌ای» یا Cross-Origin Resource Sharing تهیه نشده. دوست داشتم از خود کد جاوااسکریپتم درستش کنم اما چون قرار بود روی هاست همین وبلاگ قرار بگیره و نه سرور مجزایی؛ ترجیح دادم که به جای node.js از وانیلا جی اس استفاده کنم (توضیح : وانیلا اصطلاحا به نرم افزار یا ابزار دست نخورده گفته میشه) و خب راهکاری براش پیدا نکردم (شاید هم چون درست دنبالش نگشتم) ولی از اونجایی که بکند رو با روبی و سیناترا نوشته بودم، براش راهکار پیدا کردم و این راهکار رو در یک ویرگول به اشتراک گذاشتم (لینک).

همین یک کار کوچک، باعث شد که من مفهوم کورز و صد البته نحوه کانفیگ درستش در بکند رو یاد بگیرم، از این جهت هم واقعا خوشحالم چرا که اکثر توسعه دهنده های فرانت، مدام از این قضیه که در بکند این مشکل حل نمیشه، نالانند ((:

چیزی که سالها عقبش انداخته بودم : زرین پال!

تقریبا از وقتی که فهمیدم اینترنت چیه، دنبال راهی برای کسب درآمد ازش بودم. فکر کنم دوم سوم دبیرستان بودم که سایت زرین پال به وجود اومد و اون موقع (چون به سن قانونی نرسیده بودم) نمی‌تونستم ازش استفاده کنم؛ یا این که باید از کسی میخواستم که قبول کنه به اسمش اکانت باز کنم (مثلا پدر یا مادرم).

از وقتی به سن قانونی رسیدم هم هرروز میخواستم برم و اکانت زرین پال درست کنم تا ازش برای حمایت و فروش و … روی اینترنت استفاده کنم. خلاصه که بالاخره همزمان با ریلیز این پروژه، این اتفاق هم افتاد ((: تازه دقیقتر بخواهیم حساب کنیم، برای این مساله هم مجبور شدم کارت بانکیم رو تمدید کنم و هم برای کارت هوشمند ملی اقدام کنم!

به طور خلاصه، این پروژه پروژه جالبی برام بود. هم چالشاش هم ساختارش و هم اتفاقات پیرامونش. ضمن این که به خود پروژه هم ابتدای مطلب لینک دادم و امیدوارم بخونید و خوشتون بیاد. اگر هم از پروژه قراره استفاده‌ای کنید، حمایت هم بکنید ممنون میشم (:

Share

به روز رسانی اوبونتو از نسخه های LTS به STS

توزیع اوبونتو، معمولا در دونسخه ارائه میشه. نسخه های «پشتیبانی طولانی مدت» یا همون LTS (مخفف Long Term Support ) که اگر اشتباه نکنم هر کدوم تا پنج سال، پشتیبانی دریافت میکنند. این پشتیبانی به معنای پشتیبانی از سمت کنونیکال (شرکت سازنده این توزیع)، دریافت آپدیت های امنیتی و همچنین به روز شدن مستندات توزیع است.

نسخه های STS که طبق روتین ارائه اوبونتو هر شش ماه یه بار میان، قبلا دو سال پشتیبانی می‌شدند ولی مثل این که الان این پشتیبانی کمتر شده و همون شش ماه (یا یک سال، دقیقا نمیدونم و یادم نیست) پشتیبانی رو دریافت میکنن. این نسخه ها به قول معروف «روی لبه تکنولوژی حرکت میکنند» و معمولا تکنولوژی های جدیدی رو در خودشون جای دادن.

هر کدوم از این نسخه ها، مزایای خودش رو داره. خیلی از افراد رو دیدم که با هر آپدیتی، سریعا سیستم عامل خودشون رو آپدیت میکنند و خیلی ها هم ترجیح میدند دو سال یک بار، نسخه های LTS رو آپدیت کنند و یا حتی کل پنج سالی که برای LTS ها پشتیبانی در نظر گرفته شده، روی اون بمونن. اما برگردیم به دسته اول!

فرض کنیم شما آخرین اوبونتوی LTS (در حال حاضر ۱۸.۰۴ ) رو نصب کردید و میخواهید به STS جدید (در حال حاضر ۱۹.۰۴) به‌روز رسانی کنید. برای این کار، نیاز به چند مرحله داریم. اول لازمه که update-manager رو نصب کنیم :

sudo apt install update-manager-core

و البته معمولا این بسته روی اوبونتو نصب شده. فقط در صورتی که نصب نباشه، این دستور نصبش میکنه. نصب هم باشه یا به شما میگه که آخرین نسخه رو نصب کردید یا به روز رسانی میکنه.

گام دوم، اینه که به آپدیت منیجر، بگیم که به جای lts در نسخه های normal برامون دنبال ریلیز جدید بگرده. فقط کافیه این فایل رو با یک ویرایشگر متن باز کنیم (البته ترجیحا ویرایشگرهای خط فرمان؛ چون دسترسی ریشه نیاز داریم) :

/etc/update-manager/release-upgrades

سپس در این فایل، شما احتمالا این خط رو می‌بینید :

Prompt=lts

با تغییر مقدار متغیر prompt به normal ، شما به آپدیت منیجر گفتید که در نسخه های STS به دنبال ریلیز جدید بگرده. بعد از این که عبارت lts رو با normal جایگزین کردید، کافیه فایل رو ذخیره کرده، ببندید و در ترمینال تایپ کنید :

do-release-upgrade

و جایی هم نیاز به پسورد داشته باشید، این دستور ازتون دریافتش خواهد کرد.

این راه حلی بود که خودم دقیقا استفاده کردم برای آپدیت به ۱۹.۰۴. نمیدونم قبلتر کسی در وب فارسی توضیحش داده یا نه، ولی خب امیدوارم که به کارتون بیاد 🙂

Share

ساختن کنترلر دو بعدی برای بازی های RPG در یونیتی

مدتی میشه که یکی از اسباب بازی های جدیدم، یعنی موتور بازی‌سازی یونیتی رو با جدیت بیشتری دارم دنبال میکنم و چیزای جالبی هم ازش یاد گرفتم. همینطور باعث هم شده دوباره کمی کد بزنم و این داستانها.

یکی از سبکهای مورد علاقه من در بازی‌سازی، سبک Role Playing عه که در این سبک، شما در واقع نقش ایفا می‌کنید، نقش هایی که به شما محول شده و یا این که شما هم میتونید در شکل‌گیری نقش دخیل باشید. بازی‌هایی مثل Elder Scrolls از این دست بازی ها هستند. چندی پیش، با یک ابزار آشنا شدم به اسم RPG Maker که به شما اجازه ساخت این دست بازی ها رو بدون کد زدن میده. اما یک مساله مهمی که احتمالا باهاش روبرو خواهید شد، اینه که این ابزار شدیدا محدوده و به سختی میشه شخصی‌سازیش کرد. به همین خاطر، سعی کردم دنبال این برم که یاد بگیرم چطور یک RPG دو بعدی ساده رو بسازم (حتی شده در حد کنترل کرکتر اصلی!).

نتیجه این شد که یک سری ویدئو دیدم و رفرنسهای برنامه نویسی یونیتی رو چند بار مطالعه کردم و تونستم چنین چیزی بسازم :

نوشتن چنین کدی کار سختی نیست اما کمی دقت لازم داره. به همین خاطر، چندین ساعت درگیرش شدم و در نهایت تونستم اونطوری که دلم میخواد، بسازمش. تمام مراحل طراحی کرکتر، طراحی صحنه، کد زدن و … رو خودم انجام دادم تا ببینم چقدر در جوانب مختلفش، توانایی دارم. خوشبختانه نتیجه خیلی هم بد نشد.

برنامه نویسی

برای کنترل کردن کرکترها در بازی، نیاز به کدی داریم که بتونه دیتا رو از کی‌برد، ماوس، صفحه لمسی و … دریافت کنه و حرکتی که مد نظر داریم رو برای ما، انجام بده. این حرکت میتونه صرفا جابجایی باشه، میتونه هم حرکت و انیمیشن با هم باشه. برای این که کد کامل باشه و بازی حرفه‌ای به نظر برسه، بهتره که انیمیشن هم لحاظ کنیم.

کدی که من نوشتم به این شکله :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : Entity {

    private Animator anim; 

	// Use this for initialization
	void Start () {
        anim = GetComponent<Animator>(); 
	}
	
	// Update is called once per frame
	void Update () {
		if(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)){
			GetComponent<Rigidbody2D>().transform.position += Vector3.up * speed * Time.deltaTime; 
		}
		if(Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow) )
        {
			GetComponent<Rigidbody2D>().transform.position += Vector3.down * speed *Time.deltaTime; 
		}
		if(Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow) )
        {
			GetComponent<Rigidbody2D>().transform.position += Vector3.left * speed * Time.deltaTime; 
		}
		if(Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow) )
        {
			GetComponent<Rigidbody2D>().transform.position += Vector3.right * speed * Time.deltaTime; 
		}

        anim.SetFloat("MoveX", Input.GetAxis("Horizontal"));
        anim.SetFloat("MoveY", Input.GetAxis("Vertical")); 
	}
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

این کد نشون میده که کلاس PlayerController از کلاس دیگری به اسم Entity داره ارث بری میکنه. تنها چیزی که در Entity تعریف شده، یک متغیر برای سرعت کرکتره. البته، سایر ویژگی هایی که نیاز داریم تا در تمام کرکترها (چه Player و چه NPC ) به کار ببریم رو میتونیم در Entity بگنجونیم.

در Update اومدیم بهش گفتیم که اگر ورودی از کلیدهای خاصی بوده، با سرعت مشخصی در جهتهای مشخص، حرکت کنه. متغیر speed هم یک متغیر public بوده که در Entity تعریف کردیم. در انتها هم گفتیم که دو متغیر ممیز شناور برای حرکت در محور X و Y نیاز داریم که با Animator Controller هماهنگ باشن. این میشه کل چیزی که در متد Update داریم.

در متد اول، یعنی Setup هم صرفا به کد گفتیم که انیماتور رو از خود شیء Player بخونه و لود کنه. پس ما نیاز داریم که خودمون یک انیماتور براش بسازیم.

چطور این کد رو آزمایش کنیم؟

آزمایش کردن این کد، هیچ کاری نداره. چون کل انیمیشن و … رو در گیتهاب قرار دادم (لینک) و میتونید از گیتهاب دانلودش کرده و تست کنید. به زودی هم، این ابزار رو به صورت یک prefab برای یونیتی منتشر میکنم که بتونید مستقیما از همین دیزاین و انیمیشن، بدون چسبوندنشون به هم، استفاده کنید.

موفق باشید 🙂

Share

صداگذاری بازی کامپیوتری در یونیتی – بخش دوم

در بخش گذشته؛ با نحوه صداگذاری و ورود به Trigger و همچنین اضافه کردن موسیقی متن به بازی، آشنا شدیم. در این بخش میخوایم کمی پا رو فراتر بذاریم و Dynamic Soundscape رو هم یاد بگیریم. فقط قبل از اون باید کمی مفاهیم آهنگسازی/صداگذاری رو با هم مرور کنیم.

مفهوم Soundscape

فضاهایی که توسط صدا ساخته میشن، یا بهتر بگم؛ صدایی که باعث میشه فضایی خاص به ذهن ما بیاد رو بهش میگن Soundscape. مفهوم پیچیده‌ای نیست ولی میتونه به پیچیده شدن سایر مفاهیم یا حتی هموار تر شدن فهمشون، کمک کنه.

در تصویر بالا، چند Soundscape رو مشاهده میکنیم. یکی برای موج، یکی برای باد، یکی برای پرندگان و … . اما آیا همیشه Soundscape ها، صداهای محیطی (Ambient ) هستند؟ خیر. بعضی وقتا هم قطعات کوتاه و کوچک موسیقی هستند که به ما در درک شرایط کمک میکنن (برای اهل فنش : مثلا یه ویولن ترمولو که یه آرپژ مینور رو بزنه، میتونه کمی ترس یا نگرانی رو القاء کنه). پس، ما نیاز به Soundscaping داریم که بتونیم با صداها؛ حس القاء کنیم.

مفهوم Dynamic Soundscape

خب اینجا کمی مفهوم میتونه پیچیده باشه، اصلا چرا میگیم داینامیک؟ مشخصه، چون قرار نیست همیشه یکسان باشه. پس به چه دردی میخوره؟ فکر کنید در بازی شما صحنه‌ای وجود داره که قراره صدای آب پخش بشه، پس باید با نزدیک شدن به آب، صدای آب کم کم بلند بشه و به گوش برسه، و وقتی کارمون با اون مرحله تموم شد، صدای آب قطع بشه. این رو بهش میگیم Dynamic Soundscape . در یونیتی به سادگی میتونیم این مفهوم رو پیاده کنیم .

مفاهیم مورد نیاز در یونیتی

مفهوم Audio Mixer

میکسر، مفهومش تقریبا همه جا یکیه، پس اگر با Reaper, FL Studio, Studio One و نرم افزارهای آهنگسازی دیگر (هزاران نرم افزار دیگه!) کار کردید، با این مفهوم آشنایید پس این بخش رو می‌تونید ازش عبور کنید . اما به طور کل، ما از میکسر استفاده میکنیم جهت این که صداها رو به کانالهای مختلف بفرستیم. در این کانال ها میتونیم بلندی صدا، تغییراتشون و همچنین افکتهایی که نیاز داریم رو کنترل کنیم.

در یونیتی هم امکان ایجاد و مدیریت تعداد بسیار زیادی میکسر وجود داره، گذشته از اون ما میتونیم یه سری میکسرها رو Master در نظر بگیریم و یه سری دیگه رو زیرمجموعه اونا حساب کنیم. به این شکل، تقریبا میشه گفت که خود یونیتی داره به ما یه استودیوی کامل صداگذاری رو تحویل میده.

شروع پروژه

خب، اول از فولدر Assets با راست کلیک، یک Audio Mixer ایجاد کنیم و اسمش رو بذاریم Master (قراره همه میکسرهای دیگرمون زیرمجموعه این باشن) :

بعد از منوی Window گزینه Audio Mixer رو فعال میکنیم و به این شکل اون رو خواهیم دید :

حالا که یک Master داریم، دو گروه دیگه (از زیرمجموعه Groups )  ایجاد میکنیم به اسم Soundtrack و Sphere . از Soundtrack برای کنترل موسیقی متن (چطوری؟! یاد میگیریم) استفاده میکنیم و از Sphere برای کنترل موسیقی که کنار Sphere ما پخش میشه.

حالا میریم سراغ دوربینمون و Audio Source ای که بهش اضافه کردیم ، یک گزینه Output داریم، اون رو روی Soundtrack قرار میدیم (به همین راحتی، موسیقی/صدا رو در یک کانال از میکسر فرستادیم!)

و به همین ترتیب، موسیقی که روی Sphere قرار دادیم رو هم به کانال Sphere میفرستیم.

استفاده از Snapshot ها

میکسرها قابلیتی دارن به اسم Snapshot که میتونیم وضعیتی رو از اون میکسرها، ثابت نگه داریم. در واقع انگار که عکس گرفته باشیم (از اسم مشخصه). و به کمک همین Snapshot هاست که میشه وضعیت صدا رو کنترل کرد. در ادامه، کد و روش کار رو با هم یاد میگیریم.

نوشتن اسکریپت Dynamic Soundscape

قبل از هرچیزی، اسکریپتی که در بخش قبل نوشتیم رو از Sphere حذف میکنیم و فایل صوتی رو مستقیم به Audio Source میدیم. سپس به Audio Mixer بر میگردیم و این طور تغییرش میدیم :

با اجرای بازی، الان فقط میتونیم صدای موسیقی متن رو بشنویم. پس میریم به قسمت Snapshots و روی + کلیک می‌کنیم و اسمش رو میذاریم Snap1 . سپس، اون Snapshot ستاره دار (به نوعی Master در اسنپ شاتها!) رو به این شکل تغییر میدیم :

و باز یک Snapshot جدید به اسم Snap2 ایجاد میکنیم. حالا در Asset ها یک C# Script تازه ایجاد میکنیم به اسم DynamicSoundscape . و این ها رو درونش مینویسیم :

در خط چهارم من UnityEngine.Audio رو اضافه کردم، این به این معناست که میتونم از Snapshot ها هم استفاده کنم. در خط ۷ و ۸ هم دو تا Snapshot ساختم به اسم fadeIn (برای آرام بلند شدن صدای مورد نظر) و fadeOut (برای آرام کم شدن صدا)، سپس تعریف کردم که وقتی وارد Trigger شدیم و تگمون هم Player بود، وارد fadeIn بشه و وقتی ازش خارج شدیم، وارد fadeOut . حالا این اسکریپت و دو اسنپ شاتی که ساختیم رو به Sphere میدیم :

حالا وقتشه که بازیمون رو اجرا کنیم و نتیجه رو به چشم ببینیم 🙂

نتیجه کارمون به صورت GIF به این شکله (به تغییر کانالهای میکسر دقت کنید 😀 )

مسلما این همه اون چیزی نیست که برای صداگذاری باید بدونید، ولی به نظر من مهم ترین بخشه. امیدوارم در این دو بخش، تونسته باشم به خوبی پوشش بدم مطلب رو. در آینده هم احتمالا باز مطالبی در مورد کار با یونیتی خواهم نوشت، و اینها آخرین مطالب من در یونیتی نیستن. بهرحال، امید به این که مفید بوده باشه و امید به موفقیت شما 🙂

Share

صداگذاری بازی کامپیوتری در یونیتی – بخش اول

بحث صدا و موسیقی در بازی کامپیوتری، بحث عجیب و گهگاه پرچالشیه. چرا که صداها میتونن جو و هدف بازی ما رو تعیین کنند و بهش فضای بالاتری اختصاص بدن. در این مطلب، قصد دارم بخشی از تجربیات خودم در زمینه صداگذاری (و نه آهنگسازی!) در بازی های کامپیوتری رو با شما به اشتراک بذارم. در این مطلب، چیزی که بررسی میکنیم : ۱. قرار دادن موسیقی متن بازی و ۲. پخش صدا در Trigger های خاص خواهد بود. پس در ادامه با من باشید 🙂

قرار دادن موسیقی متن بازی

اول یک پروژه به اسم Audio Test درست میکنیم، سپس وارد Scene اصلی میشیم تا این صفحه یونیتی رو ببینیم :

حالا باید دو کار انجام بدیم، از Asset های پیشفرض، یک First Person Controller برداریم و Main Camera رو هم پاک کنیم. ضمن این که لازمه حتما یک «زمین بازی» بسازیم. بعد از این کارها Scene من این شکلی شد :

وقتی روی FPSController کلیک کنیم و اولین زیرمجموعه‌ش رو در Inspector ببینیم با این صحنه مواجه میشیم :

همونطور که می‌بینید، در این Inspector ما عنصری به اسم Audio Listener داریم. Audio Listener کارش «شنیدن صدا»ست و روی دوربین سوار میشه. کاراکتر اول شخص هم معمولا همراه خودش یک دوربین داره، پس خیالمون از بابت «شنیده شدن» صدا، راحته. حالا روی Add Component کلیک می‌کنیم و یک Audio Source بهش اضافه میکنیم و سپس، موسیقی متن مورد نظر رو به عنوان Audio Clip بهش معرفی می‌کنیم (در اینجا دوئت پیانو و ویولن که چند وقت پیش ساخته بودم رو معرفی کردم) و تیک Loop هم میزنیم :

(البته اینجا تیک Loop رو نزدم) و حالا تبریک! موسیقی متن بازی شما نشست روی بازی. ولی این همه ماجرا نیست. ماجرا در کل خیلی پیچیده تر میشه ولی این اولین چیزیه که ما باید یاد میگرفتیم (مثلا بعدها در مورد این که چطور موسیقی داینامیک استفاده کنیم و … هم بحث خواهد شد).

حالا که این رو یاد گرفتیم، بریم سراغ اضافه کردن یک لایه صدا که در یک Trigger خاص قرار می‌گیره.

ساخت Trigger های صدا

در Scene یک Sphere میسازیم، یک متریال دلخواه بهش میدیم، یک Collider از نوع Trigger براش تعریف می‌کنیم و نتیجه میشه این :

خب، محتوای Inspector باید مثل این باشه (البته الزامی نیست، صرفا خواستیم که محدوده Collider مشخص باشه). الان کاری که باید بکنیم، اینه که یک Audio Source و یک Script به این Sphere اضافه کنیم (زبان اسکریپت رو سی‌شارپ تعیین کنید). حالا کافیه در اسکریپتمون چنین چیزی بنویسیم :

این اسکریپت، میگه که «اگر چیزی وارد Collider من شد و تگ Player داشت، کلیپی که معرفی شده رو یک بار پخش کن». پس به Inspector باید بریم و برای FPSController مون، یک تگ Player تعریف کنیم. سپس، به Script (و نه Audio Source ) باید یک Clip اختصاص بدیم، به این شکل :

و خب کار تموم شد! این فایل صوتی هم با ورود ما به Trigger تعریف شده، پخش خواهد شد!

چه چیزهایی مونده که هنوز باید یاد گرفت؟

موضوعی که اینجا بهش پرداخته شد خیلی خیلی سطحی بود. برای کار صدا در یونیتی، هم باید با Audio Mixer کار کنیم هم Audio Mixer Snapshot . گذشته از اون، باید با اسکریپت؛ اینها رو به هم مرتبط کنیم، به شکلی که یه جا موسیقی آروم قراره پخش بشه و بعد موسیقی یک صحنه خوفناک وارد بشه و … . سعی می‌کنم در قسمت (های) بعد، این موارد رو هم پوشش بدم.

موفق باشید 🙂

Share

امنیت حسابهای اجتماعی

امروز صبح در تلگرام پیامی برای من فوروارد شد که حاوی یک اسکرین شات از توییت خود شرکت توییتر بود. این اسکرین شات :

همونطوری که می‌بینید، توییتر میگه یک باگ پیدا کردن که باعث شده یک سری پسوردها، بدون این که ماسک بشن، لاگ شدن. یعنی چی؟

فرض کنیم ما یک پسورد میسازیم، پسورد ما موقعی که داریم وارد میکنیم یک چیزی مثل MySecurePassword@123456 عه. این پسورد تقریبا تمام فاکتورهای یک پسورد امن رو داره. ولی اگر به صورت Plain Text بخوایم ذخیره‌ش کنیم این که جای خود، یک میلیون کارکتر دیگر هم داشته باشه احتمال لو رفتنش بسیار بالاست. (داخل پرانتز عرض کنم خدمت آقای علیرضا شیرازی، مدیریت محترم بلاگفا که تا چند سال پیش هم همینطوری پسورد ها رو نگهداری میکرد، جای این که در توییتر و … ملت رو بخاطر مواخذه کردنتون بلاک کنید، پاسخ میدادید که چرا اینطور بود! چون همون موقع هم بسیاری از اسکریپت های مدیریت محتوا پسوردها رو ماسک می‌کردن!).
حالا بخوایم MySecurePassword@123456 رو در دیتابیسی ذخیره کنیم باید چه کنیم؟ باید از «هش» و الگوریتم های مربوطه استفاده کنیم. مثلا الگوریتم MD5 (لینک جهت مطالعه). حالا این عبارت نسبتا سخت رو من با کمک ابزارهایی که دم دستم بود به MD5 هش میکنم و نتیجه میشه :

23b7618b2a23e171e6cda057a9736423

و وقتی من پسورد رو وارد کنم، اسکریپتی که برای لاگین نوشتم، یا API ای که وظیفه لاگین کردن من رو برعهده میگیره، اول پسورد رو هش میکنه بعد در دیتابیسش چک میکنه و اگر هش یکی بود، اجازه میده من به سیستم دسترسی داشته باشم. برای مثال این یک سیستم لاگین بسیار ساده بود که خودم نوشته بودم :

کد   
post '/welcome' do
 begin 
  params[:user][:password] == params[:user][:password_again]
  user = User.create(:username => params[:user][:username], :password => Digest::MD5.hexdigest(params[:user][:password]))
  redirect to("/welcome")
 rescue
  redirect to("/signup_error")
 end
end

و این یعنی ذخیره ایمن پسورد در یک دیتابیس (در اینجا از دیتابیس های مونگو استفاده کردم). و حالا اگر اون قسمتی که مربوط به هش کردن پسورد نمیشد رو، در کد قرار میدادم چه میشد؟ وقتی قرار بود Query بزنم مثلا به یوزر mamad و پسوردش 1125 بود، اینطور بر میگردوند :

کد   
{
"username":"mamad", 
"password":"1125"
}

و مثلا اگر شما قرار بود با API ای که من برای عضوگیری سرویس نوشتم کار کنید (مثلا لاگین کردن به وسیله سرویس من در اپلیکیشن، بازی یا سیستم حضور غیاب و … )، پسوردها رو به سادگی میتونستید ببینید. اما چیزی که الان بر میگرده اینه :

کد   
{"username":"mamad","password":"c21002f464c5fc5bee3b98ced83963b8"}

و خب شما نمیتونید به پسورد دسترسی پیدا کنید، مگر این که ابزار یا دیکشنری مربوط به شکستن این الگوریتم رو داشته باشید. البته معمول هم نیست شما JSON مربوط به اطلاعات کاربران رو برگردونید! چون باز هم دیتای کاربر به خطر میفته.

فکر کنم به قدر کافی در مورد پسورد و نحوه ذخیره سازیش توضیح داده باشم، هدف این بود که مباحثی مربوط به هک شدن و نفوذ به تلگرام هم در این پست مطرح کنم که متاسفانه این پست بیش از چیزی که فکر کردم، طولانی شد. پس در مورد تلگرام در پست جداگانه ای خواهم نوشت 🙂

 

Share

مرگ وبلاگ نویسی، گشت و گذاری در سرویس های وبلاگ فارسی

شاید حدود ۱۰ سال پیش، وقتی به تازگی وارد ۱۲ سالگی شده بودم، شروع به ساخت وبلاگ در یکی از قدیمی ترین سرویس های وبلاگدهی فارسی، یعنی بلاگفا کردم. بلاگفایی که تا همین یکی دو سال پیش، به معنای واقعی از پرترافیک ترین وبسایت های فارسی به شمار میرفت.
دقیقا همان روزها، بلاگفا در صفحه اصلیش، دست کم دو تبلیغ نشان میداد، اخبارش و وبلاگ های بروز شده را لیست می‌کرد و گهگاه در شکل خبرهای فوری، امکانات حذف یا اضافه شده به سیستمش رو اطلاع رسانی می‌کرد. به این شکل مثلا فهمیدیم که مدتی blogfa.ir هم در دسترسه، و هر وبلاگی که شما بسازید هم در زیردامنه blogfa.com و هم blogfa.ir قرار میگیره.
البته، آن روزها پرشین بلاگ (قدیمی ترین سیستم بلاگدهی فارسی) و بلاگ اسکای و … هم رونق زیادی داشتند. در واقع، هرکس که میتونست هزینه های یک سرویس بلاگدهی (بخاطر سرور و منابع بالایی که نیاز داره و … ) رو تامین کنه و دانشی از برنامه نویسی وب داشت، یک سیستم بلاگدهی بالا میاورد. اما اونهایی که خیلی مطرح بودند، همین بلاگفا و بلاگ اسکای و پرشین بلاگ بودند.
دوستانی هم بودند که ترجیح میدادند – علیرغم فیلترینگ – از سرویس هایی مثل وردپرس یا بلاگر استفاده کنند، و ما هم با سختی و زحمت هایی که اون زمان برای عبور از سد داشتیم، وبلاگ ها رو میخوندیم و گهگداری هم اینتراکشنی داشتیم.

اما الان چه بلایی سر بلاگ ها اومده؟ امروز بعد از چند سال صفحه اصلی بلاگفا رو باز کردم! دیدم دیگه خبری از اخبار و لیست بلاگهای بروز شده نیست، همه‌ش شده لینک در صفحه اصلی، و صفحه اصلی صرفا شده یه محل برای لاگین کردن وبلاگ نویس ها!
لیست وبلاگ های بروز شده رو باز کردم و یکی دو تا وبلاگ رو خوندم، یکیشون دقیقا همین دهم آبان ویرایش شده بود و یکی هم حدود سه روز پیش. قبلا، این بروزرسانی ها، ساعتی بود. مثلا الان لیست رو میخوندیم، میدیدیم که در ۴ ساعت گذشته دست کم ۱۰ وبلاگ بروزرسانی شدند، اما الان این اختلافها به روز رسیدند!

گرچه، دلیل «مرگ وبلاگ نویسی» مشخصه، دیگه کسی نمیره از بلاگفا سرویس بگیره و بلاگ بنویسه، معمولا یه کانال تلگرامی میزنن و حرفاشون رو اونجا میزنن، دریغ از این که نمیدونن موندگار نیست، حداقل اونقدری که وبلاگ های روی بلاگفا هستند!

Share

آغاز به یادگیری هوش مصنوعی

در پست قبلی وبلاگ، در مورد چیستی هوش مصنوعی توضیح دادم. در این یکی پست، قصدم اینه که به شما بگم برای یادگیری هوش مصنوعی لازمه چه چیزهایی بلد باشیم و چه چیزهایی رو در طول زمان یاد میگیریم. خب، پس بریم سراغ این که برای هوش مصنوعی چه پیش‌نیازی لازمه، بعد کم کم بریم سراغ سیر یادگیری و … .

  • الگوریتم:
    این که بتونید برای یک برنامه، یک الگوریتم بهینه پیدا کنید یا پیشنهاد بدید، مهم ترین عامل در یادگیری و انجام پروژه های هوش مصنوعی به حساب میاد. اگرچه این مورد، همه جا کاربرد داره و کلا برای برنامه نویس و دولپر خوب شدن لازمش داریم؛ ولی اینجا لازمه که به الگوریتم و الفبای موضوع مسلط باشیم. پس، باید یاد بگیریم که چطور با استفاده از الگوریتم ها یک برنامه بسازیم. خب برای یادگیری الگوریتم (اگر بلد نیستید) پیشنهاد من کتاب CLRS هست. هم ترجمه این کتاب در بازار موجوده و هم زبان اصلیش در اینترنت هست.
  • برنامه نویسی :
    برای این که بتونید پروژه هوش مصنوعی انجام بدید باید برنامه نویسی بلد باشید؛ بهرحال بخشی از کامپیوتره و نمیشه ازش در رفت. گرچه ممکنه شما صرفا ایده پردازی یک پروژه هوش مصنوعی رو انجام داده باشید ولی موضوع مهم اینه که شما بتونید همون ایده رو هم چندین بار تست کنید و بعد ارائه‌ش کنید به یک تیم. پس، برنامه نویسی بلد بودن هم از شرایط یادگیری و انجام پروژه در هوش مصنوعی هست. پیشنهاد من هم برای یادگیری زبان، پایتونه که الان تبدیل شده به ابزار شماره یک پروژه های هوش مصنوعی.
  • علوم شناختی :
    علوم شناختی یا Cognitive Science ترکیبیه از روانشناسی، فلسفه ذهن، زیست شناسی مغز و علوم کامپیوتر. البته انقدر ها هم خلاصه نیست و من دارم انقدر خلاصه میگم. برای این که بتونیم پروژه های هوش مصنوعی بزنیم؛ لازم داریم که بلد باشیمش. گرچه طوریه که در حین یادگیری هوش مصنوعی هم، این موضوع رو یاد میگیریم. خیلی از ایده ها و … که در هوش مصنوعی (به ویژه شبکه های عصبی مصنوعی) مطرح شده، حاصل کار دانشمندان شناختی بوده.

شبکه عصبی مصنوعی
شبکه عصبی مصنوعی

 

  • با مغز انسان آشنا بشید!
    این مورد از مهم ترین مواردی هست که باید به عنوان کسی که کار هوش مصنوعی میکنه، بلد باشیم. کاری که ما میخوایم بکنیم این هست که یک سری اعمال انسانی مثل تفکر، تصمیم گیری و یادگیری رو برای ماشین پیاده سازی کنیم و ماشین ما قراره فکر کنه برای ما. پس، بهتره که ساختار مغز رو بشناسیم و باهاش آشنا بشیم. در این زمینه هم کتاب و رفرنس زیاد داریم.
  • از منابع مختلف استفاده کنید!!!
    و در آخر هم، استفاده از منابع متعدد مثل یوتوب، وبسایت های هوش مصنوعی و کتابها توصیه میشه. به این شکل شما میتونید به راحتی و بدون هیچ مشکلی، هوش مصنوعی یاد بگیرید و از انجام پروژه های هوش لذت ببرید.
Share