Attempt to index nil with "Touched"? - lua

Here's the deal, I'm incorporating a teleportation system with a tool on Roblox Studio. However, this error has got me stumped. I will provide the code and other useful sources below and describe the issue after.
local tool = script.Parent.Parent
local debounce = false
local afterTeleport = false
local plrName = game.Players.LocalPlayer.Name
local char = workspace:FindFirstChild(plrName)
tool.Activated:connect(function(m)
if debounce == false then
local hum = game.Players.LocalPlayer.Character.Humanoid
local anim_feet = hum:LoadAnimation(script.Parent.Animation)
local current = anim_feet
local bodyVelocity = tool.Handle.LocalScript.BodyVelocity:Clone()
debounce = true
current:Play()
wait(1.1)
local gui = script.Parent.TransitionGui:Clone()
-- Properties for UI
gui.Parent = game.Players.LocalPlayer.PlayerGui
-- Transition
gui.Frame.BackgroundTransparency = 1
wait(0.02)
gui.Frame.BackgroundTransparency = 0.8
wait(0.02)
gui.Frame.BackgroundTransparency = 0.6
wait(0.02)
gui.Frame.BackgroundTransparency = 0.4
wait(0.02)
gui.Frame.BackgroundTransparency = 0.2
wait(0.02)
gui.Frame.BackgroundTransparency = 0
-- Teleport Player Into Sky Above Them
hum.Parent.HumanoidRootPart.CFrame =
CFrame.new(Vector3.new(hum.Parent.HumanoidRootPart.CFrame.X, hum.Parent.HumanoidRootPart.CFrame.Y + 700, hum.Parent.HumanoidRootPart.CFrame.Z))
bodyVelocity.Parent = hum.Parent.HumanoidRootPart
afterTeleport = true
wait(0.02)
gui.Frame.BackgroundTransparency = 0.2
wait(0.02)
gui.Frame.BackgroundTransparency = 0.4
wait(0.02)
gui.Frame.BackgroundTransparency = 0.6
wait(0.02)
gui.Frame.BackgroundTransparency = 0.8
wait(0.02)
gui.Frame.BackgroundTransparency = 1
wait(0.02)
end
end)
char.Touched:Connect(function(interact)
local hum = game.Players.LocalPlayer.Character.Humanoid
if afterTeleport == true then
if interact:IsA("Terrain") then
if hum.Parent.HumanoidRootPart:FindFirstChild("BodyVelocity") then
hum.Parent.HumanoidRootPart.BodyVelocity:Destroy()
end
elseif interact:IsA("Part") then
if hum.Parent.HumanoidRootPart:FindFirstChild("BodyVelocity") then
hum.Parent.HumanoidRootPart.BodyVelocity:Destroy()
end
elseif interact:IsA("MeshPart") then
if hum.Parent.HumanoidRootPart:FindFirstChild("BodyVelocity") then
hum.Parent.HumanoidRootPart.BodyVelocity:Destroy()
end
end
end
end)
To me, this seems correct. But the issue is whenever I play-test the code, the output displays an error that I don't understand. The output error is:
16:35:59.453 Players.BigBetterBuilder.Backpack.Sky Port.Handle.LocalScript:58: attempt to index nil with 'Touched' 
My goal for this tool is that when it is activated, it will teleport you into the sky 700 studs above the players' current position. It will add a BodyVelocity to the player's HumanoidRootPart causing the player to slow down the decent speed, and when the player touched the ground. It should remove the BodyVelocity allowing the player to roam around without having weird gravity.
But the function that's supposed to detect when a player touches the ground isn't working. And I, unfortunately, can't seem to solve the problem.

