مبتدئ → تطبيقي · 10 دروس

تطوير ألعاب 2D — من الفكرة إلى لعبة منشورة

تتعلّم أساسيات صناعة لعبة ثنائية الأبعاد بمحرك Godot المجاني — حلقة اللعبة، الشخصيات، الحركة، التصادم، النقاط، الواجهة، الصوت، واللمسات الأخيرة — وتخرج بلعبة مصغّرة جاهزة للنشر على itch.io.

كيف تدرس هذا المسار؟

  1. حمّل Godot المجاني وافتح المشروع التجريبي.
  2. اقرأ كل درس وطبّق الكود في المحرر.
  3. نفّذ المهمة — كل درس يقربك من لعبتك.
  4. أكمل الاختبار — التقدّم يُحفظ على جهازك.

محتوى أصلي من Mesue Learn — عملي ومناسب لسن +12 وما فوق.

1. حلقة اللعبة — كيف «تفكر» اللعبة كل ثانية

الهدف: تفهم Input → Update → Render قبل فتح المحرك.

كل لعبة — حتى البسيطة — تعيد نفس الدورة مراراً:

1) Input — اللاعب يضغط زراً أو يحرّك الماوس
2) Update — الكود يحدّث المواقع والنقاط والحالة
3) Render — الشاشة ترسم الإطار الجديد

هذه هي Game Loop. بسرعة 60 إطاراً في الثانية (FPS) تبدو الحركة سلسة.

في Godot:
• المشهد (Scene) = ملف يصف جزءاً من اللعبة (لاعب، عدو، قائمة)
• العقدة (Node) = قطعة بناء — Sprite2D، CharacterBody2D، Label...
• السكربت (GDScript) = منطق اللعبة بلغة تشبه Python

Godot مجاني ومفتوح المصدر — حمّله من godotengine.org. اختر Standard (ليس .NET) للمبتدئين.

  • Game Loop = إدخال → تحديث → رسم — بلا توقف
  • FPS أعلى = حركة أنعم (هدف شائع: 60)
  • Scene + Node = طريقة Godot لتنظيم اللعبة
  • GDScript سهل للمبتدئين 12+

صورة ذهنية

 [Input]
    |
    v
 [Update] ← GDScript
    |
    v
 [Render] → الشاشة
    |
    └── (تكرار كل إطار)

مختبر: حلقة على الورق

ارسم 3 إطارات متتالية: أين اللاعب؟ أين العملة؟ ماذا يتغيّر في كل إطار؟

مهمة عملية

اشرح لصديق في 4 جمل: ما الفرق بين Update و Render؟

اختبر فهمك

حلقة اللعبة (Game Loop) تعني أساساً...

2. الرسوم والمشاهد — Sprite2D وأول مشهد

الهدف: تنشئ مشروع Godot وتعرض صورة 2D على الشاشة.

1) New Project → اسم: mesue-mini-game
2) Renderer: Forward+ أو Mobile حسب جهازك
3) احفظ المشروع في مجلد واضح

أول مشهد:
• Scene → 2D Scene → سمِّ الجذر Player
• Add Child → Sprite2D
• في Inspector: Texture → اسحب صورة PNG (32×32 أو 64×64 للبداية)

مصادر رسوم مجانية:
• kenney.nl (حزم CC0)
• opengameart.org
• ارسم بنفسك في Piskel (مجاني)

نصيحة: PNG بخلفية شفافة للشخصيات. احفظ المشهد Ctrl+S → player.tscn

  • Sprite2D يعرض صورة ثنائية الأبعاد
  • المشهد .tscn = قالب قابل لإعادة الاستخدام
  • خلفية شفافة للشخصيات — PNG
  • احفظ مبكراً وكثيراً

صورة ذهنية

 [Project]
     |
     v
 [Scene: Player]
     |
     +-- Sprite2D ← texture.png

مختبر: مشهد اللاعب

أنشئ player.tscn بـ Sprite2D فقط — شغّل المشهد (F6) — هل تظهر الصورة؟

مهمة عملية

ثبّت Godot، أنشئ مشروع mesue-mini-game، واعرض sprite واحداً على الشاشة.

اختبر فهمك

في Godot، أي عقدة تعرض صورة 2D؟

3. الحركة والإدخال — WASD والفيزياء البسيطة

الهدف: تحرّك اللاعب بلوحة المفاتيح باستخدام CharacterBody2D.

