المرجع الشامل في Unity

نسخة موسعة جدًا للمبتدئين: من الصفر حتى مشروع متكامل

مرجع تدريسي أكاديمي مع أمثلة عملية

📘 الفصل الأول: مدخل Unity + طريقة التفكير الصحيحة

لماذا هذا الفصل مهم جدًا للمبتدئ؟

أكثر سبب يجعل الطالب يتعب في Unity ليس صعوبة الأدوات، بل غياب الصورة الكبيرة. عندما تدخل Unity بدون منهج واضح، تشعر أن كل شيء متداخل: مشاهد، كائنات، أصوات، سكربتات، فيزياء. هذا الفصل يبني الخريطة الذهنية التي تمنع الضياع.

ثلاثة أسئلة قبل أي مشروع

  • ما هدف اللعبة؟
  • ما الحلقة الأساسية للعب (Core Loop)؟
  • كيف يعرف اللاعب أنه نجح أو فشل؟
العنصرالمعنىمثال
Goalالهدفجمع 20 عملة
Challengeالتحديالوقت محدود + أعداء
Feedbackالتغذية الراجعةصوت/نص/اهتزاز/تأثير بصري

قبل أن تفتح الكود: اكتب مستند تصميم صغير (1 صفحة) يحدد قواعد اللعبة. هذا يوفر ساعات من التخبط.

📘 الفصل الثاني: GameObject و Components و Transform بعمق

Why: لماذا Unity يعتمد على Components؟

لأن التركيب (Composition) أكثر مرونة من الوراثة الثقيلة. يمكنك تحويل نفس الكائن من عنصر ثابت إلى عنصر فيزيائي فقط بإضافة/إزالة Component.

Transform: أهم Component في Unity

لا يوجد GameObject بدون Transform. كل حركة ودوران وحجم تبدأ منه.

C# - TransformDeepDemo.cs
using UnityEngine;

public class TransformDeepDemo : MonoBehaviour
{
    [SerializeField] private Transform target;

    void Start()
    {
        // World position
        transform.position = new Vector3(0f, 1f, 0f);

        // Local position relative to parent
        transform.localPosition = new Vector3(0f, 0.5f, 0f);

        // Rotate smoothly toward target if available
        if (target != null)
        {
            transform.LookAt(target);
        }
    }
}

localPosition يتأثر بالأب (Parent)، بينما position في العالم مباشرة. هذا يسبب كثير أخطاء للطلاب.

📘 الفصل الثالث: السكربتات ودورة الحياة (Lifecycle) والـ Time

Awake vs Start vs Update vs FixedUpdate

الدالةمتى تعملأفضل استخدام
Awakeعند تحميل الكائنتهيئة المراجع الداخلية
Startقبل أول Frameتهيئة تعتمد على كائنات أخرى
Updateكل Frameقراءة الإدخال وتحديث UI خفيف
FixedUpdateبمعدل ثابتتحديث الفيزياء
C# - TimeAndLoop.cs
using UnityEngine;

public class TimeAndLoop : MonoBehaviour
{
    private float _elapsed;

    void Update()
    {
        // deltaTime prevents frame-rate dependency.
        _elapsed += Time.deltaTime;
        if (_elapsed >= 2f)
        {
            Debug.Log("2 seconds passed");
            _elapsed = 0f;
        }
    }
}

📘 الفصل الرابع: الإدخال والحركة الأساسية (3D)

Why: لماذا نفصل الإدخال عن الحركة؟

القراءة في Update والاستجابة الفيزيائية في FixedUpdate تمنع اختلاف السلوك بين الأجهزة السريعة والبطيئة.

C# - BasicMovement3D.cs
using UnityEngine;

public class BasicMovement3D : MonoBehaviour
{
    [SerializeField] private float speed = 5f;
    private Rigidbody _rb;
    private Vector3 _input;

    void Awake() { _rb = GetComponent<Rigidbody>(); }

    void Update()
    {
        _input.x = Input.GetAxisRaw("Horizontal");
        _input.z = Input.GetAxisRaw("Vertical");
        _input = _input.normalized;
    }