You've got a timing issue in your code. At the top of your script, you are trying to access the player's character model, but when this script runs, that character might not have been loaded into the workspace yet. So, when you call char.Touched:Connect(...) it throws an error because char is null.
But you've got a different problem as well. The player's character is a Model, and Model's don't have a Touched event like Parts do. So in order to use the Touched event, you either need to attach it to platforms that a character might touch, or to the Parts inside the character model.
So try moving the char.Touched connection inside a callback that fires once the player and character have properly loaded into the game, and attach the Touched connection instead to the different Parts in the character model :
local player = game.Players.LocalPlayer
player.CharacterAdded:Connect(function(character)
-- create a helper function for checking if we're touching the ground
local function checkTouchedGround(interact)
if not afterTeleport then
return
end
local shouldRemoveVelocity = false
if interact:IsA("Terrain") then
shouldRemoveVelocity = true
elseif interact:IsA("Part") then
shouldRemoveVelocity = true
elseif interact:IsA("MeshPart") then
shouldRemoveVelocity = true
end
if shouldRemoveVelocity then
local bv = character.HumanoidRootPart:FindFirstChild("BodyVelocity", 0.1)
if bv then
bv:Destroy()
end
end
end
-- listen for any time any player parts touch the ground
for _, child in ipairs(character:GetDescendants()) do
if child:isA("BasePart") then
child.Touched:Connect(checkTouchedGround)
end
end
end)

Related

How to aim a gun where the camera is pointing?

I have a fixed "Over-The-Shoulder" camera system, and i want to aim the gun where the camera is pointing at.
My best bet was to use the CFrame.Rotation, but it didn't work so well.
That's when i tried, getting the angles from the camera every time it changed, using: workspace.CurrentCamera.Change:Connect, and then applying the rotation vectors (Camera.CFrame.Rotation) to the entire of the player's body.
Then i've tried implementing Inverse Kinematics, to my code, but honestly, i could not stand copied code that i don't know how it works, so i scraped everything that i've copied.
Everything that i found about IK (Inverse Kinematics), was little to no useful, since no one really did what i want to achieve.
I don't know if it's achievable or not, but, if it helps, here is the state of my code, and an example image.
-- Script for controling the player camera.
-- Used for aiming in general.
-- Game global table
local globals = require(game.ReplicatedStorage.globalStates)
-- Normal services
local UserInputService, runService = game:GetService("UserInputService"), game:GetService("RunService")
local LocalPlayer = game:GetService("Players").LocalPlayer -- Get client's player
local Camera = game:GetService("Workspace").CurrentCamera -- Player's camera(?)
local TweenService = game:GetService("TweenService")
local PlayerMouse = LocalPlayer:GetMouse()
local DEF_FOV = 70
local AIM_FOV = 35
local AIM_SENS = 0.3
local AIM_DURR_TIME = 0.15
local xAngle, yAngle = 0, 0
local cameraOffset = Vector3.new(3.5, 0, 7.5)
local tweenIn = TweenService:Create(Camera, TweenInfo.new(AIM_DURR_TIME), {FieldOfView = AIM_FOV})
local tweenOut = TweenService:Create(Camera, TweenInfo.new(AIM_DURR_TIME), {FieldOfView = DEF_FOV})
-- TODO: Fix bug when player changes camera mode in menu
wait(1)
Camera.CameraType = Enum.CameraType.Scriptable -- Used for creating custom behavior
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter -- Lock the cursor
PlayerMouse.Icon = "rbxassetid://9095360160" -- Set dot crosshair
UserInputService.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
xAngle = xAngle - input.Delta.x * 0.4
yAngle = math.clamp(yAngle - input.Delta.y * 0.4, -80, 80)
end
end)
-- This will run every frame
runService.RenderStepped:Connect(function()
local Character = LocalPlayer.Character
local Root = LocalPlayer.Character:FindFirstChild("HumanoidRootPart")
if Character and Root then
local startCFrame = CFrame.new((Root.CFrame.p + Vector3.new(0,2,0))) * CFrame.Angles(0, math.rad(xAngle), 0) * CFrame.Angles(math.rad(yAngle), 0, 0)
local cameraCFrame = startCFrame + startCFrame:vectorToWorldSpace(Vector3.new(cameraOffset.X,cameraOffset.Y,cameraOffset.Z))
local cameraFocus = startCFrame + startCFrame:vectorToWorldSpace(Vector3.new(cameraOffset.X,cameraOffset.Y,-50000))
Camera.CFrame = CFrame.new(cameraCFrame.p ,cameraFocus.p)
end
end)
-- Only 'aim' if TOGGLE_MOUSE and TOGGLE_MOUSE are false
PlayerMouse.Button2Down:Connect(function()
if globals.TOGGLE_MOUSE == false and globals.IS_PLAYER_AIMING == false then
tweenIn:Play()
globals.IS_PLAYER_AIMING = true
else
print("Could not start player aim")
end
end)
PlayerMouse.Button2Up:Connect(function()
tweenOut:Play()
globals.IS_PLAYER_AIMING = false
end)