استبدل الجذر أو أضف:
• CharacterBody2D (جذر) → Sprite2D (ابن)
• أضف CollisionShape2D — شكل دائرة أو مستطيل يطابق الصورة

سكربت على CharacterBody2D (أيقونة ورقة صغيرة):

extends CharacterBody2D

var speed = 200

func _physics_process(delta):
var direction = Vector2.ZERO
if Input.is_action_pressed("ui_right"): direction.x += 1
if Input.is_action_pressed("ui_left"): direction.x -= 1
if Input.is_action_pressed("ui_down"): direction.y += 1
if Input.is_action_pressed("ui_up"): direction.y -= 1
velocity = direction.normalized() * speed
move_and_slide()

_physics_process تُستدعى مع الفيزياء — مناسبة للحركة.

Project → Project Settings → Input Map: يمكنك إضافة move_left وربطها بـ A.

  • CharacterBody2D للشخصيات القابلة للتحريك
  • CollisionShape2D يحدد حدود التصادم
  • _physics_process للحركة كل إطار فيزياء
  • normalized() يمنع السرعة الأعلى في القطر

صورة ذهنية

 [Keyboard]
  WASD / arrows
       |
       v
 [Input Map]
       |
       v
 velocity → move_and_slide()
       |
       v
 [Sprite يتحرك]

مختبر: تحكم سلس

جرّب speed = 100 ثم 400 — أيهما أمتع للعب؟ أضف حدود شاشة لاحقاً.

مهمة عملية

اجعل اللاعب يتحرك بسلاسة في 4 اتجاهات — CollisionShape2D مطابق للصورة.

اختبر فهمك

لماذا نستخدم direction.normalized()؟

4. التصادم — جمع العملات وتجنّب العوائق

الهدف: تكتشف التلامس بين اللاعب والعناصر باستخدام Area2D أو body_entered.

عملة (Coin):
• Area2D → Sprite2D + CollisionShape2D (دائرة صغيرة)
• سكربت على Area2D:

extends Area2D

func _ready():
body_entered.connect(_on_body_entered)

func _on_body_entered(body):
if body.name == "Player":
queue_free() # اختفاء العملة

اللاعب يجب أن يكون CharacterBody2D اسمه Player.

عائق ثابت:
• StaticBody2D + CollisionShape2D — اللاعب ينزلق بـ move_and_slide

طبقات التصادم (Collision Layers/Masks) تمنع تصادمات غير مرغوبة — راجع Layer 1 للاعب، Layer 2 للعملات في Project Settings لاحقاً.

  • Area2D للمحفزات (عملة، باب، منطقة خطر)
  • body_entered عند دخول جسم في المنطقة
  • queue_free() يحذف العقدة من المشهد
  • StaticBody2D للجدران والعوائق الثابتة

صورة ذهنية

 [Player] ----يتحرك---->
       |
       x  تصادم
       |
 [Coin Area2D] → queue_free()

مختبر: 5 عملات

انسخ Coin 5 مرات في المشهد — هل تختفي كلها عند اللمس؟ أضف Player لمجموعة player في Node.

مهمة عملية

أضف 3 عملات وعائقاً واحداً StaticBody2D — اللاعب لا يمرّ من العائق.

اختبر فهمك

أي عقدة مناسبة لعملة يجمعها اللاعب؟

5. النقاط والحالة — متى تفوز ومتى تخسر

الهدف: تبني متغيرات score و lives وتربطها بأحداث التصادم.

أنشئ Autoload (Singleton) للحالة العامة:
• Project → Project Settings → Autoload
• اسم: GameState — سكربت game_state.gd:

extends Node

var score = 0
var lives = 3

func add_point(amount = 1):
score += amount

func lose_life():
lives -= 1
if lives <= 0:
get_tree().change_scene_to_file("res://game_over.tscn")

في سكربت العملة بعد التصادم:
GameState.add_point(10)

في سكربت العدو:
GameState.lose_life()

استخدم إشارات (signals) لفصل المنطق: العملة تصدر collected — الواجهة تستمع.

  • Autoload = متغيرات مشتركة بين كل المشاهد
  • افصل منطق النقاط عن رسم الواجهة
  • change_scene_to_file للانتقال Game Over
  • Signals تقلل الربط المباشر بين العقد

صورة ذهنية

 [Coin] --collected--> GameState.score += 10
 [Enemy] --hit-----> GameState.lives -= 1
                           |
                           v
                    lives == 0 ?
                           |
                           v
                    [Game Over]

