⚡

تعلم لغة Luau

الدليل الشامل لبرمجة ألعاب روبلوكس - من أول سطر كود إلى الأنظمة الاحترافية

ما هي لغة Luau؟

Luau هي لغة البرمجة النصية الرسمية لمنصة روبلوكس، مبنية على لغة Lua 5.1 الشهيرة لكنها مطوَّرة بنظام أنواع حديث وأداء أعلى بكثير. بها تبرمج كل شيء في لعبتك: حركة الشخصيات، الأسلحة، الأنظمة، المتاجر، الواجهات وكل ما تتخيله.

🌙

مبنية على Lua

أساس قوي من أشهر لغات البرمجة النصية في العالم

🛡️

نظام أنواع

أضف أنواعاً للمتغيرات ويكتشف Luau أخطاءك قبل التشغيل

⚡

أداء فائق

أسرع من Lua العادية بمرات عديدة بفضل المحسّنات الحديثة

🎮

مصممة لروبوكس

تعمل مباشرة داخل Roblox Studio مع كل واجهات اللعبة

كيف تبدأ؟

كل ما تحتاجه لبدء رحلة التعلم هو Roblox Studio المجاني:

  • حمّل Roblox Studio من الموقع الرسمي وسجّل الدخول بحساب روبلوكس
  • أنشئ مشروعاً جديداً واختر قالب Baseplate
  • من نافذة Explorer أضف سكربتاً: Script للسيرفر أو LocalScript للعميل
  • اكتب الكود في المحرر ثم اضغط F5 لتشغيل اللعبة
  • راقب النتائج والرسائل في نافذة Output أسفل الشاشة

أول برنامج لك

‎print('مرحباً بك في Luau!')

المتغيرات وأنواع البيانات

المتغير هو صندوق تخزن فيه قيمة. اكتب كلمة local ثم الاسم ثم علامة = ثم القيمة:

‎local name = 'Ahmed'          -- نص
‎local age = 15                -- رقم صحيح
‎local height = 1.75           -- رقم عشري
‎local isWinner = false        -- قيمة منطقية
‎local nothing = nil           -- لا شيء

‎print(type(name))             -- string
‎print(type(age))              -- number
‎print(type(isWinner))         -- boolean

تنبيه مهم: استخدم local دائماً. لو كتبت الاسم بدونها يصبح المتغير عاماً ويسهل الخطأ فيه:

‎coins = 100          -- متغير عام - تجنب هذا
‎local score = 200    -- متغير محلي - الأفضل دائماً

أنواع البيانات في Luau

🕳️

nil

يعني لا قيمة - عدم وجود شيء

✅

boolean

قيمتان فقط: true و false

🔢

number

كل الأعداد صحيحة وعشرية

💬

string

النصوص بين علامتي تنصيص

📦

table

الجدول: مصفوفات وقواميس

⚙️

function

الدوال القابلة للاستدعاء

🧵

thread

خيوط التنفيذ المتوازي

🧩

userdata

بيانات مخصصة من روبلوكس

الأرقام والعمليات الرياضية

كل العمليات الحسابية الأساسية متوفرة، مع مكتبة math الكاملة:

‎local sum = 10 + 5        -- جمع: 15
‎local diff = 10 - 5       -- طرح: 5
‎local product = 10 * 5    -- ضرب: 50
‎local quotient = 10 / 5   -- قسمة: 2
‎local remainder = 10 % 3  -- الباقي: 1
‎local power = 2 ^ 3       -- الأس: 8
‎local floored = 10 // 3   -- قسمة صحيحة: 3

‎print(math.floor(4.7))        -- 4   التقريب للأسفل
‎print(math.ceil(4.2))         -- 5   التقريب للأعلى
‎print(math.round(4.5))        -- 5   التقريب العادي
‎print(math.abs(-7))           -- 7   القيمة المطلقة
‎print(math.max(3, 9, 5))      -- 9   الأكبر
‎print(math.min(3, 9, 5))      -- 3   الأصغر
‎print(math.sqrt(16))          -- 4   الجذر التربيعي
‎print(math.random(1, 6))      -- نرد من 1 إلى 6
‎print(math.clamp(42, 0, 10))  -- 10  حصر القيمة بين حدين
‎print(math.lerp(0, 100, 0.5)) -- 50  قيمة بين قيمتين