How to move multiple parts with attachments

I'm trying to make something where when you move your mouse, a part moves
When the mouse moves, I want this arm to move and have everything follow just using orientation (and stay at the center of certain parts)
This is my code for moving the parts
-- This is using remote events. This is local
game.ReplicatedStorage.JudgeCam2.OnClientEvent:Connect(function()
game.Workspace.CurrentCamera.CameraType = Enum.CameraType.Scriptable
wait(.1)
local TS = game:GetService("TweenService")
TS:Create(game.Workspace.CurrentCamera, TweenInfo.new(.75,Enum.EasingStyle.Linear,Enum.EasingDirection.Out,0,false,0), {CFrame = game.Workspace.CamParts.JudgeCam2.CFrame}):Play()
local GUN_ICON = "rbxasset://textures/GunCursor.png"
local GUN_RELOAD_ICON = "rbxasset://textures/GunWaitCursor.png"
mouse.Icon = GUN_ICON
local UIS = game:GetService("UserInputService")
local LastMousePos = nil -- Used to calculate how far mouse has moved
UIS.InputChanged:Connect(function(input, gameProcessedEvent)
local CurrentMousePos1 = Vector2.new(mouse.X,mouse.Y)
if gameProcessedEvent then return end
if input.UserInputType == Enum.UserInputType.MouseMovement then -- runs every time mouse is moved
if LastMousePos == nil then
game.ReplicatedStorage.MoveJudge2:FireServer(mouse,CurrentMousePos1,CurrentMousePos1)
else
game.ReplicatedStorage.MoveJudge2:FireServer(mouse,CurrentMousePos1,LastMousePos)
end
LastMousePos = CurrentMousePos1
end
end)
UIS.InputBegan:Connect(function(KeyCode)
if KeyCode.UserInputType == Enum.UserInputType.MouseButton1 and debouce2 == false then
debouce2 = true
mouse.Icon = GUN_RELOAD_ICON
wait(6)
debouce2 = false
end
end)
end)
-- This is the server
game.ReplicatedStorage.MoveJudge2.OnServerEvent:Connect(function(plr, mouse,CurrentMousePos,LastMousePos)
print("(" .. tostring(CurrentMousePos) .. " - " .. tostring(LastMousePos) .. ")/5")
local change = (CurrentMousePos - LastMousePos)/5 -- calculates distance mouse traveled (/5 to lower sensitivity)
game.Workspace.Arm2.CFrame = game.Workspace.Arm2.CFrame * CFrame.Angles(0,math.rad(change.X),-math.rad(change.Y))
end)
Is there any way I can do this with positions or attactments?

Pathfinder is making my NPC follow my oldest position only