    void FixedUpdate()
    {
        _rb.velocity = new Vector3(_input.x * speed, _rb.velocity.y, _input.z * speed);
    }
}

📘 الفصل الخامس: الفيزياء التفصيلية 3D

  • Rigidbody: جسم فيزيائي يتحرك بالقوى والسرعة.
  • Collider: شكل التصادم.
  • IsTrigger: كشف عبور بدون تصادم مادي.
C# - JumpController3D.cs
using UnityEngine;

public class JumpController3D : MonoBehaviour
{
    [SerializeField] private float jumpForce = 7f;
    [SerializeField] private Transform groundPoint;
    [SerializeField] private LayerMask groundLayer;

    private Rigidbody _rb;
    private bool _jumpRequested;

    void Awake() { _rb = GetComponent<Rigidbody>(); }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)) _jumpRequested = true;
    }

    void FixedUpdate()
    {
        if (_jumpRequested && Physics.CheckSphere(groundPoint.position, 0.2f, groundLayer))
        {
            _rb.velocity = new Vector3(_rb.velocity.x, 0f, _rb.velocity.z);
            _rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }

        _jumpRequested = false;
    }
}

📘 الفصل السادس: الفيزياء التفصيلية 2D

أهم فرق عملي عن 3D

كل شيء يكون بمكونات 2D. أي خلط بين 2D و3D يمنع عمل التصادمات.

C# - Collision2DGuide.cs
using UnityEngine;

public class Collision2DGuide : MonoBehaviour
{
    private void OnCollisionEnter2D(Collision2D c)
    {
        Debug.Log("2D Collision: " + c.gameObject.name);
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        Debug.Log("2D Trigger: " + other.gameObject.name);
    }
}

📘 الفصل السابع: Animation + Animator + State Machine

بناء Animator Controller صحيح

  • States: Idle, Run, Jump, Fall.
  • Parameters: Speed(float), IsGrounded(bool).
  • Transitions بشروط واضحة.
C# - AnimatorBinder.cs
using UnityEngine;

public class AnimatorBinder : MonoBehaviour
{
    [SerializeField] private Animator animator;
    [SerializeField] private Rigidbody2D rb;
    void Update()
    {
        animator.SetFloat("Speed", Mathf.Abs(rb.velocity.x));
        animator.SetBool("IsGrounded", Mathf.Abs(rb.velocity.y) < 0.01f);
    }
}

📘 الفصل الثامن: UI و Canvas و TextMeshPro

UI ليس مجرد نص على الشاشة

واجهة المستخدم هي عقد المعلومات بين اللعبة واللاعب: نقاط، صحة، وقت، رسائل الفوز والخسارة.

C# - HUDController.cs
using TMPro;
using UnityEngine;

public class HUDController : MonoBehaviour
{
    [SerializeField] private TextMeshProUGUI scoreText;
    private int _score;

    public void AddScore(int value)
    {
        _score += value;
        scoreText.text = "Score: " + _score;
    }
}

📘 الفصل التاسع: GameManager وإدارة الحالة والمشاهد

Why: لماذا نحتاج GameManager؟

GameManager يدير الحالة العامة ويمنع تكرار منطق اللعبة عبر ملفات متفرقة.

C# - GameManagerFlow.cs
using UnityEngine;
using UnityEngine.SceneManagement;

public class GameManagerFlow : MonoBehaviour
{
    public static GameManagerFlow Instance;
    public int Lives = 3;

    void Awake()
    {
        if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); }
        else Destroy(gameObject);
    }

    public void LoseLife()
    {
        Lives--;
        if (Lives <= 0) SceneManager.LoadScene("GameOver");
    }
}

📘 الفصل العاشر: Prefabs و Coroutines و Spawn Systems

لماذا Prefab أساس الإنتاجية؟

Prefab يقلل التكرار، ويجعل تعديل نموذج واحد ينعكس على كل النسخ الجديدة منه.

