🎮 جزوه گودوت (Godot) – جلسه اول
🔰 پیشنیازها: گودوت، گرهها و مفهوم مختصات
گودوت یک موتور بازیسازی مدرن و متنباز است. اولین چیزی که باید درک کنید دستگاه مختصات در صحنه ۲ بعدی است. در گودوت مانند یک صفحه شطرنج، هر نقطه دارای یک موقعیت (x,y) میباشد.
📐 محور X (افقی) :
محور X از چپ به راست حرکت میکند. مقادیر مثبت → سمت راست صفحه، مقادیر منفی → سمت چپ صفحه. فرض کنید بازیکن شما در مختصات (۰,۰) وسط صحنه است، با افزایش X به سمت راست میرود.
📐 محور Y (عمودی) :
محور Y از بالا به پایین افزایش مییابد. مقادیر مثبت → پایین صفحه، مقادیر منفی → بالای صفحه. برخلاف ریاضیات معمول، در موتورهای بازی، Y مثبت به سمت پایین است.
(Y-) بالا ↑
↓ پایین (Y+)
🔰 گام صفر: آشنایی با محیط و مختصات
در گودوت هر چیزی یک «گره» (Node) است. برای جابجایی اشیا باید دستگاه مختصات را بلد باشیم. در صفحهٔ بازی دو بعدی، محور X از چپ به راست حرکت میکند: X+ سمت راست، X- سمت چپ. محور Y از بالا به پایین: Y+ به سمت پایین، Y- به سمت بالا. این برخلاف ریاضیات مدرسه است اما در تمام موتورهای بازی همین گونه است. برای تمرین، در گودوت یک گره Sprite2D اضافه کنید و مقدار Position را در Inspector تغییر دهید تا اثر را ببینید.
🧬 ۱- دستور extends (به ارث بردن) چیست؟
در برنامهنویسی شیءگرا، وراثت یعنی یک کلاس (یا گره) میتواند تمام ویژگیها و قابلیتهای یک کلاس دیگر را بگیرد. در گودوت، وقتی مینویسیم extends CharacterBody2D به موتور میگوییم: «اسکریپت من تمام امکانات گره CharacterBody2D را به ارث میبرد». این یعنی متغیرهایی مثل velocity و توابعی مثل move_and_slide() بدون تعریف دوباره در دسترس ما هستند.
✅ کاربرد extends در گودوت:
- همیشه اول اسکریپت باید extends بنویسیم – درست بعد از باز کردن فایل اسکریپت، اولین خط غیرتوضیحی حتماً extends است. به گودوت میگوید این اسکریپت به کدام نوع گره متصل میشود.
- مثال: اگر اسکریپت به Area2D متصل است مینویسیم extends Area2D. اگر به CharacterBody2D متصل است، دقیقاً همان نوع را مینویسیم. در غیر این صورت خطا میدهد.
- به ارث بردن یعنی توابع آمادهای مثل move_and_slide() یا add_child() را بدون کدنویسی مجدد دارید.
extends CharacterBody2D # وراثت از گره حرکتپذیر
⚙️ ۲- تابع (Function) چیست و چگونه بنویسیم؟
تابع یک بلوک کد است که یک کار مشخص را انجام میدهد. به جای اینکه چندین بار کد تکراری بنویسید، آن را داخل تابع قرار میدهید و هر وقت نیاز بود صدا میزنید. در گودوت، توابع با کلیدواژه func شروع میشوند.
func نام_تابع(پارامترها):
# بدنه تابع
return مقدار (اختیاری)
مثال معروف: _physics_process(delta) یک تابع ویژه است که گودوت به طور خودکار در هر فریم فیزیک (۶۰ بار در ثانیه) آن را اجرا میکند. برای حرکت بازیکن از همین تابع استفاده میکنیم. توجه کنید که _(underscore) نشاندهنده تابع داخلی و ویژه است.
# مثال ساده از یک تابع سفارشی:
func سلام_گفتن():
print("سلام! به بازی خوش آمدی.")
# صدا زدن تابع در جای دیگر:
سلام_گفتن()
📦 ۳- متغیر (var) در GDScript مثل پایتون
متغیر محلی برای ذخیره اطلاعات است. در گودوت (و پایتون) برای ساخت متغیر از var استفاده میکنیم. نیازی به تعیین نوع نیست؛ موتور خودش تشخیص میدهد. ساختار: var نام_متغیر = مقدار
📌 مثالهای کاربردی var:
- var speed = 300 # عدد صحیح (سرعت)
- var player_name = “قهرمان” # رشته متنی
- var position = Vector2(100, 200) # بردار دو بعدی
در کد حرکت بازیکن، از @export var speed = 300 استفاده کردیم. @export یک کلیدواژه خاص است که باعث میشود متغیر speed در پنل Inspector (بازرس) گودوت قابل تغییر باشد بدون اینکه نیاز به ویرایش کد باشد. برای مبتدیان، یعنی میتوانید سرعت را مستقیماً از محیط گرافیکی گودوت عوض کنید.
var امتیاز = ۰
var سرعت_حرکت = ۲۵۰
@export var speed = 300 # قابل تغییر در Inspector
🎮 کد نهایی حرکت بازیکن (تحلیل عمیق)
حالا که میدانیم extends، تابع و var چه هستند، کد زیر را کامل متوجه میشوید. این اسکریپت را روی گره CharacterBody2D قرار دهید.
extends CharacterBody2D # وراثت از گره حرکتپذیر
@export var speed = 300 # متغیر سرعت (قابل ویرایش در Inspector)
func _physics_process(delta): # تابع فریم فیزیک
var input_dir = Vector2.ZERO # بردار جهت حرکت (۰,۰)
# محاسبه ورودی افقی (راست و چپ)
input_dir.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
# محاسبه ورودی عمودی (پایین و بالا)
input_dir.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
# اگر طول بردار بیشتر از ۱ بود (حرکت مورب)، نرمال کن تا سرعت ثابت بماند
if input_dir.length() > 1:
input_dir = input_dir.normalized()
# اعمال سرعت به بردار velocity (که از CharacterBody2D به ارث رسیده)
velocity = input_dir * speed
# حرکت بده و برخوردها را مدیریت کن
move_and_slide()
– خط اول: اسکریپت ما تمام ویژگیهای گره CharacterBody2D را میگیرد.
– خط دوم: متغیر سرعت = ۳۰۰ پیکسل بر ثانیه. @export یعنی در بازرس گودوت قابل تغییر است.
– خط چهارم: _physics_process یک تابع است که ۶۰ بار در ثانیه اجرا میشود.
– خط پنجم: یک متغیر به نام input_dir از نوع Vector2 میسازیم که مقدار اولیه (۰,۰) دارد.
– خطوط هفتم و هشتم: با استفاده از Input.get_action_strength کلیدهای راست/چپ/بالا/پایین را میخوانیم. اگر کلید راست فشرده شود input_dir.x=1، اگر چپ فشرده شود -۱= input_dir.x .
– خط دهم و یازدهم: اگر هم راست و هم پایین فشرده شود طول بردار ۱.۴ میشود که باعث افزایش سرعت در حالت مورب میشود. با نرمال کردن طول را دوباره ۱ میکنیم تا سرعت یکسان بماند.
– خط چهاردهم: متغیر velocity (که از ارثبری آمده) را برابر حاصلضرب جهت در سرعت قرار میدهیم.
– خط آخر: move_and_slide() باعث جابجایی واقعی گره و برخورد با موانع میشود.
🛠️ گامهای عملی در گودوت
- ساخت پروژه: گودوت را باز کنید → New Project → نام MyFirstGame → Create Folder → Create & Edit.
- ساخت گره اصلی (بازیکن): در پنل Scene دکمه + را بزنید → گره CharacterBody2D را جستجو کرده و انتخاب کنید. نام آن را Player بگذارید.
- افزودن ظاهر (Sprite2D): روی Player کلیک راست → Add Child Node → Sprite2D. در Inspector کنار Texture یک تصویر ساده بارگذاری کنید (میتوانید یک مستطیل رنگی ۳۲×۳۲ بسازید).
- افزودن برخورد (CollisionShape2D): دوباره روی Player → Add Child Node → CollisionShape2D. در Inspector پایین Shape، New RectangleShape2D ایجاد کنید و اندازه آن را حدود ۴۰×۴۰ تنظیم کنید.
- اتصال اسکریپت: روی Player راست کلیک → Attach Script. نام player.gd را قبول کنید. کد حرکت را داخلش بنویسید (کدی که در بالا توضیح داده شد).
- ذخیره صحنه: Ctrl+S و نام player.tscn.
- تنظیم به عنوان صحنه اصلی: روی فایل player.tscn در FileSystem راست کلیک → Set as Main Scene.
- اجرا با F5: بازی اجرا میشود. با کلیدهای جهتها بازیکن را حرکت دهید.
📡 سیگنال چیست؟ (ارتباط بین گرهها)
سیگنالها مانند زنگ درب منزل هستند. وقتی کسی وارد میشود، زنگ به صدا در میآید. در گودوت، گره Area2D میتواند وقتی بازیکن وارد محدوده آن میشود، سیگنال body_entered را ارسال کند. برای ساخت خط پایان:
- یک گره Area2D جدید به صحنه اضافه کنید (نام Finish).
- زیر آن CollisionShape2D (به شکل مستطیل) و یک Sprite2D برای ظاهر اضافه کنید.
- روی گره Area2D کلیک کنید، تب Node را در کنار Inspector ببینید. روی سیگنال body_entered دوبار کلیک کنید.
- در پنجره باز شده، گره خود Area2D را انتخاب کنید و Connect را بزنید. کد زیر ساخته میشود:
extends Area2D
func _on_body_entered(body):
if body.name == "Player":
print("به خط پایان رسیدی! پیروز شدی.")
با این کار، به محض برخورد بازیکن با منطقه، پیام در خروجی چاپ میشود. شما میتوانید به جای print کارهای دیگری مثل بارگذاری صحنه بعد یا افزایش امتیاز انجام دهید.
📝 تمرین برای تثبیت یادگیری
✏️ تمرین ۲: مقدار speed را در Inspector به ۵۰۰ تغییر دهید و دوباره اجرا کنید، ببینید چه تغییری میکند.
✏️ تمرین ۳: یک دشمن ساده (Area2D) بسازید که با برخورد، پیام Game Over بدهد.
📌 جمعبندی جلسه اول
در این جزوه یاد گرفتید که extends به معنای ارثبری و دسترسی به امکانات گره پایه است و همیشه باید در ابتدای اسکریپت نوشته شود. تابع (func) یک بلوک کد برای انجام کارهای مشخص است و _physics_process یکی از مهمترین توابع برای حرکت بازیکن میباشد. متغیر var مانند پایتون برای ذخیره مقادیر بکار میرود و @export آن را در محیط گرافیکی قابل تغییر میکند. همچنین محور X و Y را درک کردید و توانستید یک کاراکتر متحرک بسازید. جلسه بعد: امتیازدهی، Autoload و ساخت چند سطح ساده. عالی کار کردید!