I am trying to make a maze/horror game. I used an online template in the Roblox library as my enemy. I used pathfinder as you will see in the code below. It's finding me like it's supposed to, except it only goes for my LAST position. As you can see in the image below, it completely skipped me, went to my LAST position, then started chasing me. I don't know why it only goes for my last position, and not my current position.
local PathFindingS = game:GetService("PathfindingService")
local humanoid = script.Parent:WaitForChild("Humanoid")
local rootPart = script.Parent:WaitForChild("HumanoidRootPart")
local Players = game:GetService("Players")
game.Workspace.Fruity.Humanoid.WalkSpeed = 60
--To calculate the path
while wait() do
for i, player in pairs(game.Players:GetPlayers()) do
local character = game.Workspace:WaitForChild(player.Name)
local characterPos = character.PrimaryPart.Position
local path = PathFindingS:CreatePath()
path:ComputeAsync(rootPart.Position, characterPos)
game.Workspace.Fruity.HumanoidRootPart:SetNetworkOwner(nil)
local waypoints = path:GetWaypoints()
for i, waypoint in pairs(waypoints) do
local part = Instance.new("Part")
part.Shape = "Ball"
part.Material = "Neon"
part.Size = Vector3.new(0.6,0.6,0.6)
part.Position = waypoint.Position + Vector3.new(0,2,0)
part.Anchored = true
part.CanCollide = false
part.Parent = game.Workspace
humanoid:MoveTo(waypoint.Position)
humanoid.MoveToFinished:Wait()
end
end
end
Your NPC's pathfinding updates when you call path:ComputeAsync(rootPart.Position, characterPos). The reason it is not updating more frequently is that you are blocking the start of the next loop with the last line : humanoid.MoveToFinished:Wait()
Your code is telling the NPC that it must walk to every single point between every single player, which could take minutes at a time, before ever calculating the path again.
The way to fix this is to make it so that the path can be recalculated quickly and asynchronously. To do this, try something like this :
local PathfindingService = game:GetService("PathfindingService")
local PlayerService = game:GetService("Players")
local humanoid = script.Parent:WaitForChild("Humanoid")
local rootPart = script.Parent:WaitForChild("HumanoidRootPart")
-- create a place where pathfinding orbs can be created and destroyed quickly
local shouldDebugPath = true
local OrbFolder = Instance.new("Folder", game.Workspace)
OrbFolder.Name = "Debug - Orbs"
local OrbTemplate = Instance.new("Part")
OrbTemplate.Shape = Enum.PartType.Ball
OrbTemplate.Material = Enum.Material.Neon
OrbTemplate.Size = Vector3.new(0.6,0.6,0.6)
OrbTemplate.Anchored = true
OrbTemplate.CanCollide = false
local function createWaypoint(position)
local orb = OrbTemplate:Clone()
orb.Position = position + Vector3.new(0,2,0)
orb.Parent = OrbFolder
return orb
end
-- set up some navigation variables and set up a chain
-- so that when the humanoid arrives at a waypoint,
-- it automatically selects the next point.
local currentOrbIndex = 0
local currentPath = {} -- store pathfinding waypoints here
local function moveToNextWaypoint(previousPointReached)
currentOrbIndex += 1
if currentOrbIndex > #currentPath then
return
end
local nextWaypoint = currentPath[currentOrbIndex]
humanoid:MoveTo(nextWaypoint.Position)
end
humanoid.MoveToFinished:Connect(moveToNextWaypoint)
-- calculate and continually update the path
local pauseLength = 1.0 --seconds (lower this number to speed up refresh rate)
while wait(pauseLength) do
-- find the closest player's character model
local targetCharacter = nil
local closestDist = math.huge
local playerList = PlayerService:GetPlayers()
for i, player in pairs(playerList) do
-- check that the player's character has spawned in
local character = player.Character
if not character then
continue
end
-- do some quick maths and select which player is closest
local p1 = character.PrimaryPart.Position
local p2 = rootPart.Position
local d = math.abs((p1 - p2).Magnitude)
if d < closestDist then
closestDist = d
targetCharacter = character
end
end
-- check that there are actually players in the server
if targetCharacter == nil then
continue
end
-- compute the path to the character
local characterPos = targetCharacter.PrimaryPart.Position
local path = PathfindingService:CreatePath()
path:ComputeAsync(rootPart.Position, characterPos)
local waypoints = path:GetWaypoints()
-- debug : show the path to the player
if shouldDebugPath then
OrbFolder:ClearAllChildren()
for i, waypoint in pairs(waypoints) do
local position = waypoint.Position
createWaypoint(position)
end
end
-- start the player walking towards the nearest player
currentOrbIndex = 0
currentPath = waypoints
moveToNextWaypoint()
end
This solution recalculates the path to the nearest player every 1 second. That path is stored as a series of waypoints and it uses the Humanoid.MoveToFinished signal as the trigger to move to the next known waypoint. Whenever the path is recalculated, we simply start the process over.
With this system, the loop isn't blocked and the distance and path to the different players can be regularly recalculated. This allows the NPC to dynamically switch targets towards the closest player too.

