الدليل الشامل لبرمجة ألعاب روبلوكس - من أول سطر كود إلى الأنظمة الاحترافية
Luau هي لغة البرمجة النصية الرسمية لمنصة روبلوكس، مبنية على لغة Lua 5.1 الشهيرة لكنها مطوَّرة بنظام أنواع حديث وأداء أعلى بكثير. بها تبرمج كل شيء في لعبتك: حركة الشخصيات، الأسلحة، الأنظمة، المتاجر، الواجهات وكل ما تتخيله.
أساس قوي من أشهر لغات البرمجة النصية في العالم
أضف أنواعاً للمتغيرات ويكتشف Luau أخطاءك قبل التشغيل
أسرع من Lua العادية بمرات عديدة بفضل المحسّنات الحديثة
تعمل مباشرة داخل Roblox Studio مع كل واجهات اللعبة
كل ما تحتاجه لبدء رحلة التعلم هو Roblox Studio المجاني:
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 -- متغير محلي - الأفضل دائماً
يعني لا قيمة - عدم وجود شيء
قيمتان فقط: true و false
كل الأعداد صحيحة وعشرية
النصوص بين علامتي تنصيص
الجدول: مصفوفات وقواميس
الدوال القابلة للاستدعاء
خيوط التنفيذ المتوازي
بيانات مخصصة من روبلوكس
كل العمليات الحسابية الأساسية متوفرة، مع مكتبة 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
دمج النصوص ومعالجتها من أكثر ما ستستخدمه في البرمجة:
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 ينفذ كوداً حسب تحقق شرط معين. لاحظ 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
الدالة قطعة كود قابلة لإعادة الاستخدام، تأخذ مدخلات وترجع مخرجات:
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
الجدول هو أقوى هيكل بيانات في 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)
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)
-- صناعة الكلاس
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.clock لقياس الوقت و os.time للطابع الزمني
خيوط برمجية متقدمة للتحكم في التنفيذ
التعامل الصحيح مع النصوص العربية واليونيكود
التعامل مع البيانات الثنائية بسرعة عالية
print(os.clock()) -- الوقت بالثواني منذ بدء التشغيل
print(os.time()) -- الطابع الزمني الحالي
local co = coroutine.create(function()
print('أنا داخل الكوروتين')
end)
coroutine.resume(co)
print(utf8.len('مرحباً')) -- عدد الأحرف الحقيقي
هذه الميزات تجعل Luau أقوى بكثير من Lua العادية، وستستخدمها يومياً:
أنواع اختيارية تكشف أخطاءك قبل التشغيل
تجاهل باقي الدورة وانتقل للتالية داخل الحلقات
+= و -= و *= و /= لاختصار العمليات
print مع أقواس backtick يدمج المتغيرات في النص مباشرة
for k, v in table do مباشرة بدون pairs
local x = if شرط then a else b في سطر واحد
task.wait و spawn و delay بديلة wait القديمة
table.clone و freeze و create دوال جديدة قوية
--!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.wait(2)
-- شغّل دالة فوراً في خيط منفصل
task.spawn(function()
print('يعمل في الخلفية')
end)
-- شغّل دالة بعد تأخير
task.delay(3, function()
print('بعد 3 ثواني')
end)
-- شغّل في نهاية الإطار الحالي
task.defer(print, 'لاحقاً!')
الآن بعد أن أتقنت اللغة، هذه هي المفاهيم التي ستحتاجها لبرمجة ألعاب روبلوكس فعلياً:
يعمل على السيرفر - المنطق الرئيسي وحماية اللعبة
يعمل عند اللاعب - الواجهات والتحكم والمؤثرات
كود مشترك تستدعيه من أي مكان عبر require
-- الوصول للجزء الذي يحتوي السكربت
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')
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)
-- السيرفر: إنشاء واستقبال الرسائل
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('مرحباً!')
الآن وقت التطبيق الحقيقي! هذه سكربتات كاملة جاهزة للنسخ والاستخدام مباشرة، تغطي كل ما تحتاجه لبناء لعبة حقيقية خطوة بخطوة.
سكربتات السيرفر - منطق اللعبة والحماية
أشياء مخفية عن اللاعبين - أدوات سرية
مشترك بين السيرفر والعميل - RemoteEvents
واجهات اللاعب الرسومية
أدوات تبدأ مع اللاعب فور دخوله
سكربتات العميل - التحكم والواجهات
كل سكربت احترافي يتبع هذا الترتيب: الخدمات ثم الإعدادات ثم الدوال ثم الربط:
-- ===== 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
هذا السكربت ينشئ لوحة نقاط تظهر لكل اللاعبين في الزاوية العلوية:
-- ضعه في 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)
لكي لا تضيع نقاط اللاعبين عند الخروج، احفظها في 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)
بوابة تنزلق للأعلى عند لمسها، مع حماية من الفتح المتكرر السريع:
-- ضعه داخل الباب كـ 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)
من أشهر السكربتات الكلاسيكية - أي لاعب يلمس الجزء يموت فوراً:
-- ضعه داخل الجزء القاتل كـ 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)
يعطى كل لاعب أداة تلقائياً عند دخوله اللعبة، ومع كل إعادة إنعاش:
-- ضعه في 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)
واجهة كاملة تُنشأ برمجياً مع عداد يتحدث كل ثانية - ضعه كـ 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)
الحلقة الرئيسية لأي لعبة تنافسية: استراحة ثم جولة ثم إعادة. ضعه في 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
متجر آمن يتحقق من النقاط في السيرفر - الجزء الأول للسيرفر:
-- ===== السيرفر: 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)
-- ===== العميل: LocalScript في StarterPlayerScripts =====
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local remote = ReplicatedStorage:WaitForChild('BuyItem')
-- عندما يضغط اللاعب على زر الشراء في الواجهة
remote:FireServer('Speed') -- اطلب شراء زيادة السرعة
هذا السكربت يجمع كل ما تعلمناه: نقاط + حفظ بيانات + إنشاء + أحداث لمس. ضعه في 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
السيرفر هو السلطة الوحيدة في لعبتك: لا تثق أبداً بأي شيء يرسله العميل. تحقق دائماً من الرصيد والسعر والمسافة والصلاحيات في السيرفر قبل التنفيذ، هكذا تحمي لعبتك من المخربين والغشاشين.
تهانينا! 🎉 بهذه الأمثلة أصبحت قادراً على بناء أنظمة حقيقية كاملة: نقاط، حفظ بيانات، متجر، جولات، وأدوات - وجميعها مترابطة بين السيرفر والعميل. جرّب دمجها في لعبة واحدة واصنع شيئاً مذهلاً!
جميعنا نواجه أخطاء، والمهم أن تعرف قراءتها وحلها:
تحاول الوصول لخاصية في قيمة فارغة - تحقق أن المتغير ليس nil قبل الوصول
تنادي دالة غير موجودة - تأكد من اسم الدالة وصياغتها الصحيحة
نسيت إغلاق 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) -- رسالة الخطأ
افتح Roblox Studio وطبّق كل مثال في هذا الدليل بنفسك، فالتعلم الحقيقي يأتي من التجربة!