JIT
Roblox Lua NPC Script Issues
مرحبا سكربت هذه يعاني من مشاكل معينة وهو بلغة 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
Updated files
v1