Atempted to index nil error when trying to find the playergui component of a player

I'm trying to make a cutscene sorta thing for a dorr in roblox studio. My solution was to set up a collision detector on the door which would then make a gui template and set its parent to the playergui component.
I did this using the code
local afterIntoTransform = script.Parent.Parent.DoorUnion.Position.Z -6
local afterOutwardsTransform = script.Parent.Parent.DoorUnion.Position.Z + 6
local debounce = false
local function executeFadeSceneAndTpPlayer(player)
local fadeScene = Instance.new("ScreenGui")
local fadeSceneFrame = Instance.new("Frame")
fadeScene.Name = "fadeScene"
fadeSceneFrame.Name = "fadeFrame"
fadeSceneFrame.Size = UDim2.new(1,0,1,0)
fadeSceneFrame.Parent = fadeScene
fadeSceneFrame.BorderSizePixel = 0
fadeSceneFrame.BackgroundColor3 = Color3.new(1, 1, 1)
fadeSceneFrame.BackgroundTransparency = 1
print(game.Players:GetPlayerFromCharacter(player).Name)
fadeScene.Parent = game.Players:GetPlayerFromCharacter(player).PlayerGui
for i = 0, 20, 1 do
fadeSceneFrame.BackgroundTransparency -= 0.05
wait(0.01)
end
player.HumanoidRootPart.Position = Vector3.new(player.HumanoidRootPart.Position.X, player.HumanoidRootPart.Position.Y, afterOutwardsTransform)
for i = 0, 20, 1 do
fadeSceneFrame.BackgroundTransparency += 0.05
wait(0.01)
end
fadeScene:Destroy()
end
script.Parent.Touched:Connect(function(hit)
if not debounce then
debounce = true
executeFadeSceneAndTpPlayer(hit.Parent)
wait(0.2)
debounce = false
end
end)
It tells me: Attempted to index nil with name on line 15.
It works sometimes and sometimes doesnt but recently Ive noticed a trend that I can walk into the door then out again and then it breaks. I haven't coded in a while so I'm a little rusty but I hope I can get some help.
You are running into the same issue as this person : Roblox - attempt to index nil with 'leaderstats'
You are not accounting for the fact that the Touched event fires for every single part that touches it, and some of those parts might not belong to a player.
You can protect against this error by making sure that the object belongs to a player before you call executeFadeSceneAndTpPlayer()
script.Parent.Touched:Connect(function(hit)
local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
if not plr then
return
end
if not debounce then
debounce = true
executeFadeSceneAndTpPlayer(hit.Parent)
wait(0.2)
debounce = false
end
end)

How can I implement a debounce in this code

