I was able to import the FBX model in Roblox Studio and even animate it, but this Humanoid cannot climb stairs and the animation has a 1 second delay.
I need to remove this delay, teach him to climb stairs and jump with animation.
Animate script on tree,
Humanoid model and animation
Here is the animation script
model = script.Parent
Humanoid = model:WaitForChild("Humanoid")
RunService = game:GetService("RunService")
Humanoid:ChangeState(Enum.HumanoidStateType.None)
idleAnimation = script:WaitForChild('idle');
walkAnimation = script:WaitForChild('walk');
runAnimation = script:WaitForChild('run');
jumpAnimation = script:WaitForChild('jump');
Pose = "none"
LastPose = Pose
PoseTime = tick()
ToolAnimTime = 0
local animation: AnimationTrack = nil;
function playAnim(anim)
if animation ~= nil and animation.IsPlaying then
--print(animation.Animation.Name, ' == ', anim.Name, ' = ', animation.Animation.Name == anim.Name)
if animation.Animation.Name == anim.Name then
return;
end
animation:Stop();
end
print('playAnim ', anim.Name)
animation = Humanoid:LoadAnimation(anim)
animation.Looped = false
animation:Play()
end
function SetPose(pose)
LastPose = Pose
Pose = pose
PoseTime = tick()
end
function OnRunning(Speed)
if Speed > 0 then
SetPose("Running")
else
SetPose("Standing")
end
end
function OnDied()
SetPose("Dead")
end
function OnJumping()
SetPose("Jumping")
end
function OnClimbing()
SetPose("Climbing")
end
function OnGettingUp()
SetPose("GettingUp")
end
function OnFreeFall()
SetPose("FreeFall")
end
function OnFallingDown()
SetPose("FallingDown")
end
function OnSeated()
SetPose("Seated")
end
function OnPlatformStanding()
SetPose("PlatformStanding")
end
function OnSwimming(Speed)
return OnRunning(Speed)
end
function MoveFreeFall()
--RightShoulder.MaxVelocity = 0.15
--LeftShoulder.MaxVelocity = 0.15
--RightShoulder.DesiredAngle = 0.5
--LeftShoulder.DesiredAngle = -0.5
--RightHip.DesiredAngle = -0.5
--LeftHip.DesiredAngle = 0.5
end
function MoveSit()
--RightShoulder.MaxVelocity = 0.15
--LeftShoulder.MaxVelocity = 0.15
--RightShoulder.DesiredAngle = (math.pi / 2)
--LeftShoulder.DesiredAngle = -(math.pi / 2)
--RightHip.DesiredAngle = 1
--LeftHip.DesiredAngle = -1
end
function Move()
if (Pose == "Jumping") then
playAnim(jumpAnimation)
return
elseif (Pose == "FreeFall") then
MoveFreeFall()
return
elseif (Pose == "Seated") then
MoveSit()
return
end
local ClimbFudge = 0
if (Pose == "Running") then
playAnim(runAnimation);
elseif (Pose == "Climbing") then
playAnim(jumpAnimation);
else
playAnim(idleAnimation);
end
end
Humanoid.Died:connect(OnDied)
Humanoid.Running:connect(OnRunning)
Humanoid.Jumping:connect(OnJumping)
Humanoid.Climbing:connect(OnClimbing)
Humanoid.GettingUp:connect(OnGettingUp)
Humanoid.FreeFalling:connect(OnFreeFall)
Humanoid.FallingDown:connect(OnFallingDown)
Humanoid.Seated:connect(OnSeated)
Humanoid.PlatformStanding:connect(OnPlatformStanding)
Humanoid.Swimming:connect(OnSwimming)
RunService.Stepped:connect(Move)
I was able to import the model, import the animation, but now there is a problem with the script, the game has a delay of 1 second.
I had a delay per second due to the fact that the script was server-side, I thought that if you make the script local, then other players will not see the animation of the current player, but it turns out they will. So I just made the server script a local script.
Related
I'm first going to start off with my code which is in a Server Script:
local an = Instance.new("Animation")
an.AnimationId = "rbxassetid://11699868187"
local idle = Instance.new("Animation")
idle.AnimationId = "rbxassetid://11699874417"
local ant
local idlet
local canthit = {}
local knockTime = 30
function takeOwnership(model)
for i, v in pairs(model:GetDescendants()) do
if v:IsA("Part") or v:IsA("BasePart") then
repeat
v:SetNetworkOwner(nil)
until v:GetNetworkOwner() == nil
end
end
end
function resetOwernship(model, ply)
for i, v in pairs(model:GetDescendants()) do
if v:IsA("Part") or v:IsA("BasePart") then
v:SetNetworkOwner(ply)
end
end
end
function rag(char)
local ah = Instance.new("IntValue")
ah.Parent = char
ah.Name = "MaxTime"
ah.Value = knockTime
local thib = Instance.new("IntValue")
thib.Parent = char
thib.Name = "Time"
thib.Value = knockTime
for i, v in pairs(char:GetDescendants()) do
if v:IsA("Motor6D") and v.Parent.Name ~= "HumanoidRootPart" then
local Socket = Instance.new("BallSocketConstraint")
local a1 = Instance.new("Attachment")
local a2 = Instance.new("Attachment")
a1.Parent = v.Part0
a2.Parent = v.Part1
Socket.Parent = v.Parent
Socket.Attachment0 = a1
Socket.Attachment1 = a2
a1.CFrame = v.C0
a2.CFrame = v.C1
Socket.LimitsEnabled = true
Socket.TwistLimitsEnabled = true
v:Destroy()
end
end
char.Humanoid.BreakJointsOnDeath = false
char.Humanoid.RequiresNeck = false
end
function unrag(char)
for i,v in pairs(char:GetDescendants()) do
if v:IsA("BallSocketConstraint") then
v.UpperAngle = 0
v.TwistUpperAngle = 0
v.TwistLowerAngle = 0
local Joints = Instance.new("Motor6D",v.Parent)
Joints.Part0 = v.Attachment0.Parent
Joints.Part1 = v.Attachment1.Parent
Joints.C0 = v.Attachment0.CFrame
Joints.C1 = v.Attachment1.CFrame
v:Destroy()
end
end
end
game:GetService("ReplicatedStorage").Jump.OnServerEvent:Connect(function(ply)
if ply.Character and ply.Team == game:GetService("Teams").Survivor then
if ply.Character.Ragdoll.Value == true then
--game:GetService("ReplicatedStorage"):WaitForChild("Message"):FireClient(ply, "Sorry, jumping isn't available for ragdolls right now")
end
end
end)
game.Players.PlayerAdded:Connect(function(p)
if #game:GetService("Teams").Beast:GetPlayers() == 1 then
p.Team = game:GetService("Teams").Survivor
else
p.Team = game:GetService("Teams").Beast
end
p.CharacterAdded:Connect(function(c)
canthit[p] = nil
if p.Team == game:GetService("Teams").Beast then
local theBat = game:GetService("ServerStorage").Bat:Clone()
theBat.Parent = c
theBat.Equipped:Connect(function()
local h = c:FindFirstChild("Humanoid")
if h then
local a = h:FindFirstChild("Animator")
if a then
idlet = a:LoadAnimation(idle)
idlet:Play()
end
end
end)
theBat.Activated:Connect(function()
local h = c:FindFirstChild("Humanoid")
if h then
local a = h:FindFirstChild("Animator")
if a then
ant = a:LoadAnimation(an)
ant:Play()
theBat.Handle.Swoosh:Play()
local thinge = theBat.Handle.Touched:Connect(function(t)
local ply = game.Players:GetPlayerFromCharacter(t:FindFirstAncestorWhichIsA("Model"))
local ragd = ply.Character:FindFirstChild("Ragdoll")
if ragd then
if ragd.Value == true then
return
end
end
if ply and ply.Team == game:GetService("Teams").Survivor and ply ~= p and not canthit[ply] then
theBat.Handle.Hit:Play()
canthit[ply] = true
local char = ply.Character
ragd.Value = true
rag(char)
while not (char.Time.Value <= 0) do
takeOwnership(char)
char.Humanoid:ChangeState(Enum.HumanoidStateType.Physics)
wait(1)
char.Time.Value -= 1
char.HP.Value -= .5
end
if char.HP.Value <= 0 then
char.HP.Value = 0
game:GetService("ReplicatedStorage"):WaitForChild("Message"):FireAllClients(ply.Name.." has been captured!")
char.Humanoid.Health = 0
return
end
char.Time:Destroy()
char.MaxTime:Destroy()
char.Humanoid:ChangeState(Enum.HumanoidStateType.GettingUp)
unrag(char)
resetOwernship(char, ply)
ragd.Value = false
coroutine.wrap(function()
wait(5)
canthit[ply] = nil
end)()
end
end)
wait(.2)
thinge:Disconnect()
end
end
end)
theBat.Unequipped:Connect(function()
if idlet then
idlet:Stop()
end
end)
end
end)
end)
So, this is my horrible code for a little game I am making. It's kind of similar to Flee the Facility. In my game, the person who is the 'beast' should be able to drag a player when they are knocked out, but they can't. I tried adding a rope constraint to the ragdoll and the 'beast', but it just makes the beast have input lag and makes it's animations go weird.
I must note that the beast is a random player. I would try setting ownership of the ragdoll'd player to the grabber (which is the 'beast') but I need to set it to the server in order to change the state to Physics. I would use an event and a local script but I do not want this to be exploitable at all.
So, my question is: How do I attach a rope from a player to a ragdolled player without the non-ragdolled player lagging or having weird input lag and weird animations?
Like I said, I tried setting ownership but that wouldn't work as I need server ownership for setting the humanoid's state to physics. Just using takeOwnership() then ChangeState both on the same line wouldn't work so I had to put it in the while loop.
I'm working on a FFA gamemode in Garry's mod where players have to survive each other and a nextbot NPC hunting them. My override of the GM:PlayerDeath adequately indicates when the current round should be over, however after SpawnGhoulInitial() is called, any deaths cause the game to crash. Im doing all this round stuff in my roundsystem_2.lua file:
-- GLOBAL VALUES
roundActive = false
roundCountdownStartTime = 0
roundStartTime = 0
phase = 1
countDownLength = 5
initialGhoulDelay = 10
spawnWepDelay = 15
ghoulMadnessDelay = 20
roundLength = 30
DEFINE_BASECLASS( "gamemode_base" )
-- vector list dictionary of nexbot spawn locations per map, starting with initial spawn location
nextbot_spawns = {gm_backrooms_classic = {Vector(-7413, 318, 93), Vector(-4270, 4079, 93), Vector(-3279, -4620, 93), Vector(-11092, -4632, 93), Vector(-11086, 4316, 93)}}
function GM:UpdateTimer(time)
net.Start("round_timer")
net.WriteInt(time, 10)
net.Broadcast()
end
-- function used to spawn the first ghoul of the round
function SpawnGhoulInitial()
local name = RandomizeNextbot()
local Ent = ents.Create(name)
if( !IsValid(Ent)) then
print("entity is not valid.")
return
end
Ent:SetPos(nextbot_spawns.gm_backrooms_classic[1])
Ent:Spawn()
print("Spawned ghoul ".. name)
PrintMessage(4, "The Ghoul has spawned. SURVIVE!")
end
-- function called to spawn the next four ghouls to sweep the map and kill off straggling players
function SpawnGhoulMadness()
local name = RandomizeNextbot()
local Ent = ents.Create(name)
if( !IsValid(Ent)) then
print("entity is not valid.")
return
end
for i=2, 5 do
Ent:SetPos(nextbot_spawns.gm_backrooms_classic[i])
Ent:Spawn()
print("Spawned ghoul madness ghoul number ".. tostring(i))
end
PrintMessage(4, "The Ghoul has summoned some friends. RUN!")
end
-- function called to spawn in weapons for the players
function SpawnWeps()
for k, v in pairs(player.GetAll()) do
if (v:Alive()) then
v:Give("weapon_crowbar", false)
v:Give("weapon_pistol", false)
end
end
PrintMessage(4, "You've found supplies. Kill other survivors!")
end
function CountAlivePlayers()
local players = player.GetAll()
local alive = 0
for i = 1, #players do
local player = players[i]
if (player:Alive()) then
alive = alive + 1
end
end
return alive
end
-- function called to give us a random nextbot to spawn in
function RandomizeNextbot()
local names = {"table of nextbot names here, not posting them for stackoverflow because its embarrassing"}
local random_nextbot = math.random(#names)
return names[random_nextbot]
end
-- function called under the conditions that the round has ended, will start the next round.
function GM:EndRound(winner)
-- clear the map, clear sounds, print winner on screen, allow the respawning
if (winner == nil) then
PrintMessage(4, "Nobody Wins!")
else
PrintMessage(4, winner:Nick().." Wins!")
end
game.CleanUpMap(false, {})
RunConsoleCommand("stopsound")
timer.Remove("RoundTimer")
timer.Create("roundEndTimer", 5, 1, function()
for k, v in pairs(player.GetAll()) do
if (v:Alive()) then
v:StripWeapons()
v:KillSilent()
end
end
net.Start("round_active")
net.WriteBool(false)
net.Broadcast()
roundActive = false
end)
end
function GM:StartCountDownTimer(repetitions)
self:UpdateTimer(repetitions)
timer.Create("CountdownTimer", 1, repetitions, function()
repetitions = repetitions - 1
self:UpdateTimer(repetitions)
end)
end
function GM:StartRoundTimer(repetitions)
self:UpdateTimer(repetitions)
timer.Create("RoundTimer", 1, repetitions, function()
repetitions = repetitions - 1
self:UpdateTimer(repetitions)
if (repetitions <= 0) then
self:EndRound(nil)
end
end)
end
--overriding playerdeath function for best death results
function GM:PlayerDeath(victim, inflictor, attacker)
BaseClass.PlayerDeath(self, victim, inflictor, attacker)
local players = player.GetAll()
local lastAlive = NULL
for i = 1, #players do
local p = players[i]
if (p:Alive()) then
lastAlive = p
end
end
if (CountAlivePlayers() <= 1 && roundActive) then
self:EndRound(lastAlive)
end
end
-- hook for starting the round on player spawn if there are enough players.
-- countdown for round start will restart if a new player spawns in during the countdown.
function GM:DoRoundSpawn()
--every time player joins reset the countdown
if (CountAlivePlayers() > 1) then
roundCountdownStartTime = CurTime()
self:StartCountDownTimer(countDownLength)
end
end
-- The main timing of the round. Checks against curtime to find when
-- specific events in the round need to happen.
function GM:Think()
-- if there is more than one player, and the time elapsed is a full 30 sec, start round
if (CountAlivePlayers() > 1 && roundActive == false) then
if ((roundCountdownStartTime + countDownLength) < CurTime()) then
roundStartTime = CurTime()
roundActive = true
phase = 1
net.Start("round_active")
net.WriteBool(true)
net.Broadcast()
timer.Remove("CountdownTimer")
self:StartRoundTimer(roundLength)
PrintMessage(4, "The Round has begun. Last player to survive wins!")
end
end
-- if the round is active, do this stuff
if (roundActive == true) then
-- if its been 10 sec since round start, spawn the ghoul
if ((roundStartTime + initialGhoulDelay) < CurTime() && phase == 1) then
SpawnGhoulInitial()
phase = 2
end
-- if its been 30 sec since round start, spawn in weapons for players
if((roundStartTime + spawnWepDelay) < CurTime() && phase == 2) then
SpawnWeps()
phase = 3
end
-- if its been 120 sec (2 minutes) since round start, begin the ghoul madness
if((roundStartTime + ghoulMadnessDelay) < CurTime() && phase == 3) then
SpawnGhoulMadness()
phase = 4
end
end
end
However, I do call my GM:DoRoundSpawn function in my init.lua file:
AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")
include("shared.lua")
include("roundsystem_2.lua")
util.AddNetworkString("round_timer")
util.AddNetworkString("round_active")
roundActive = false
playerSpawns = {--table of spawnpoints, too much to paste for stackoverflow}
function GM:PlayerSpawn(ply)
ply:SetGravity(.85)
ply:SetMaxHealth(100)
ply:SetupHands()
ply:SetWalkSpeed(250)
ply:SetRunSpeed(450)
ply:SetModel("models/player/Kleiner.mdl")
local random_spawn = math.random(#playerSpawns)
ply:SetPos(playerSpawns[random_spawn])
--this section basically will make it so if someone spawns
--in while the round is active, they will stay dead until
--the round is no longer active
print("Player: " .. ply:GetName() .. " has spawned!")
if (roundActive == true) then
ply:KillSilent()
return
end
self:DoRoundSpawn()
end
function GM:PlayerDeathThink(ply)
if (roundActive == false) then
ply:Spawn()
return true
else
return false
end
end
I've combed through both of these files a couple times over and I cant for the life of me figure out why deaths don't break the game until after the nextbot spawn happens. Is there something here I'm missing?
I'm making a roblox script that will teleport the player and make a "Beam" animation, but when i activate the script by walking on the tele pad, i get this error
Workspace.RedPedastal.Script:17: attempt to index function with 'Transparency'
Code:
local Teleport = "BeachHill"
local Beam = workspace.Beam
function Beam() #Just here incase of need
for i = 0, 100, 1 do
Beam.Transparency = 1 - i/100
Beam.Size = Vector3.new(2048, 1.8 + i/10, 1.8 + i/10)
print(Beam.Transparency, Beam.Size)
end
end
function Touch (hit)
if script.Parent.Locked == false and script.Parent.Parent : FindFirstChild(Teleport).Locked == false then script.Parent.Locked = true script.Parent.Parent:FindFirstChild(Teleport).Locked=true
local Pos = script.Parent.Parent:FindFirstChild(Teleport)
hit.parent:moveTo(Pos.Position)
for i = 0, 100, 1 do
Beam.Transparency = Beam.Transparency - 0.01 #<- Error
Beam.Size = Vector3.new(2048, 1.8 + i/10, 1.8 + i/10)
print(Beam.Transparency, Beam.Size)
end
wait(1)
script.Parent.Locked = false
script.Parent.Parent:FindFirstChild(Teleport).Locked = false
end
end
script.Parent.Touched:connect(Touch)
Functions and variables in Lua live in the same namespace, so you can't have a function and a variable both called Beam in the same place.
I've been working on editing/recreating a Rainmeter skin (sorry if these codes are a bit messy) and I can't figure out how to restart the countdown (ie. Christmas) every year. I basically want this countdown clock to loop, if that makes sense... I've attached the .ini code and the .lua code.
[Rainmeter]
Update=1000
[Variables]
Color=0,0,0
FontName=Franklin Gothic Heavy
FontHeight=46
;Set these variables so change the day the count down is counting to, and to change what the text says when it hits that time!;
toYear=2021
toMonth=12
toDay=25
toHour=24
toMinute=0
toSecond=0
ReleaseText="0"
;Measures;
[MeasureScript]
Measure=script
ScriptFile=#CURRENTPATH#countdown.lua
TableName=Countdown
year=#toYear#
month=#toMonth#
day=#toDay#
hour=#toHour#
min=#toMinute#
sec=#toSecond#
fintext=#ReleaseText#
;Meters;
[MeterLogo]
Meter=Image
ImageName=Xmas.png
SolidColor=0,0,0,1
X=R
Y=0
[MeterString]
Meter=string
MeasureName=MeasureScript
X=555
Y=38
H=1
FontFace=#FontName#
FontSize=#FontHeight#
FontColor=#Color#
StringStyle=BOLD
Text=%1
AntiAlias=1
StringAlign=Center
Group=Static
[MeterTextTop]
[MeterTextMiddle]
Meter=String
Text=Insert text
X=308
Y=131
FontColor=0,0,0
StringStyle=BOLD
FontSize=20
FontFace=Franklin Gothic Heavy
AntiAlias=1
StringAlign=Center
[MeterTextBottom]
Meter=String
Text= Hello!
X=320
Y=190
FontColor=255,255,255
StringStyle=BOLD
FontSize=20
FontFace=Franklin Gothic Heavy
AntiAlias=1
StringAlign=Center
And here is the .lua code
PROPERTIES = {year=0, month=0, day=0, hour=0, min=0, sec=0, fintext=""}
function Initialize()
RELEASEDATE = {}
setmetatable(RELEASEDATE, getmetatable(PROPERTIES))
for k,v in pairs(PROPERTIES) do
if k ~= fintext then
RELEASEDATE[k] = v
end
end
RELEASEDATE.isdst = true
RELEASETEXT = PROPERTIES.fintext or ""
end
function GetTimeLeft()
local dif = os.time(RELEASEDATE) - os.time()
local timeleft = {
[1] = math.floor(dif/60/60/24), --day
}
local text = {}
for i=1, #timeleft do
if i == 1 then
if timeleft[i] > 0 then
table.insert(text,timeleft[i])
end
else
table.insert(text,timeleft[i])
end
end
if dif <= 0 then
text = RELEASETEXT
else
text = table.concat(text,":")
end
return tostring(text)
end
function Update()
end
function GetStringValue()
return GetTimeLeft()
end
local function centerText(text)
local x,y = term.getSize()
local x2,y2 = term.getCursorPos()
term.setCursorPos(math.round((x / 2) - (text:len() / 2)), y2)
write(text)
end
You could recalculate if you are a day over:
-- this is just an example for testing
PROPERTIES = {year=2021, month=9, day=23, fintext="Its christmas!"}
function Initialize()
RELEASEDATE = {}
setmetatable(RELEASEDATE, getmetatable(PROPERTIES))
for k,v in pairs(PROPERTIES) do
if k ~= fintext then
RELEASEDATE[k] = v
end
end
RELEASEDATE.isdst = true
RELEASEDATE.year = os.date("%Y")
RELEASETEXT = PROPERTIES.fintext or ""
end
function GetTimeLeft()
local dif = os.time(RELEASEDATE) - os.time{year=os.date("%Y"),month=os.date("%m"), day=os.date("%d")}
if dif < 0 then
RELEASEDATE.year = os.date("%Y")+1
return GetTimeLeft()
end
local timeleft = {
[1] = math.floor(dif/60/60/24), --day
}
local text = {}
for i=1, #timeleft do
if i == 1 then
if timeleft[i] > 0 then
table.insert(text,timeleft[i])
end
else
table.insert(text,timeleft[i])
end
end
if dif <= 0 then
text = RELEASETEXT
else
text = table.concat(text,":")
end
return tostring(text)
end
-- this is just for testing
Initialize()
for i=1, 5 do
print(GetTimeLeft() .. " day left till christmas")
end
The titles explains it all, I'm trying to make a roblox npc walk around the maze normally, then when it sees the player, it starts running after the player to kill it, I have the NPC, I have the killing part, I just need the code for the NPC walking around normally and the code for the NPC detecting the player then running after them. Thanks! :D
edited: heres my code
debugMode = false
targetNPCs = false
--
h = script.Parent.Parent:WaitForChild("Humanoid")
pathService = game:GetService("PathfindingService")
targetV = script.Parent:WaitForChild("Target")
function closestTargetAndPath()
local humanoids = {}
if targetNPCs then
local function recurse(o)
for _,obj in pairs(o:GetChildren()) do
if obj:IsA("Model") then
if obj:findFirstChild("Humanoid") and obj:findFirstChild("Torso") and obj.Humanoid ~= h and obj.Humanoid.Health > 0 and not obj:findFirstChild("ForceField") then
table.insert(humanoids,obj.Humanoid)
end
end
recurse(obj)
end
end
recurse(workspace)
else
for _,v in pairs(game.Players:GetPlayers()) do
if v.Character and v.Character:findFirstChild("HumanoidRootPart") and v.Character:findFirstChild("Humanoid") and v.Character.Humanoid.Health > 0 and not v:findFirstChild("ForceField") then
table.insert(humanoids,v.Character.Humanoid)
end
end
end
local closest,path,dist
for _,humanoid in pairs(humanoids) do
local myPath = pathService:ComputeRawPathAsync(h.Torso.Position,humanoid.Torso.Position,500)
if myPath.Status ~= Enum.PathStatus.FailFinishNotEmpty then
-- Now that we have a successful path, we need to figure out how far we need to actually travel to reach this point.
local myDist = 0
local previous = h.Torso.Position
for _,point in pairs(myPath:GetPointCoordinates()) do
myDist = myDist + (point-previous).magnitude
previous = point
end
if not dist or myDist < dist then -- if true, this is the closest path so far.
closest = humanoid
path = myPath
dist = myDist
end
end
end
return closest,path
end
function goToPos(loc)
h:MoveTo(loc)
local distance = (loc-h.Torso.Position).magnitude
local start = tick()
while distance > 4 do
if tick()-start > distance/h.WalkSpeed then -- Something may have gone wrong. Just break.
break
end
distance = (loc-h.Torso.Position).magnitude
wait()
end
end
while wait() do
local target,path = closestTargetAndPath()
local didBreak = false
local targetStart
if target and h.Torso then
targetV.Value = target
targetStart = target.Torso.Position
roaming = false
local previous = h.Torso.Position
local points = path:GetPointCoordinates()
local s = #points > 1 and 2 or 1
for i = s,#points do
local point = points[i]
if didBreak then
break
end
if target and target.Torso and target.Health > 0 then
if (target.Torso.Position-targetStart).magnitude < 1.5 then
local pos = previous:lerp(point,.5)
local moveDir = ((pos - h.Torso.Position).unit * 2)
goToPos(previous:lerp(point,.5))
previous = point
end
else
didBreak = true
break
end
end
else
targetV.Value = nil
end
if not didBreak and targetStart then
goToPos(targetStart)
end
end
I won't provide any code as you have not shown any efforts to solve that problem.
Enter "roblox npc" into www.google.com. Click the second hit:
https://developer.roblox.com/en-us/articles/Moving-NPCs-Between-Points
This covers the walking around part and also links this
https://developer.roblox.com/en-us/articles/Pathfinding
which covers more complex movements.
The seeing part is done with ray casting
https://developer.roblox.com/en-us/articles/Raycasting
If you can cast a ray from the NPC to your player calculate a path to that player and attack.