C# - WaveSpawner.cs
using System.Collections;
using UnityEngine;

public class WaveSpawner : MonoBehaviour
{
    [SerializeField] private GameObject enemyPrefab;

    void Start()
    {
        StartCoroutine(SpawnRoutine());
    }

    private IEnumerator SpawnRoutine()
    {
        while (true)
        {
            for (int i = 0; i < 3; i++)
            {
                Instantiate(enemyPrefab, transform.position + Random.insideUnitSphere * 2f, Quaternion.identity);
                yield return new WaitForSeconds(0.4f);
            }
            yield return new WaitForSeconds(4f);
        }
    }
}

📘 الفصل الحادي عشر: Audio System (SFX/Music/Mixer)

تصميم صوتي بسيط لكنه احترافي

الطبقةالمحتوىملاحظة
Musicخلفية مستمرةLoop + مستوى منخفض
SFXقفز/ضرب/جمعPlayOneShot
UIضغط أزراريرتبط بالقوائم

الموازنة بين الموسيقى والمؤثرات أهم من جودة الملف الصوتي نفسه.

📘 الفصل الثاني عشر: ScriptableObject + Event Bus

فصل البيانات عن المنطق

ScriptableObject يمنحك قوة تخزين البيانات خارج MonoBehaviour، مما يسهل إعادة استخدامها في عدة Scenes دون تكرار.

C# - EnemyConfig.cs
using UnityEngine;

[CreateAssetMenu(menuName = "Configs/Enemy")]
public class EnemyConfig : ScriptableObject
{
    public int health;
    public float moveSpeed;
}

📘 الفصل الثالث عشر: Save/Load Architecture

ابدأ بسيطًا ثم توسع

PlayerPrefs بسيط لكنه يناسب النماذج الصغيرة. للمشاريع الأكبر، يمكن الانتقال إلى JSON + File I/O.

C# - SaveLoadSimple.cs
using UnityEngine;

public static class SaveLoadSimple
{
    public static void SaveBest(int best)
    {
        PlayerPrefs.SetInt("BEST_SCORE", best);
        PlayerPrefs.Save();
    }

    public static int LoadBest()
    {
        return PlayerPrefs.GetInt("BEST_SCORE", 0);
    }
}

📘 الفصل الرابع عشر: تنظيم المشروع بأسلوب Clean Architecture مبسط

هيكل مجلدات عملي مقترح

Structure.txt
// Assets/Scripts/Domain - قواعد العمل الأساسية
// Assets/Scripts/Application - Use Cases وLogic متوسط
// Assets/Scripts/Presentation - UI Controllers
// Assets/Scripts/Infrastructure - حفظ/تحميل/Audio/Services
// Assets/Scripts/Common - Utilities/Extensions/Constants

أي كود UI يستدعي كود Domain مباشرة بشكل عشوائي هو إشارة أن الهيكلة تحتاج تحسين.

📘 الفصل الخامس عشر: مشروع تخرج متكامل + Rubric + خطة تدريب

متطلبات المشروع النهائي (Coin Rush Pro)

  • لاعب يتحرك ويقفز ويتأثر بالفيزياء.
  • أعداء أو عقبات مع نظام Spawn.
  • نقاط + وقت + شاشات فوز/خسارة.
  • Audio كامل (Music/SFX).
  • حفظ أفضل نتيجة.
  • تنظيم ملفات بمبدأ فصل المسؤوليات.

Rubric تقييم (100)

المعيارالدرجةالتفصيل
وظائف اللعبة30كل المزايا المطلوبة تعمل بثبات
جودة الكود20أسماء واضحة، تنظيم، تجنب التكرار
صحة استخدام Unity20تطبيق صحيح لـ Physics/Animator/UI
تجربة المستخدم15وضوح الواجهة والصوت والتغذية الراجعة
الاختبار والاستقرار15معالجة حالات الحواف وعدم الانهيار

لكي يصبح الطالب جاهزًا لسوق العمل: اطلب منه تقديم شرح معماري مختصر (5 دقائق) يبرر قراراته البرمجية.