My first thought was to ask on Roblox devforum but since imo they got a really messed up admission system, I might as well ask it here.
I've got a tool that shoots a block (wedge) to wherever the mouse is pointing when clicked. It also casts a ray and the block itself sets the health of any humanoid that makes contact with it to 0. But I have got no idea on how to actually implement a cooldown on the gun so you can't just literally spam blocks that kill anything that touches them arround. I think implementing a debounce here is the best option, but I got stuck with that since day 1 and I have no idea how to write it down correctly
I already tried with most of the things that I thought of after visiting this page Roblox dev page about Debounce, also read through some articles that had similar issues in the dev forum, but I can just spam blocks arround whatever I do.
The tool has just two parts (one being the handle) a localscript to wield the parts together, a localscript to catch the mouse position when clicked, two remote events to pass the info from the localscript to the server script and the following server script
local tool = script.Parent
local clickEvent = tool.ClickEvent
local clickEventConnection
local Players = game:GetService("Players")
local Teams = game:GetService("Teams")
--Function that creates the part with a touched listener that kills any humanoid that comes into contact with said block
local function createPart(location)
local part = Instance.new("WedgePart")
part.CFrame = location
part.Parent = workspace
part.BrickColor = BrickColor.new("Black")
part.Touched:connect(function(hit)
if hit.Parent then
local hum = hit.Parent:FindFirstChild("Humanoid")
if hum then
hum.Health = 0
end
end
end)
game:GetService("Debris"):AddItem(part, 2)
end
--With the information on the click position of the localscript, this function creates a ray and a beam that accompanies the block, as well as executing the createpart() function on said location
local function onClick(player, clickLocation, ignore)
createPart(clickLocation)
local ray = Ray.new(
tool.Handle.CFrame.p,
(clickLocation.p - tool.Handle.CFrame.p).unit * 500
)
local hit, position, normal = workspace:FindPartOnRay(ray, player.Character, ignore)
local beam = Instance.new("Part", workspace)
if player.Team == Teams["Blue Team"] then
beam.BrickColor = BrickColor.new("Bright blue")
elseif player.Team == Teams["Red Team"] then
beam.BrickColor = BrickColor.new("Bright red")
else
beam.BrickColor = BrickColor.new("Ghost grey")
end
beam.FormFactor = "Custom"
beam.Material = "Neon"
beam.Transparency = 0.25
beam.Anchored = true
beam.Locked = true
beam.CanCollide = false
local distance = (tool.Handle.CFrame.p - position).magnitude
beam.Size = Vector3.new(0.3, 0.3, distance)
beam.CFrame = CFrame.new(tool.Handle.CFrame.p, position) * CFrame.new(0, 0, -distance / 2)
game:GetService("Debris"):AddItem(beam, 1)
end
--subscribing onclick() when equiping the weapon and unsubscribig when unequipping it
local function onEquip()
clickEventConnection = clickEvent.OnServerEvent:connect(onClick)
end
local function onUnequip()
clickEventConnection:disconnect()
end
tool.Equipped:connect(onEquip)
tool.Unequipped:connect(onUnequip)
I just wanted to make a 'cooldown' so a block can be fired each 3 seconds. As is, you can just spam as much as you like to
A simple way to debounce the clicks is to use a variable to decide whether to quickly escape from a function.
You can modify your onClick function so it does not execute if a cooldown is still in place :
-- make a cooldown tracker
local isGunOnCooldown = false
local cooldownTime = 3.0 --seconds
local function onClick(player, clickLocation, ignore)
-- debounce any spammed clicks
if isGunOnCooldown then
return
end
-- put the gun on cooldown
isGunOnCooldown = true
-- fire a bullet
createPart(clickLocation)
local ray = Ray.new(
tool.Handle.CFrame.p,
(clickLocation.p - tool.Handle.CFrame.p).unit * 500)
local hit, position, normal = workspace:FindPartOnRay(ray, player.Character, ignore)
local beam = Instance.new("Part", workspace)
if player.Team == Teams["Blue Team"] then
beam.BrickColor = BrickColor.new("Bright blue")
elseif player.Team == Teams["Red Team"] then
beam.BrickColor = BrickColor.new("Bright red")
else
beam.BrickColor = BrickColor.new("Ghost grey")
end
beam.FormFactor = "Custom"
beam.Material = "Neon"
beam.Transparency = 0.25
beam.Anchored = true
beam.Locked = true
beam.CanCollide = false
local distance = (tool.Handle.CFrame.p - position).magnitude
beam.Size = Vector3.new(0.3, 0.3, distance)
beam.CFrame = CFrame.new(tool.Handle.CFrame.p, position) * CFrame.new(0, 0, -distance / 2)
game:GetService("Debris"):AddItem(beam, 1)
-- start the gun's cooldown and reset it
spawn(function()
wait(cooldown)
isGunOnCooldown = false
end)
end

Resources