Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

Zombie NPC keeps trying to follow another Zombie NPC with the same name?

Asked by 1 year ago

I'm trying to practice OOP with zombies. What it's supposed to do is check if a player is around them, NOT an NPC. It will check the magnitude of each player's characters, and chooses the character model with the least magnitude. Then it chases that player using pathfinding service. However, instead of doing that, it just moves to another NPC with the same name. I am using 2 module scripts and a regular script to make it work.

Script (Placed in ServerScriptService):

local replicatedStorage = game:GetService("ReplicatedStorage")
local zombieModel = workspace.Zombie
local Zombie = require(game:GetService("ServerScriptService").Modules.NPCModule.ZombieModule)

local newZombie = Zombie.new(zombieModel, "Zombie", workspace.Ground.Position + Vector3.new(0,10,0))
local model = newZombie.Model
local zombiePosition = model.HumanoidRootPart.Position
local targetPosition = nil

model.Humanoid.WalkSpeed = newZombie.Speed
model.Humanoid.Health = newZombie.Health
model.HumanoidRootPart.Position = newZombie.SpawnLocation
model.Parent = workspace

game.Players.PlayerAdded:Connect(function(player)
    player.CharacterAdded:Connect(function(character)
        print("reached")
        game:GetService("RunService").Heartbeat:Connect(function()
            targetPosition = newZombie:GetClosestTarget()
            print(targetPosition)
            if targetPosition and character.HumanoidRootPart.Position == targetPosition then
                newZombie:FindTarget(zombiePosition, targetPosition)
            end
        end)
    end)
end)

ZombieModule (Module script placed in ServerScriptService under a module called "NPCModule"):