الإسناد المركب: اختصارات خاصة بـ Luau تجعل الكود أقصر وأنظف:

‎local coins = 0
‎coins += 10    -- نفس: coins = coins + 10
‎coins -= 3     -- نفس: coins = coins - 3
‎coins *= 2     -- نفس: coins = coins * 2
‎coins /= 7     -- نفس: coins = coins / 7

‎print(coins)   -- 2

النصوص (Strings)

دمج النصوص ومعالجتها من أكثر ما ستستخدمه في البرمجة:

‎local first = 'Hello'
‎local last = 'Luau'

‎print(first .. ' ' .. last)       -- دمج: Hello Luau
‎print(#first)                     -- الطول: 5
‎print(string.upper(first))        -- HELLO
‎print(string.lower('ABC'))        -- abc
‎print(string.sub('Roblox', 1, 3)) -- Rob
‎print(string.rep('ha', 3))        -- hahaha
‎print(string.format('العمر: %d', 15))

‎local name = 'Sara'
‎print(`مرحباً {name}!`)            -- تداخل النصوص (خاص بـ Luau)

‎local parts = string.split('a,b,c', ',')  -- تقسيم إلى جدول

الشرط if والحلقات (Loops)

الشرط if ينفذ كوداً حسب تحقق شرط معين. لاحظ elseif و else:

‎local score = 85

‎if score >= 90 then
‎    print('ممتاز!')
‎elseif score >= 70 then
‎    print('جيد جداً')
‎elseif score >= 50 then
‎    print('مقبول')
‎else
‎    print('يحتاج تحسين')
‎end

‎-- تعبير شرطي (خاص بـ Luau)
‎local grade = if score >= 50 then 'ناجح' else 'راسب'
‎print(grade)

‎-- فقط nil و false يعتبران خطأ في الشرط
‎if 0 then print('الصفر يعتبر صحيح!') end

الحلقات (Loops) لتكرار الكود عدة مرات - while و repeat و for:

‎-- حلقة while: تكرر طالما الشرط صحيح
‎local count = 0
‎while count < 3 do
‎    count += 1
‎    print('العدد:', count)
‎end

‎-- حلقة repeat: تنفذ مرة على الأقل
‎local tries = 0
‎repeat
‎    tries += 1
‎until tries >= 3

‎-- حلقة for رقمية
‎for i = 1, 5 do
‎    print(i)
‎end

‎for i = 10, 1, -2 do   -- عد تنازلي بخطوة 2
‎    print(i)
‎end

‎local fruits = {'تفاح', 'موز', 'عنب'}

‎for index, fruit in fruits do     -- تكرار معمم (خاص بـ Luau)
‎    print(index, fruit)
‎end

‎for key, value in pairs({a = 1, b = 2}) do
‎    print(key, value)
‎end

‎-- continue: تجاهل باقي الدورة وانتقل للتالية (خاص بـ Luau)
‎for i = 1, 10 do
‎    if i % 2 == 0 then
‎        continue
‎    end
‎    print(i)   -- الأعداد الفردية فقط
‎end

‎while true do
‎    break   -- الخروج من الحلقة فوراً
‎end

الدوال (Functions)

الدالة قطعة كود قابلة لإعادة الاستخدام، تأخذ مدخلات وترجع مخرجات:

‎local function add(a, b)
‎    return a + b
‎end

‎print(add(3, 4))   -- 7

‎-- إرجاع قيم متعددة
‎local function minMax(list)
‎    return math.min(table.unpack(list)), math.max(table.unpack(list))
‎end

‎local small, big = minMax({5, 2, 9, 1})
‎print(small, big)   -- 1   9

‎-- معاملات متغيرة العدد
‎local function sum(...)
‎    local total = 0
‎    for _, n in {...} do
‎        total += n
‎    end
‎    return total
‎end

‎print(sum(1, 2, 3, 4))   -- 10

‎-- دالة مجهولة (anonymous function)
‎local double = function(x)
‎    return x * 2
‎end
‎print(double(8))   -- 16

‎-- الإغلاقات (closures): دالة تتذكر متغيراتها الخارجية
‎local function counter()
‎    local count = 0
‎    return function()
‎        count += 1
‎        return count
‎    end
‎end

‎local next = counter()
‎print(next())   -- 1
‎print(next())   -- 2

‎-- العودية (recursion): دالة تنادي نفسها
‎local function factorial(n)
‎    if n <= 1 then return 1 end
‎    return n * factorial(n - 1)
‎end
‎print(factorial(5))   -- 120

الجداول (Tables)

الجدول هو أقوى هيكل بيانات في Luau: مصفوفات وقواميس في قالب واحد. تنبيه: الفهرسة تبدأ من 1 وليس 0!

‎-- مصفوفة (array)
‎local fruits = {'تفاح', 'موز', 'عنب'}
‎print(fruits[1])       -- تفاح
‎print(#fruits)         -- 3
‎table.insert(fruits, 'مانجو')   -- إضافة
‎table.remove(fruits, 1)         -- حذف أول عنصر
‎print(fruits[1])       -- موز

‎-- قاموس (dictionary)
‎local player = {
‎    name = 'Ali',
‎    score = 100,
‎    level = 5,
‎}
‎print(player.name)     -- Ali
‎player.score += 50     -- Ali الآن 150

‎-- جدول متداخل
‎local team = {
‎    name = 'الصقور',
‎    members = {'Ali', 'Sara'},
‎}
‎print(team.members[2]) -- Sara

‎local nums = {5, 2, 9, 1}
‎table.sort(nums)                -- ترتيب تصاعدي
‎print(table.concat(nums, ', ')) -- 1, 2, 5, 9

‎print(table.find(nums, 9))      -- 3 (موقع العنصر)

‎local copy = table.clone(nums)  -- نسخة جديدة (خاص بـ Luau)
‎table.clear(nums)               -- تفريغ الجدول
‎print(#nums)                    -- 0

‎local made = table.create(3, 0) -- جدول من ثلاثة أصفار
‎local frozen = table.freeze({x = 1})  -- جدول مقفل (خاص بـ Luau)

الميتاتيبل (Metatables) - سحر الجداول

‎local vec = {x = 1, y = 2}

‎local meta = {
‎    __index = function(t, key)
‎        return 'لا توجد خاصية اسمها ' .. key
‎    end,
‎    __tostring = function(t)
‎        return `({t.x}, {t.y})`
‎    end,
‎}

‎setmetatable(vec, meta)
‎print(vec.z)         -- لا توجد خاصية اسمها z
‎print(tostring(vec)) -- (1, 2)

البرمجة كائنية التوجه (OOP)

‎-- صناعة الكلاس
‎local Animal = {}
‎Animal.__index = Animal

‎function Animal.new(name)
‎    local self = setmetatable({}, Animal)
‎    self.name = name
‎    return self
‎end

‎function Animal:speak()
‎    print(self.name .. ' يصدر صوتاً')
‎end

‎local cat = Animal.new('مشمش')
‎cat:speak()   -- مشمش يصدر صوتاً

‎-- الوراثة
‎local Dog = setmetatable({}, {__index = Animal})
‎Dog.__index = Dog

‎function Dog.new(name)
‎    local self = Animal.new(name)
‎    return setmetatable(self, Dog)
‎end

‎function Dog:speak()
‎    print(self.name .. ' ينبح!')   -- تجاوز الدالة الأصلية
‎end

‎local dog = Dog.new('ريكس')
‎dog:speak()   -- ريكس ينبح!

مكتبات قياسية إضافية

إلى جانب math و string و table، توفر Luau مكتبات مهمة أخرى:

⏰

os

os.clock لقياس الوقت و os.time للطابع الزمني

🧶

coroutine

خيوط برمجية متقدمة للتحكم في التنفيذ

🔤

utf8

التعامل الصحيح مع النصوص العربية واليونيكود

🧱

buffer

التعامل مع البيانات الثنائية بسرعة عالية

‎print(os.clock())   -- الوقت بالثواني منذ بدء التشغيل
‎print(os.time())    -- الطابع الزمني الحالي

‎local co = coroutine.create(function()
‎    print('أنا داخل الكوروتين')
‎end)
‎coroutine.resume(co)

‎print(utf8.len('مرحباً'))   -- عدد الأحرف الحقيقي

ميزات Luau الحصرية

هذه الميزات تجعل Luau أقوى بكثير من Lua العادية، وستستخدمها يومياً:

🛡️

نظام الأنواع

أنواع اختيارية تكشف أخطاءك قبل التشغيل

⏭️

continue

تجاهل باقي الدورة وانتقل للتالية داخل الحلقات

✍️

الإسناد المركب

+= و -= و *= و /= لاختصار العمليات

🧵

تداخل النصوص

print مع أقواس backtick يدمج المتغيرات في النص مباشرة

🔁

التكرار المعمم

for k, v in table do مباشرة بدون pairs

❓

if كتعبير

local x = if شرط then a else b في سطر واحد

⏱️

مكتبة task

task.wait و spawn و delay بديلة wait القديمة

📦

مكتبة الجداول

table.clone و freeze و create دوال جديدة قوية

نظام الأنواع (Type Annotations)

‎--!strict   ← فعّل الفحص الصارم في أول سطر

‎type Player = {
‎    name: string,
‎    health: number,
‎}

‎local function heal(p: Player, amount: number): Player
‎    p.health += amount
‎    return p
‎end

‎local hero: Player = {name = 'Ali', health = 50}
‎heal(hero, 25)
‎print(hero.health)  -- 75

مكتبة task - الطريقة الحديثة للتوقيت

‎-- انتظر ثانيتين (لا توقف باقي اللعبة)
‎task.wait(2)

‎-- شغّل دالة فوراً في خيط منفصل
‎task.spawn(function()
‎    print('يعمل في الخلفية')
‎end)

‎-- شغّل دالة بعد تأخير
‎task.delay(3, function()
‎    print('بعد 3 ثواني')
‎end)

‎-- شغّل في نهاية الإطار الحالي
‎task.defer(print, 'لاحقاً!')

أساسيات روبلوكس العملية

الآن بعد أن أتقنت اللغة، هذه هي المفاهيم التي ستحتاجها لبرمجة ألعاب روبلوكس فعلياً:

🖥️

Script

يعمل على السيرفر - المنطق الرئيسي وحماية اللعبة

📱

LocalScript

يعمل عند اللاعب - الواجهات والتحكم والمؤثرات

🧩

ModuleScript

كود مشترك تستدعيه من أي مكان عبر require

الكائنات والخدمات (Instances and Services)

‎-- الوصول للجزء الذي يحتوي السكربت
‎local part = script.Parent
‎part.Color = Color3.fromRGB(255, 0, 0)
‎part.Anchored = false

‎-- إنشاء كائن جديد بالكامل
‎local newPart = Instance.new('Part')
‎newPart.Size = Vector3.new(4, 4, 4)
‎newPart.Position = Vector3.new(0, 10, 0)
‎newPart.Parent = workspace

‎-- الوصول للخدمات عبر GetService
‎local Players = game:GetService('Players')
‎local RunService = game:GetService('RunService')

الأحداث (Events) - قلب التفاعل في اللعبة

‎local Players = game:GetService('Players')
‎local part = script.Parent

‎-- عند لمس اللاعب للجزء
‎part.Touched:Connect(function(hit)
‎    local humanoid = hit.Parent:FindFirstChildOfClass('Humanoid')
‎    if humanoid then
‎        humanoid.Health -= 10
‎    end
‎end)

‎-- عند تغير خاصية معينة
‎part:GetPropertyChangedSignal('Position'):Connect(function()
‎    print('تحرك الجزء!')
‎end)

‎-- عند انضمام لاعب للعبة
‎Players.PlayerAdded:Connect(function(player)
‎    print(player.Name .. ' انضم للعبة!')
‎end)

التواصل بين السيرفر والعميل (RemoteEvents)

‎-- السيرفر: إنشاء واستقبال الرسائل
‎local remote = Instance.new('RemoteEvent')
‎remote.Name = 'SendMessage'
‎remote.Parent = game:GetService('ReplicatedStorage')

‎remote.OnServerEvent:Connect(function(player, message)
‎    print(player.Name .. ' قال: ' .. message)
‎end)

‎-- العميل (LocalScript): إرسال رسالة للسيرفر
‎remote:FireServer('مرحباً!')

إنشاء سكربتات روبلوكس كاملة

الآن وقت التطبيق الحقيقي! هذه سكربتات كاملة جاهزة للنسخ والاستخدام مباشرة، تغطي كل ما تحتاجه لبناء لعبة حقيقية خطوة بخطوة.

خطوات إنشاء أي سكربت

  • افتح Roblox Studio وأنشئ مشروعاً جديداً بقالب Baseplate
  • من نافذة Explorer اضغط بزر الفأرة الأيمن على الخدمة المناسبة
  • اختر Insert Object ثم Script أو LocalScript
  • الصق الكود من الأمثلة في هذه الصفحة
  • اضغط F5 لتشغيل اللعبة وراقب نافذة Output

أين تضع كل سكربت؟

🖥️

ServerScriptService

سكربتات السيرفر - منطق اللعبة والحماية

🔒

ServerStorage

أشياء مخفية عن اللاعبين - أدوات سرية

🔄

ReplicatedStorage

مشترك بين السيرفر والعميل - RemoteEvents

🪟

StarterGui

واجهات اللاعب الرسومية

🎒

StarterPack

أدوات تبدأ مع اللاعب فور دخوله

📱

StarterPlayerScripts

سكربتات العميل - التحكم والواجهات

1 - هيكل سكربت احترافي

كل سكربت احترافي يتبع هذا الترتيب: الخدمات ثم الإعدادات ثم الدوال ثم الربط:

‎-- ===== 1. الخدمات =====
‎local Players = game:GetService('Players')
‎local ServerStorage = game:GetService('ServerStorage')

‎-- ===== 2. الإعدادات =====
‎local CONFIG = {
‎    startCoins = 100,
‎    maxPlayers = 20,
‎}

‎-- ===== 3. الدوال =====
‎local function onPlayerAdded(player)
‎    print('انضم اللاعب: ' .. player.Name)
‎end

‎local function onPlayerRemoving(player)
‎    print('خرج اللاعب: ' .. player.Name)
‎end

‎-- ===== 4. الربط =====
‎Players.PlayerAdded:Connect(onPlayerAdded)
‎Players.PlayerRemoving:Connect(onPlayerRemoving)

‎-- لا تنسَ اللاعبين الموجودين مسبقاً
‎for _, player in Players:GetPlayers() do
‎    onPlayerAdded(player)
‎end

2 - نظام النقاط ولوحة الصدارة

هذا السكربت ينشئ لوحة نقاط تظهر لكل اللاعبين في الزاوية العلوية:

‎-- ضعه في ServerScriptService كـ Script
‎local Players = game:GetService('Players')

‎local function setupPlayer(player)
‎    local leaderstats = Instance.new('Folder')
‎    leaderstats.Name = 'leaderstats'
‎    leaderstats.Parent = player

‎    local coins = Instance.new('IntValue')
‎    coins.Name = 'Coins'
‎    coins.Value = 0
‎    coins.Parent = leaderstats
‎end

‎Players.PlayerAdded:Connect(setupPlayer)

3 - حفظ البيانات (DataStore)

لكي لا تضيع نقاط اللاعبين عند الخروج، احفظها في DataStore:

‎-- ضعه في ServerScriptService
‎local DataStoreService = game:GetService('DataStoreService')
‎local Players = game:GetService('Players')
‎local store = DataStoreService:GetDataStore('CoinsData')

‎local function loadCoins(player)
‎    local success, data = pcall(function()
‎        return store:GetAsync(player.UserId)
‎    end)
‎    if success and data then
‎        return data
‎    end
‎    return 0   -- لاعب جديد ليس لديه نقاط
‎end

‎local function saveCoins(player, coins)
‎    local success, err = pcall(function()
‎        store:SetAsync(player.UserId, coins)
‎    end)
‎    if not success then
‎        warn('فشل الحفظ: ' .. err)
‎    end
‎end

‎Players.PlayerRemoving:Connect(function(player)
‎    local stats = player:FindFirstChild('leaderstats')
‎    local coins = stats and stats:FindFirstChild('Coins')
‎    if coins then
‎        saveCoins(player, coins.Value)
‎    end
‎end)

4 - بوابة تفتح باللمس

بوابة تنزلق للأعلى عند لمسها، مع حماية من الفتح المتكرر السريع:

‎-- ضعه داخل الباب كـ Script
‎local TweenService = game:GetService('TweenService')
‎local door = script.Parent
‎local closedPosition = door.Position
‎local openPosition = door.Position + Vector3.new(0, 8, 0)
‎local isOpen = false
‎local canToggle = true

‎local function toggleDoor()
‎    isOpen = not isOpen
‎    local target = isOpen and openPosition or closedPosition
‎    local tween = TweenService:Create(
‎        door,
‎        TweenInfo.new(0.5),
‎        {Position = target}
‎    )
‎    tween:Play()
‎end

‎door.Touched:Connect(function(hit)
‎    if not canToggle then return end
‎    if hit.Parent:FindFirstChild('Humanoid') then
‎        canToggle = false
‎        toggleDoor()
‎        task.wait(1)   -- منع الفتح والإغلاق السريع
‎        canToggle = true
‎    end
‎end)

5 - الجزء القاتل (الحمم)

من أشهر السكربتات الكلاسيكية - أي لاعب يلمس الجزء يموت فوراً:

‎-- ضعه داخل الجزء القاتل كـ Script
‎local killPart = script.Parent
‎killPart.Anchored = true

‎killPart.Touched:Connect(function(hit)
‎    local humanoid = hit.Parent:FindFirstChildOfClass('Humanoid')
‎    if humanoid then
‎        humanoid.Health = 0   -- القضاء على اللاعب فوراً
‎    end
‎end)

6 - إعطاء أداة عند الانضمام

يعطى كل لاعب أداة تلقائياً عند دخوله اللعبة، ومع كل إعادة إنعاش:

‎-- ضعه في ServerScriptService
‎local Players = game:GetService('Players')
‎local ServerStorage = game:GetService('ServerStorage')

‎Players.PlayerAdded:Connect(function(player)
‎    player.CharacterAdded:Connect(function(character)
‎        task.wait(1)   -- انتظر اكتمال تحميل الشخصية
‎        local tool = ServerStorage:FindFirstChild('Sword')
‎        if tool then
‎            local copy = tool:Clone()
‎            copy.Parent = player.Backpack
‎        end
‎    end)
‎end)

7 - واجهة اللاعب (GUI)

واجهة كاملة تُنشأ برمجياً مع عداد يتحدث كل ثانية - ضعه كـ LocalScript في StarterPlayerScripts:

‎-- LocalScript في StarterPlayerScripts
‎local Players = game:GetService('Players')
‎local player = Players.LocalPlayer

‎-- إنشاء الواجهة برمجياً
‎local gui = Instance.new('ScreenGui')
‎gui.Name = 'HUD'
‎gui.Parent = player:WaitForChild('PlayerGui')

‎local label = Instance.new('TextLabel')
‎label.Size = UDim2.new(0, 220, 0, 50)
‎label.Position = UDim2.new(0, 20, 0, 20)
‎label.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
‎label.BackgroundTransparency = 0.4
‎label.TextColor3 = Color3.fromRGB(56, 189, 248)
‎label.TextSize = 22
‎label.Text = 'مرحباً بك!'
‎label.Parent = gui

‎-- عداد يزداد كل ثانية
‎local seconds = 0
‎task.spawn(function()
‎    while true do
‎        task.wait(1)
‎        seconds += 1
‎        label.Text = 'الوقت: ' .. seconds
‎    end
‎end)

8 - نظام الجولات (Round System)

الحلقة الرئيسية لأي لعبة تنافسية: استراحة ثم جولة ثم إعادة. ضعه في ServerScriptService:

‎-- ضعه في ServerScriptService
‎local Players = game:GetService('Players')

‎local INTERMISSION = 10   -- ثواني الاستراحة
‎local ROUND_TIME = 60     -- ثواني الجولة

‎local function startRound()
‎    print('بدأت الجولة!')
‎    for _, player in Players:GetPlayers() do
‎        player:LoadCharacter()   -- أعِد الشخصية لنقطة البداية
‎    end
‎    task.wait(ROUND_TIME)
‎    print('انتهت الجولة!')
‎end

‎-- الحلقة الرئيسية للعبة
‎while true do
‎    print('استراحة لمدة ' .. INTERMISSION .. ' ثواني')
‎    task.wait(INTERMISSION)
‎    startRound()
‎end

9 - متجر بسيط (السيرفر)

متجر آمن يتحقق من النقاط في السيرفر - الجزء الأول للسيرفر:

‎-- ===== السيرفر: ServerScriptService =====
‎local Players = game:GetService('Players')
‎local ReplicatedStorage = game:GetService('ReplicatedStorage')

‎-- إنشاء RemoteEvent إن لم يكن موجوداً
‎local buyRemote = ReplicatedStorage:FindFirstChild('BuyItem')
‎if not buyRemote then
‎    buyRemote = Instance.new('RemoteEvent')
‎    buyRemote.Name = 'BuyItem'
‎    buyRemote.Parent = ReplicatedStorage
‎end

‎local ITEMS = {
‎    Speed = 100,   -- سعر زيادة السرعة
‎    Jump = 150,    -- سعر زيادة القفز
‎}

‎buyRemote.OnServerEvent:Connect(function(player, itemName)
‎    local price = ITEMS[itemName]
‎    if not price then return end   -- منتج غير موجود

‎    local stats = player:FindFirstChild('leaderstats')
‎    local coins = stats and stats:FindFirstChild('Coins')
‎    if not coins or coins.Value < price then
‎        return   -- لا يملك نقاطاً كافية
‎    end

‎    coins.Value -= price   -- اخصم السعر

‎    local humanoid = player.Character and player.Character:FindFirstChildOfClass('Humanoid')
‎    if humanoid and itemName == 'Speed' then
‎        humanoid.WalkSpeed += 10
‎    elseif humanoid and itemName == 'Jump' then
‎        humanoid.JumpPower += 20
‎    end

‎    print(player.Name .. ' اشترى ' .. itemName)
‎end)

10 - متجر بسيط (العميل)

‎-- ===== العميل: LocalScript في StarterPlayerScripts =====
‎local ReplicatedStorage = game:GetService('ReplicatedStorage')
‎local remote = ReplicatedStorage:WaitForChild('BuyItem')

‎-- عندما يضغط اللاعب على زر الشراء في الواجهة
‎remote:FireServer('Speed')   -- اطلب شراء زيادة السرعة

11 - المشروع الكامل: لعبة جمع العملات

هذا السكربت يجمع كل ما تعلمناه: نقاط + حفظ بيانات + إنشاء + أحداث لمس. ضعه في ServerScriptService وشغّل اللعبة:

‎-- ===== لعبة جمع العملات الكاملة =====
‎-- ضع هذا السكربت كاملاً في ServerScriptService
‎local Players = game:GetService('Players')
‎local DataStoreService = game:GetService('DataStoreService')
‎local store = DataStoreService:GetDataStore('CoinGameV1')

‎local SPAWN_EVERY = 3    -- كوين جديد كل 3 ثواني
‎local COIN_VALUE = 10    -- قيمة كل كوين

‎-- ===== لوحة الصدارة والحفظ =====
‎local function setupPlayer(player)
‎    local stats = Instance.new('Folder')
‎    stats.Name = 'leaderstats'
‎    stats.Parent = player

‎    local coins = Instance.new('IntValue')
‎    coins.Name = 'Coins'
‎    coins.Parent = stats

‎    local success, saved = pcall(function()
‎        return store:GetAsync(player.UserId)
‎    end)
‎    coins.Value = (success and saved) or 0
‎end

‎Players.PlayerAdded:Connect(setupPlayer)

‎Players.PlayerRemoving:Connect(function(player)
‎    local stats = player:FindFirstChild('leaderstats')
‎    local coins = stats and stats:FindFirstChild('Coins')
‎    if coins then
‎        pcall(function()
‎            store:SetAsync(player.UserId, coins.Value)
‎        end)
‎    end
‎end)

‎-- ===== إنشاء كوين عشوائي =====
‎local function spawnCoin()
‎    local coin = Instance.new('Part')
‎    coin.Shape = Enum.PartType.Cylinder
‎    coin.Size = Vector3.new(0.4, 3, 3)
‎    coin.Orientation = Vector3.new(0, 0, 90)
‎    coin.Position = Vector3.new(math.random(-50, 50), 3, math.random(-50, 50))
‎    coin.Color = Color3.fromRGB(255, 200, 0)
‎    coin.Material = Enum.Material.Neon
‎    coin.Anchored = true
‎    coin.CanCollide = false
‎    coin.Parent = workspace

‎    local taken = false
‎    coin.Touched:Connect(function(hit)
‎        if taken then return end
‎        local character = hit.Parent
‎        if not character then return end
‎        local player = Players:GetPlayerFromCharacter(character)
‎        if player then
‎            taken = true
‎            local stats = player:FindFirstChild('leaderstats')
‎            if stats and stats:FindFirstChild('Coins') then
‎                stats.Coins.Value += COIN_VALUE
‎            end
‎            coin:Destroy()
‎        end
‎    end)

‎    -- اختفاء الكوين إذا لم يجمعه أحد
‎    task.delay(10, function()
‎        if coin.Parent then
‎            coin:Destroy()
‎        end
‎    end)
‎end

‎-- ===== حلقة اللعبة الرئيسية =====
‎while true do
‎    task.wait(SPAWN_EVERY)
‎    spawnCoin()
‎end

🔐 القاعدة الأمنية الذهبية

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

تهانينا! 🎉 بهذه الأمثلة أصبحت قادراً على بناء أنظمة حقيقية كاملة: نقاط، حفظ بيانات، متجر، جولات، وأدوات - وجميعها مترابطة بين السيرفر والعميل. جرّب دمجها في لعبة واحدة واصنع شيئاً مذهلاً!

الأخطاء الشائعة والتصحيح

جميعنا نواجه أخطاء، والمهم أن تعرف قراءتها وحلها:

🚧

attempt to index nil

تحاول الوصول لخاصية في قيمة فارغة - تحقق أن المتغير ليس nil قبل الوصول

📵

attempt to call a nil value

تنادي دالة غير موجودة - تأكد من اسم الدالة وصياغتها الصحيحة

🔚

Expected end

نسيت إغلاق if أو function أو حلقة - تأكد من كل كلمة end

‎-- الخطأ الشهير: attempt to index nil
‎local data = nil
‎-- print(data.score)  ← سيتوقف السكربت هنا

‎-- الحماية: تحقق أولاً
‎if data then
‎    print(data.score)
‎end

‎-- pcall: شغّل كوداً مع احتمال الخطأ بدون توقف السكربت
‎local ok, err = pcall(function()
‎    error('حدث خطأ متعمد!')
‎end)

‎print(ok)   -- false
‎print(err)  -- رسالة الخطأ

نصائح ذهبية

  • استخدم local دائماً إلا إذا كنت تحتاج متغيراً عاماً فعلاً
  • تذكر أن فهرسة الجداول تبدأ من 1 وليس 0
  • فقط nil و false يعتبران خطأ - حتى 0 والنص الفارغ صحيحان
  • استخدم task.wait بدلاً من wait القديمة
  • فعّل --!strict في بداية السكربتات لكشف الأخطاء مبكراً
  • قارن بـ == ولعدم المساواة استخدم ~= (وليس !=)
  • اقرأ رسالة الخطأ ورقم السطر في نافذة Output فهي تدلك على المشكلة
  • جرب كل مثال بنفسك وعدّله وشاهد النتيجة مباشرة
  • استخدم ModuleScript للكود المشترك بين السكربتات

مصادر موثوقة للتعلم

🚀

ابدأ رحلتك الآن

افتح Roblox Studio وطبّق كل مثال في هذا الدليل بنفسك، فالتعلم الحقيقي يأتي من التجربة!