مرحبا سكربت هذه يعاني من مشاكل معينة وهو بلغة roblox lua --[[
سكربت مدمج للشخصية غير اللاعبة (NPC)
- يتجول بين نقاط محددة (waypoints).
- يتوقف ويوقف أنيميشن المشي عند اقتراب لاعب.
- يتفاعل (مع أنيميشن) عند لمس اللاعب له مباشرة.
- يربط أنيميشن المشي بالحركة الفعلية.
المتطلبات:
1. نموذج NPC يحتوي على Humanoid, HumanoidRootPart, Animator.
2. مجلد "WaypointsFolder" في Workspace يحتوي على Parts باسم "Waypoint1", "Waypoint2", ...
3. يجب استبدال معرفات الأنيميشن (Animation IDs) أدناه.
]]
local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local rootPart = npc:WaitForChild("HumanoidRootPart")
local animator = humanoid:WaitForChild("Animator")
local playersService = game:GetService("Players")
local runService = game:GetService("RunService")
-- ===== إعدادات =====
local waypointsFolderName = "WaypointsFolder"
local numberOfWaypoints = 8 -- العدد المتوقع للنقاط
local detectionRadius = 50 -- مدى كشف اللاعب للتوقف
local movementTimeout = 15 -- أقصى وقت انتظار للوصول للنقطة
local checkInterval = 0.5 -- الفاصل الزمني للحلقة الرئيسية
local retryDelay = 1 -- تأخير إعادة محاولة الوصول لنفس النقطة
local WALK_ANIMATION_ID = "rbxassetid://139807428535351" -- <<== استبدل بالـ ID الصحيح لأنيميشن المشي
local INTERACTION_ANIMATION_ID = "rbxassetid://133212607109891" -- <<== استبدل بالـ ID الصحيح لأنيميشن التفاعل
-- يمكنك إضافة ID لأنيميشن الوقوف (Idle) إذا أردت
-- local IDLE_ANIMATION_ID = "rbxassetid://0"
local INTERACTION_COOLDOWN_AFTER_ANIM = 1 -- كول داون قصير بعد انتهاء أنيميشن التفاعل (بالثواني)
-- ===== تحميل الأنيميشن =====
local walkingAnimation = Instance.new("Animation")
walkingAnimation.Name = "WalkingAnimation"
walkingAnimation.AnimationId = WALK_ANIMATION_ID
walkingAnimation.Parent = npc -- ربطه بالـ NPC لتسهيل التنظيف
local walkAnimationTrack = animator:LoadAnimation(walkingAnimation)
walkAnimationTrack.Looped = true
walkAnimationTrack.Priority = Enum.AnimationPriority.Core -- أو Action لتجاوز الأنيميشن الافتراضي
local interactionAnimation = Instance.new("Animation")
interactionAnimation.Name = "InteractionAnimation"
interactionAnimation.AnimationId = INTERACTION_ANIMATION_ID
interactionAnimation.Parent = npc
local interactionTrack = animator:LoadAnimation(interactionAnimation)
interactionTrack.Looped = false -- التفاعل عادة ليس حلقيًا
interactionTrack.Priority = Enum.AnimationPriority.Action -- أولوية أعلى من المشي
-- يمكنك تحميل أنيميشن الوقوف هنا إذا استخدمته
--[[
local idleAnimation = Instance.new("Animation")
idleAnimation.Name = "IdleAnimation"
idleAnimation.AnimationId = IDLE_ANIMATION_ID
idleAnimation.Parent = npc
local idleAnimationTrack = animator:LoadAnimation(idleAnimation)
idleAnimationTrack.Looped = true
idleAnimationTrack.Priority = Enum.AnimationPriority.Core
--]]
-- ===== إعداد النقاط =====
local waypointsFolder = workspace:WaitForChild(waypointsFolderName)
local waypoints = {}
if waypointsFolder then
for i = 1, numberOfWaypoints do
local waypoint = waypointsFolder:FindFirstChild("Waypoint" .. i)
if waypoint and waypoint:IsA("BasePart") then
table.insert(waypoints, waypoint)
else
warn("لم يتم العثور على Waypoint" .. i .. " أو أنه ليس BasePart في " .. waypointsFolderName)
end
end
else
warn("لم يتم العثور على مجلد النقاط: " .. waypointsFolderName)
end
if #waypoints == 0 then
warn("لم يتم العثور على أي نقاط مسار صالحة. سيتم تعطيل السكربت.")
script.Disabled = true
return
elseif #waypoints < numberOfWaypoints then
warn("تم العثور على " .. #waypoints .. " نقاط فقط بدلاً من " .. numberOfWaypoints .. ". سيستمر السكربت بالنقاط الموجودة.")
numberOfWaypoints = #waypoints -- استخدم العدد الفعلي للنقاط الموجودة
end
-- ===== متغيرات الحالة =====
local currentWaypointIndex = 1
local patrolMode = "sequential" -- "sequential" أو "random"
local isStoppedDueToPlayer = false
local isInteracting = false
local interactionDebounce = false
local movementInterrupted = false -- لتتبع ما إذا تم إيقاف الحركة الحالية (بسبب لاعب أو تفاعل)
-- ===== الدوال المساعدة =====
-- دالة للتحقق من وجود لاعبين قريبين
local function isPlayerNearby()
local npcPosition = rootPart.Position
for _, player in ipairs(playersService:GetPlayers()) do
local character = player.Character
if character and character ~= npc then -- تأكد أنه ليس الـ NPC نفسه
local playerRoot = character:FindFirstChild("HumanoidRootPart")
local playerHumanoid = character:FindFirstChildOfClass("Humanoid")
if playerRoot and playerHumanoid and playerHumanoid.Health > 0 then
if (playerRoot.Position - npcPosition).Magnitude <= detectionRadius then
return true -- تم العثور على لاعب قريب
end
end
end
end
return false -- لا يوجد لاعبون قريبون
end
-- دالة لإيقاف حركة وأنيميشن الـ NPC بسبب لاعب
local function stopMovementDueToPlayer()
if not isStoppedDueToPlayer then
print("لاعب قريب! إيقاف الحركة والأنيميشن.")
isStoppedDueToPlayer = true
movementInterrupted = true -- إشارة إلى أن الحركة الحالية يجب أن تتوقف
humanoid:MoveTo(rootPart.Position) -- أمر بالتوقف في المكان الحالي
if walkAnimationTrack.IsPlaying then
walkAnimationTrack:Stop()
end
-- يمكنك تشغيل أنيميشن الوقوف هنا إذا أردت
-- if not idleAnimationTrack.IsPlaying then idleAnimationTrack:Play() end
end
end
-- دالة لاستئناف حركة الـ NPC (بعد ابتعاد اللاعب)
local function resumeMovementAfterPlayerLeft()
if isStoppedDueToPlayer then
print("ابتعد اللاعبون. استعداد لاستئناف الحركة.")
isStoppedDueToPlayer = false
-- الحلقة الرئيسية ستتعامل مع استئناف الحركة والأنيميشن
-- يمكنك إيقاف أنيميشن الوقوف هنا إذا كنت تستخدمه
-- if idleAnimationTrack.IsPlaying then idleAnimationTrack:Stop() end
end
end
-- دالة لاختيار النقطة التالية (فقط بعد الوصول)
local function getNextWaypointIndex()
if patrolMode == "sequential" then
currentWaypointIndex = currentWaypointIndex + 1
if currentWaypointIndex > #waypoints then -- استخدم الطول الفعلي لجدول النقاط
print("اكتمل المسار التسلسلي. التبديل إلى الوضع العشوائي.")
patrolMode = "random"
local nextIndex
local lastSequentialIndex = #waypoints -- آخر نقطة في التسلسل
repeat
nextIndex = math.random(1, #waypoints)
until nextIndex ~= lastSequentialIndex
currentWaypointIndex = nextIndex
end
elseif patrolMode == "random" then
local previousIndex = currentWaypointIndex
repeat
currentWaypointIndex = math.random(1, #waypoints)
until currentWaypointIndex ~= previousIndex
print("التحرك عشوائيًا إلى Waypoint " .. currentWaypointIndex)
end
return currentWaypointIndex
end
-- دالة لتحديث أنيميشن المشي بناءً على الحركة
local function updateWalkAnimation()
-- تأكد أننا لسنا متوقفين بسبب لاعب أو تفاعل
if not isStoppedDueToPlayer and not isInteracting then
-- تحقق مما إذا كان الـ Humanoid يتحرك بالفعل
if humanoid.MoveDirection.Magnitude > 0.1 then
if not walkAnimationTrack.IsPlaying then
-- أوقف أنيميشن الوقوف إذا كان يعمل
-- if idleAnimationTrack.IsPlaying then idleAnimationTrack:Stop() end
walkAnimationTrack:Play()
print("تشغيل أنيميشن المشي.")
end
else
if walkAnimationTrack.IsPlaying then
walkAnimationTrack:Stop()
print("إيقاف أنيميشن المشي (لا توجد حركة).")
-- شغل أنيميشن الوقوف إذا كان متاحًا ولم يكن يعمل
-- if idleAnimationTrack and not idleAnimationTrack.IsPlaying then idleAnimationTrack:Play() end
end
end
elseif walkAnimationTrack.IsPlaying then -- إذا كنا متوقفين لسبب ما، أوقف المشي
walkAnimationTrack:Stop()
print("إيقاف أنيميشن المشي (متوقف بسبب لاعب أو تفاعل).")
-- شغل أنيميشن الوقوف
-- if idleAnimationTrack and not idleAnimationTrack.IsPlaying then idleAnimationTrack:Play() end
end
end
-- ===== منطق التفاعل عند اللمس =====
local function onTouch(hit)
if interactionDebounce or isInteracting then -- لا تتفاعل إذا كان الكول داون نشطًا أو يتفاعل بالفعل
return
end
local character = hit.Parent
local player = character and playersService:GetPlayerFromCharacter(character)
-- تحقق مما إذا كان الجزء الملموس ينتمي إلى لاعب وليس الـ NPC نفسه
if player and character ~= npc then
local playerHumanoid = character:FindFirstChildOfClass("Humanoid")
if playerHumanoid and playerHumanoid.Health > 0 then
print("تم لمس الـ NPC بواسطة اللاعب: " .. player.Name)
interactionDebounce = true -- ابدأ الكول داون المؤقت للمس
isInteracting = true -- إشارة إلى أن الـ NPC مشغول بالتفاعل
movementInterrupted = true -- أوقف أي حركة حالية
-- أوقف الحركة فوراً
humanoid:MoveTo(rootPart.Position)
-- إيقاف أنيميشن المشي (أو الوقوف)
if walkAnimationTrack.IsPlaying then walkAnimationTrack:Stop() end
-- if idleAnimationTrack.IsPlaying then idleAnimationTrack:Stop() end
-- تشغيل أنيميشن التفاعل وانتظار انتهائه
print("بدء أنيميشن التفاعل.")
interactionTrack:Play()
interactionTrack.Stopped:Wait() -- انتظر حتى ينتهي الأنيميشن
print("انتهاء أنيميشن التفاعل.")
-- إعادة تعيين حالة التفاعل والانتظار قليلاً قبل السماح بتفاعل جديد
isInteracting = false
task.wait(INTERACTION_COOLDOWN_AFTER_ANIM)
interactionDebounce = false -- اسمح بتفاعل جديد
print("الـ NPC متاح للتفاعل مرة أخرى.")
-- الحلقة الرئيسية ستستأنف الحركة/الأنيميشن المناسب
end
end
end
-- ربط دالة اللمس
rootPart.Touched:Connect(onTouch)
-- ===== الحلقة الرئيسية للدوريات والكشف والأنيميشن =====
local function mainLoop()
-- شغل أنيميشن الوقوف في البداية إذا كنت تستخدمه
-- if idleAnimationTrack then idleAnimationTrack:Play() end
while npc.Parent and humanoid.Health > 0 do
local playerIsNear = isPlayerNearby()
-- التعامل مع حالة التوقف بسبب اللاعب
if playerIsNear and not isInteracting then
stopMovementDueToPlayer()
elseif not playerIsNear and isStoppedDueToPlayer then -- لاحظ التحقق من not isInteracting هنا أيضًا
resumeMovementAfterPlayerLeft()
end
-- محاولة التحرك فقط إذا لم نكن متوقفين بسبب لاعب ولم نكن نتفاعل
if not isStoppedDueToPlayer and not isInteracting then
local targetWaypoint = waypoints[currentWaypointIndex]
if targetWaypoint then
print("محاولة التحرك إلى Waypoint " .. currentWaypointIndex .. " (" .. patrolMode .. ")")
movementInterrupted = false -- إعادة تعيين علامة المقاطعة قبل بدء الحركة
humanoid:MoveTo(targetWaypoint.Position)
local reached = false
local connection
local finishedEventFired = false
-- ربط حدث انتهاء الحركة
connection = humanoid.MoveToFinished:Connect(function(didReach)
if connection then connection:Disconnect() end
connection = nil
if not movementInterrupted then -- تجاهل النتيجة إذا تمت مقاطعة الحركة
reached = didReach
finishedEventFired = true
if not reached then
warn("MoveToFinished أبلغ عن فشل الوصول إلى Waypoint " .. currentWaypointIndex)
end
else
print("تم تجاهل MoveToFinished بسبب مقاطعة (لاعب أو تفاعل).")
end
end)
-- انتظر حتى ينتهي الحدث أو تنتهي المهلة أو تتم المقاطعة
local waitStartTime = os.clock()
while not finishedEventFired and not movementInterrupted and not isInteracting and not isStoppedDueToPlayer and (os.clock() - waitStartTime < movementTimeout) do
-- أثناء الانتظار للوصول، قم بتحديث أنيميشن المشي
updateWalkAnimation()
task.wait(0.1)
end
-- تنظيف الاتصال إذا كان لا يزال موجودًا
if connection then
connection:Disconnect()
connection = nil
end
-- تقييم النتيجة بعد الانتظار
if movementInterrupted or isInteracting or isStoppedDueToPlayer then
print("تمت مقاطعة الحركة إلى Waypoint " .. currentWaypointIndex)
if walkAnimationTrack.IsPlaying then walkAnimationTrack:Stop() end
-- لا تغير currentWaypointIndex
elseif finishedEventFired and reached then
print("تم الوصول بنجاح إلى Waypoint " .. currentWaypointIndex)
if walkAnimationTrack.IsPlaying then walkAnimationTrack:Stop() end -- أوقف المشي عند الوصول
-- يمكنك تشغيل أنيميشن الوقوف هنا للحظة
currentWaypointIndex = getNextWaypointIndex() -- اختر النقطة التالية فقط عند النجاح
elseif finishedEventFired and not reached then
warn("فشل الوصول إلى Waypoint " .. currentWaypointIndex .. ". المحاولة مرة أخرى في الدورة التالية.")
if walkAnimationTrack.IsPlaying then walkAnimationTrack:Stop() end
task.wait(retryDelay)
else -- حدث انتهاء المهلة
warn("انتهت مهلة الحركة إلى Waypoint " .. currentWaypointIndex .. ". المحاولة مرة أخرى في الدورة التالية.")
humanoid:MoveTo(rootPart.Position) -- أوقف محاولة الحركة الحالية
if walkAnimationTrack.IsPlaying then walkAnimationTrack:Stop() end
task.wait(retryDelay)
end
else
warn("Waypoint " .. currentWaypointIndex .. " غير صالح. محاولة النقطة التالية.")
currentWaypointIndex = getNextWaypointIndex()
task.wait(1)
end
else
-- إذا كنا متوقفين بسبب لاعب أو نتفاعل، تأكد من أن أنيميشن المشي متوقف
updateWalkAnimation() -- سيقوم هذا بإيقاف المشي إذا لزم الأمر
end
-- انتظر قبل الدورة التالية للحلقة الرئيسية
task.wait(checkInterval)
end
print("توقف سكربت الـ NPC (مات أو تمت إزالته).")
end
-- ===== التنظيف عند حذف الـ NPC =====
npc.Destroying:Connect(function()
print("تنظيف سكربت الـ NPC...")
-- إيقاف وتدمير كل الأنيميشن
if walkAnimationTrack then
walkAnimationTrack:Stop()
walkAnimationTrack:Destroy()
end
if walkingAnimation then
walkingAnimation:Destroy()
end
if interactionTrack then
interactionTrack:Stop()
interactionTrack:Destroy()
end
if interactionAnimation then
interactionAnimation:Destroy()
end
-- أضف تنظيف لأنيميشن الوقوف إذا استخدمته
--[[
if idleAnimationTrack then
idleAnimationTrack:Stop()
idleAnimationTrack:Destroy()
end
if idleAnimation then
idleAnimation:Destroy()
end
--]]
-- يمكنك إضافة أي تنظيف آخر هنا (مثل فصل الاتصالات إن وجدت)
end
-- ===== بدء التشغيل =====
if #waypoints > 0 then
task.spawn(mainLoop) -- ابدأ الحلقة الرئيسية في خيط منفصل
print("تم تهيئة سكربت الـ NPC المدمج بنجاح.")
else
warn("لا يمكن بدء الدورية بسبب عدم وجود نقاط مسار كافية.")
script.Disabled = true
end