📘 الفصل السادس عشر: قالب الدرس العملي (شرح + تمرين + حل نموذجي)

كيف نستخدم هذا القالب في كل محاضرة؟

لجعل التعلم عمليًا ومنظمًا، كل درس يمر بثلاث مراحل ثابتة: شرح المفهوم، تنفيذ تمرين، ثم مراجعة حل نموذجي. بهذه الطريقة الطالب لا يحفظ فقط، بل يبني مهارة حل المشكلات.

المرحلةالمدة المقترحةالهدف
شرح Why/How20 دقيقةفهم الفكرة وسياقها
تطبيق مباشر25 دقيقةتحويل الفهم إلى كود
مراجعة الحل15 دقيقةتثبيت المفهوم وتصحيح الأخطاء

مثال تمرين من الفيزياء 2D

المطلوب: حركة يمين/يسار + قفز مرة واحدة عند ملامسة الأرض + طباعة تحذير إذا اللاعب حاول القفز وهو في الهواء.

C# - LessonTemplate_Solution.cs
using UnityEngine;

public class LessonTemplate_Solution : MonoBehaviour
{
    [SerializeField] private float speed = 6f;
    [SerializeField] private float jumpForce = 10f;
    [SerializeField] private Transform groundCheck;
    [SerializeField] private LayerMask groundLayer;
    private Rigidbody2D _rb;

    void Awake() { _rb = GetComponent<Rigidbody2D>(); }

    void Update()
    {
        float x = Input.GetAxisRaw("Horizontal");
        _rb.velocity = new Vector2(x * speed, _rb.velocity.y);

        bool grounded = Physics2D.OverlapCircle(groundCheck.position, 0.15f, groundLayer);
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (grounded)
            {
                _rb.velocity = new Vector2(_rb.velocity.x, 0f);
                _rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
            }
            else
            {
                Debug.Log("Cannot jump: player is not grounded.");
            }
        }
    }
}

ثبّت هذا القالب في كل درس، وستلاحظ تحسنًا سريعًا في أداء الطلاب.

📘 الفصل السابع عشر: خطة تدريس 8 أسابيع

خطة تنفيذ الكورس داخل فصل تدريبي

الأسبوعالوحداتالهدفناتج مطلوب
11-2فهم البيئة وComponentsمشهد ابتدائي منظم
23-4Lifecycle + حركة 3Dلاعب متحرك
35فيزياء 3D عميقةقفز/Collision/Trigger
46-72D Physics + Animatorمشهد Platformer صغير
58-9UI + إدارة الحالةHUD + GameOver
610-11Spawn + Audioموجات أعداء + SFX/Music
712-13Data + Save/Loadحفظ تقدم اللاعب
814-15Architecture + Projectعرض مشروع نهائي

نموذج توزيع درجات

  • 30% واجبات أسبوعية.
  • 20% تقييم عملي منتصف الكورس.
  • 50% مشروع نهائي.

📘 الفصل الثامن عشر: Debugging Playbook

خطة تشخيص الأعطال بدون تخمين

أي مشكلة في Unity يمكن التعامل معها إذا اتبعت منهجًا ثابتًا بدل العشوائية.

الخطوةماذا تفعل؟أداة Unity
1عرّف السلوك المتوقعملاحظة واضحة
2حدّد السلوك الفعليGame View + Console
3تابع القيم أثناء التشغيلInspector Runtime
4أضف Logs دقيقةDebug.Log
5اختبر فرضية واحدةتعديل صغير + إعادة اختبار
C# - DebugChecklist.cs
using UnityEngine;

public class DebugChecklist : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rb;

    void Update()
    {
        // Structured debugging line helps identify timing/value issues fast.
        Debug.Log("[DebugChecklist] frame=" + Time.frameCount + " pos=" + transform.position + " vel=" + rb.velocity);
    }
}

لا تغيّر عدة أشياء معًا أثناء التصحيح. غيّر عنصرًا واحدًا فقط في كل تجربة.