مختبر: فوز وخسارة

اجمع 50 نقطة = رسالة فوز في print — أو انتقل لمشهد win.tscn بسيط.

مهمة عملية

نفّذ GameState مع score و lives — جمع عملة +10، خطأ -1 حياة.

اختبر فهمك

لماذا Autoload مثل GameState مفيد؟

6. واجهة اللعب — HUD للنقاط والوقت

الهدف: تعرض Score و Lives وعداداً بـ Control nodes.

مشهد واجهة (overlay):
• أضف CanvasLayer للمشهد الرئيسي main.tscn
• تحته: Control → VBoxContainer
• Label للنقاط: Name = ScoreLabel
• Label للحياة: LivesLabel

سكربت HUD (hud.gd):

extends Control

@onready var score_label = /ScoreLabel

func _process(_delta):
score_label.text = "النقاط: " + str(GameState.score)

أو استخدم signal من GameState عند تغيير score لتحديث أدق.

Timer للوقت:
• Timer عقدة — Wait Time = 1 — Autostart — One Shot = Off
• timeout → يقلل الوقت المتبقي

ثبّت الخط: Theme أو custom font TTF عربي إن رغبت.

  • CanvasLayer يرسم فوق اللعبة
  • Label يعرض نصاً ديناميكياً كل إطار أو عند الإشارة
  • Timer للعد التنازلي أو spawn الأعداء
  • لا تكدس منطق اللعب داخل UI — اقرأ من GameState

صورة ذهنية

 [CanvasLayer]
      |
      +-- ScoreLabel  ← GameState.score
      +-- LivesLabel  ← GameState.lives
      +-- Timer       ← الوقت المتبقي

مختبر: HUD حي

اجمع عملة — هل يتحدث الرقم فوراً؟ أضف شريط وقت 60 ثانية.

مهمة عملية

ابنِ HUD يعرض النقاط والحياة — يتحدث عند كل تغيير في GameState.

اختبر فهمك

أي عقدة تضمن أن الواجهة فوق رسوم اللعبة؟

7. الصوت — مؤثرات وإحساس اللعب

الهدف: تضيف جمع عملة، خسارة حياة، وموسيقى خلفية اختيارية.

مؤثر قصير (SFX):
• AudioStreamPlayer على اللاعب أو المشهد
• Import ملف .ogg أو .wav (صغير < 100KB للمؤثر)
• عند جمع عملة:
.play()

موسيقى خلفية:
• AudioStreamPlayer موسيقى — Autoplay — يمكن Loop من Import
• مسار منفصل للصوت: Master → Music و SFX في Audio Bus (اختياري متقدم)

مصادر مجانية:
• freesound.org (تحقق من الترخيص)
• kenney.nl/audio

لا ترفع الصوت فجأة — اختبر سماعات وسماعة الجوال. زر كتم في القائمة لاحقاً يقدّر اللاعبون.

  • AudioStreamPlayer للمؤثرات والموسيقى
  • play() في لحظة الحدث — تصادم، قفز، فوز
  • ملفات OGG أخف للموسيقى الطويلة
  • الصوت نصف تجربة اللعب — لا تهمله

صورة ذهنية

 [Coin collected]
       |
       v
 [AudioStreamPlayer.play()]
       |
       v
  سماعة اللاعب

مختبر: صوتان

مؤثر جمع + مؤثر game over — هل يتداخلان؟ خفّض Music Bus إن لزم.

مهمة عملية

أضف صوت جمع عملة وصوت خسارة حياة — شغّلهما في اللحظة الصحيحة.

اختبر فهمك

متى تشغّل مؤثر الصوت في Godot؟

8. اللمسات — جزيئات، اهتزاز، وإحساس «لعبة حقيقية»

الهدف: تضيف game juice بسيط: وميض، جزيئات، شاشة هزّ عند الضرر.

Game Juice = تغذية راجعة صغيرة تجعل اللعب ممتعاً:

• عند جمع عملة: كبّر Sprite مؤقتاً (Tween scale 1.0 → 1.2 → 1.0)
• GPUParticles2D عند الاختفاء — نجوم صفراء قليلة
• Camera2D + shake: حرّك offset عشوائياً 0.1 ثانية عند الضرر
• Tween (Godot 4): create_tween().tween_property(sprite, "scale", Vector2(1.1,1.1), 0.05)

شاشة البداية:
• مشهد menu.tscn — زر Play → change_scene_to_file("main.tscn")