local NPC = require(script.Parent)
local runService = game:GetService("RunService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local PFS = game:GetService("PathfindingService")

local Zombie = {}
Zombie.__index = Zombie

function Zombie.new(model, zombieType, position)
    local newZombie = NPC.new(model, position)
    setmetatable(newZombie, Zombie)

    if zombieType == "Zombie" then
        print("is zombie")
        newZombie.Speed = 15
        newZombie.ZombieType = zombieType
        print(newZombie.Model.Name)
        print(newZombie.Model.HumanoidRootPart.Name)
    end

    return newZombie
end

function Zombie:GetClosestTarget()
    local model = self.Model
    if model then
        print("not nil!")
    end
    local target = nil
    local lowestMagnitudeTarget = nil
    local lowestMagnitude = 99999999999999999
    local targetPosition = nil
    local zombiePosition = model.HumanoidRootPart.Position

    for i, player in pairs(game.Players:GetPlayers()) do
        local characterModel = player.Character
        local hrpPosition = characterModel.HumanoidRootPart.Position
        print(hrpPosition)
        local magnitude = (hrpPosition - zombiePosition).Magnitude

        if magnitude < lowestMagnitude and game.Players:GetPlayerFromCharacter(characterModel)then
            lowestMagnitude = magnitude
            lowestMagnitudeTarget = hrpPosition
        end
    end

    target = lowestMagnitudeTarget
    return target
end

function Zombie:FindTarget(zombiePosition, targetPosition)
    local model = self.Model
    local zombieHumanoid = model.Humanoid

    local path = PFS:CreatePath({
        AgentRadius = 2,
        AgentHeight = 5,
        AgentCanJump = true,
        AgentCanClimb = true,
        Costs = {
            Water = 20
        }
    })

    while true do
        path:ComputeAsync(zombiePosition, targetPosition)
        if path.Status == Enum.PathStatus.Success then
            for i, waypoint in pairs(path:GetWaypoints()) do
                if waypoint.Action == Enum.PathWaypointAction.Jump then
                    zombieHumanoid:ChangeState(Enum.HumanoidStateType.Jumping)
                end
                zombieHumanoid:MoveTo(waypoint.Position)
                zombieHumanoid.MoveToFinished:Wait()
            end
        end
    end
end

return Zombie

NPCModule (Module script placed in ServerScriptService):

local NPC = {}
NPC.__index = NPC

function NPC.new(model, position)
    local newNPC = {}
    setmetatable(newNPC, NPC)

    newNPC.Model = model:Clone()
    newNPC.Health = 100
    newNPC.Speed = 16
    newNPC.InteractionDistance = 2
    newNPC.SpawnLocation = position

    return newNPC
end

return NPC

As always, thank you for your time.

0
I usually add a BoolValue named "NPC" in any NPCs. You can use them to check using "if not Zombie:FindFirstChild("NPC") then" or something. Another idea is to use "game.Players:GetPlayerFromCharacter(model)" when checking for players so that if it isn't a player, it returns nil RainDroutz 23 — 1y

1 answer

Log in to vote
0
Answered by 1 year ago

hiya!

i'm not all too sure myself as to why you're code isn't working, although i thought cleaning it up would help; hopefully give a new aspect to the code

changes

  • changed how the zombie checks for the closest player
  • minified the amount of variables used in Zombie:GetClosestTarget()
  • added "AdvancedAI" function to zombie module

script in ServerScriptService

local replicatedStorage = game:GetService("ReplicatedStorage")
local zombieModel = workspace.Zombie
local Zombie = require(game:GetService("ServerScriptService").Modules.NPCModule.ZombieModule)

local newZombie = Zombie.new(zombieModel, "Zombie", workspace.Ground.Position + Vector3.new(0,10,0))
local model = newZombie.Model
local zombiePosition = model.HumanoidRootPart.Position
local targetPosition = nil

model.Humanoid.WalkSpeed = newZombie.Speed
model.Humanoid.Health = newZombie.Health
model.HumanoidRootPart.Position = newZombie.SpawnLocation
model.Parent = workspace

game:GetService("RunService").Heartbeat:Connect(newZombie.AdvancedAI)

zombie module

local NPC = require(script.Parent)
local runService = game:GetService("RunService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local PFS = game:GetService("PathfindingService")

local Zombie = {}
Zombie.__index = Zombie

function Zombie.new(model, zombieType, position)
    local newZombie = NPC.new(model, position)
    setmetatable(newZombie, Zombie)

    if zombieType == "Zombie" then
        print("is zombie")
        newZombie.Speed = 15
        newZombie.ZombieType = zombieType
        print(newZombie.Model.Name)
        print(newZombie.Model.HumanoidRootPart.Name)
    end

    return newZombie
end

function Zombie:AdvancedAI()
    local ClosestTarget = self:GetClosestTarget()
    if not ClosestTarget then
        -- warn("unable to identify a close target - wth")
        return false -- fail :(
    end
    self:FindTarget(self.Model.HumanoidRootPart.Position, ClosestTarget.HumanoidRootPart.Position)
    return true -- success!
end

function Zombie:GetClosestTarget()
    assert(self.Model, string.format("no model exists! (got %s)", tostring(self.Model or nil)))
    --[[
    if you don't fancy a hard error, here's a less ASSERTIVE assert:

    if not self.Model then
        -- end the call
        return
    end
    ]]
    local target = nil
    local lowestMagnitudeTarget = nil
    local lowestMagnitude = 12^12 -- aka a big hecking number
    local zombiePosition = self.Model.HumanoidRootPart.Position

    for i, player in pairs(game.Players:GetPlayers()) do
        local characterModel = player.Character
        local magnitude = (characterModel.HumanoidRootPart.Position - zombiePosition).Magnitude

        if magnitude < lowestMagnitude then
            lowestMagnitude = magnitude
            lowestMagnitudeTarget = characterModel
        end
    end

    return lowestMagnitudeTarget
end

function Zombie:FindTarget(zombiePosition, targetPosition)
    local model = self.Model
    local zombieHumanoid = model.Humanoid

    local path = PFS:CreatePath({
        AgentRadius = 2,
        AgentHeight = 5,
        AgentCanJump = true,
        AgentCanClimb = true,
        Costs = {
            Water = 20
        }
    })

    while true do
        path:ComputeAsync(zombiePosition, targetPosition)
        if path.Status == Enum.PathStatus.Success then
            for i, waypoint in pairs(path:GetWaypoints()) do
                if waypoint.Action == Enum.PathWaypointAction.Jump then
                    zombieHumanoid:ChangeState(Enum.HumanoidStateType.Jumping)
                end
                zombieHumanoid:MoveTo(waypoint.Position)
                zombieHumanoid.MoveToFinished:Wait()
            end
        end
    end
end

return Zombie
Ad

Answer this question