اختبر مع صديق 5 دقائق — راقب أين يمل أو يضحك. اللمسات الصغيرة فرق كبير.

  • Tween لحركات سلسة بدون تعقيد
  • جزيئات بسيطة = احتفال عند الجمع
  • قائمة بداية تجعل المشروع «لعبة» لا مجرد مشهد
  • اختبر مع لاعب حقيقي

صورة ذهنية

 [حدث: جمع]
     |
 +---+---+
 v       v
Tween   Particles
     |
     v
 «حسّيت بالجائزة!»

مختبر: juice واحد

اختر: اهتزاز كاميرا أو جزيئات عملة — نفّذ واحداً كاملاً قبل إضافة الثاني.

مهمة عملية

أضف قائمة بداية + تأثير بصري عند جمع العملة (Tween أو Particles).

اختبر فهمك

«Game juice» يعني غالباً...

9. التصدير والنشر — itch.io للعالم

الهدف: تصدّر لعبتك لـ HTML5 أو Windows وتنشر صفحة itch.io.

تصدير Godot:
• Project → Export → Add → Windows Desktop أو Web
• للويب: Export HTML5 — قد تحتاج قالب export templates من الموقع
• Export Project → مجلد build/

itch.io (مجاني):
1) حساب على itch.io
2) Create new project
3) Kind: HTML أو Windows
4) ارفع ملف ZIP للويب (index.html + .pck + .wasm) أو .exe
5) اختر Embed في الصفحة للـ HTML5
6) صورة غلاف 630×500 + وصف عربي/إنجليزي قصير
7) Visibility: Public أو Draft للاختبار

اكتب في الوصف: «صُنعت مع Godot — مسار Mesue Learn»

تحذير: لا ترفع مشروعاً فيه أصول بدون ترخيص.

  • صدّر إلى مجلد build نظيف
  • itch.io منصة مجانية مناسبة للألعاب المصغّرة
  • HTML5 يعمل في المتصفح — سهل المشاركة
  • غلاف ووصف يزيدان النقرات

صورة ذهنية

 [Godot Project]
       |
   Export
       |
       v
 [build/ zip]
       |
       v
 [itch.io page] → رابط للعالم

مختبر: رفع Draft

ارفع كـ Draft أولاً — افتح الرابط من جوال وكمبيوتر — هل تعمل؟

مهمة عملية

صدّر اللعبة وارفعها itch.io (Draft أو Public) مع غلاف ووصف.

اختبر فهمك

لنشر لعبة Godot في المتصفح غالباً تصدّر إلى...

10. المشروع النهائي — لعبة مصغّرة كاملة

الهدف: تسلّم لعبة 2D playable مع نقاط، حياة، واجهة، صوت، ونشر itch.io.

المطلوب: لعبة جمع أو هروب بزمن محدود (60–90 ثانية)

الحد الأدنى:
• لاعب يتحرك بـ WASD/أسهم
• 5+ عملات أو أهداف قابلة للجمع
• عائق أو عدو واحد على الأقل
• GameState: score + lives (أو وقت)
• HUD: نقاط + حياة أو مؤقت
• صوتان: جمع + خسارة/فوز
• قائمة بداية + شاشة Game Over
• لمسة polish واحدة (Tween/جزيئات/اهتزاز)
• رابط itch.io يعمل

معيار النجاح:
✓ صديق يلعب دقيقتين دون شرح مطوّل
✓ لا أخطاء حمراء في Output
✓ README.txt: كيف تلعب + أصول مستخدمة
✓ فخور بمشاركة الرابط

  • لعبة كاملة > مشهد تجريبي
  • README يوثّق الأصول والتراخيص
  • رابط itch.io جزء من التسليم
  • اللعب أولاً — زينة ثانياً

صورة ذهنية

 Menu → Main → Game Over
   |        |         |
 Play    score++    Retry
          HUD
          SFX
            |
            v
      [itch.io link]

مختبر: عرض نهائي

سجّل فيديو 30 ثانية gameplay — شاركه مع صديق واسأل: هل فهمت الهدف فوراً؟

مهمة عملية

سلّم لعبة مصغّرة كاملة + رابط itch.io + README — أعلّق برابط Mesue Learn إن رغبت.

اختبر فهمك

أفضل معيار نجاح للمشروع النهائي؟

لعبتك الأولى جاهزة للنشر

ارفع لعبتك على itch.io وشارك الرابط — أو تابع مسار JavaScript لألعاب الويب.