How would I make a player invisible only to certain players? - lua

Basically what I've done so far is add a boolValue to every player, and if that boolValue is set to true, they can see invisible people, if it is false, they can't. I am clueless about the code currently so I have no idea how to begin. I'm also changing the value of the Bool Value through a tool.

The key to making one player invisible only to certain players is the understanding that any changes made to the world in a LocalScript are only visible to that player. They are not replicated between other clients. So, if we can set up a system where LocalScripts can be told to turn a specific player invisible, that player will vanish from their screen and their screen only.
So if we had that system, Player Two could say, "Hey game server, could you let the people know to turn me invisible!". That message would go up to the server, the server would send it out to a bunch of different clients, and each client would make the change locally using their LocalScripts, and the player would effectively turn invisible on their screen.
So even though Player Two looks to be invisible to Players One and Three, to the game server, nothing is different or out of the ordinary, the changes are localized entirely to each player's version of the world.
One way to do this is to use a setup like this :
A Tool in StarterPack to allow players to control their visibility
A BoolValue inside that tool to hold onto the visiblity state
A LocalScript to control the tool's logic
A RemoteEvent in ReplicatedStorage
A Script in ServerScriptService to handle the event routing
In the LocalScript, you would have something like this :
local tool = script.Parent
local IsHidden = tool:WaitForChild("IsHidden", 5)
local TogglePlayerVisible = game.ReplicatedStorage.TogglePlayerVisible
local localPlayer = game.Players.LocalPlayer
-- debug
tool.Name = "Turn Invisible"
tool.RequiresHandle = false
IsHidden.Value = false
-- 1) When a player activates the tool, flip the BoolValue
tool.Activated:Connect(function()
IsHidden.Value = not IsHidden.Value
tool.Name = IsHidden.Value and "Become Visible" or "Turn Invisible"
end)
-- 2) When the BoolValue changes, tell the server about it
IsHidden.Changed:Connect(function(updatedValue)
TogglePlayerVisible:FireServer(updatedValue)
end)
-- 4) When the server tells us a player has used the tool, make that player invisible locally
TogglePlayerVisible.OnClientEvent:Connect(function(player, isHidden)
local invisible = 1.0
local visible = 0.0
-- if we are the one who sent it, only make us a little invisible
if (player.Name == localPlayer.Name) then
invisible = 0.7
end
-- loop over all of the parts in a player's Character Model and hide them
for _, part in ipairs(player.Character:GetDescendants()) do
local shouldTogglePart = true
-- handle exceptions
if not (part:IsA("BasePart") or part:IsA("Decal")) then
shouldTogglePart = false
elseif part.Name == "HumanoidRootPart" then
shouldTogglePart = false
end
-- hide or show all the parts and decals
if shouldTogglePart then
part.Transparency = isHidden and invisible or visible
end
end
end)
And in the Script, you would have something like this :
local TogglePlayerVisible = game.ReplicatedStorage.TogglePlayerVisible
-- 3) whenever a player triggers this event, send it out to all players
TogglePlayerVisible.OnServerEvent:Connect(function(player, isVisible)
TogglePlayerVisible:FireAllClients(player, isVisible)
end)
If you would like only specific players to not see a player, then you can modify step 3 so that instead of using TogglePlayerVisible:FireAllClients, you would specifically choose which players to send the message to using FireClient.

Related

Roblox Lua: Issue defining a value in a player's leaderstats folder (no error message)

I have a system in my game where when a player steps on a checkpoint, a Stage value in the leaderboard will go up, and then that value is saved to that player's userId.
I want there to be a button that resets that value back to 0, and this is the code I tried for it.
local textButton = script.Parent
local Players = game:GetService("Players")
textButton.MouseButton1Up:Connect(function()
local player = game.Players.LocalPlayer
local stage = Players:WaitForChild(player):WaitForChild("leaderstats").Stage
if stage.Value > 1 then
stage.Value = 1
end
end)
I've tried debugging it by putting print commands in different places to see where the code gets, and when I try to run print(stage) it does not show anything.
LocalScripts cannot change the value on the server. To change a value once a button is clicked, RemoteEvents should be used.
There is a page documenting about them here: https://create.roblox.com/docs/scripting/networking/remote-events-and-functions
To use a RemoteEvent, first you need to put it in a place that is replicated across the server and the client. A good example of this would be to put it in ReplicatedStorage. After that, you need to set up a script that listens for the .OnServerEvent event of the RemoteEvent. To do this, you need to connect a function. As seen in your script, you already know how to do this. So I will get straight to the point.
On the server, set up a script that listens for .OnServerEvent. Next, on the client, edit your script to fire the client instead of trying to set the value. Go back to the server script, and set the value there. The event also passes the Player object, so you can utilize that to actually reset the value.
TLDR;
LocalScript inside of your TextButton:
local textButton = script.Parent
local Players = game:GetService("Players")
local RemoteEvent = 'RemoteEvent location'
textButton.MouseButton1Up:Connect(function()
RemoteEvent:FireServer()
end)
Server Script in anywhere, preferably ServerScriptService:
RemoteEvent.OnServerEvent:Connect(function(player)
local Players = game:GetService("Players")
local stage = Players:WaitForChild(player):WaitForChild("leaderstats").Stage
if stage.Value > 1 then
stage.Value = 1
end
end)
Good luck!

LuaU script (Roblox), how can I make a Key get pressed with a script

An example would have been like
local E = game:GetService('UserInputService').SetKeyDown(Enum.KeyCode.E)
but it doesnt work ofcourse because i cant jsut make my game press E by itself with this thing, so it requieres soemthing longer and if you find the solution can you also do one where it makes it pressed down?
The input can only be registered on the client, therefore you will have to code in a LocalScript. There are 2 services which are used to get the player's input:-
UserInputService
This example shows how to use the UserInputService in getting the player's LeftMouseButton input.
local UserInputService = game:GetService("UserInputService")
local function onInputBegan(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
print("The left mouse button has been pressed!")
end
end
UserInputService.InputBegan:Connect(onInputBegan)
ContextActionService
This example properly shows how to use ContextActionService in binding user input to a contextual action. The context is the tool being equipped; the action is reloading some weapon.
local ContextActionService = game:GetService("ContextActionService")
local ACTION_RELOAD = "Reload"
local tool = script.Parent
local function handleAction(actionName, inputState, inputObject)
if actionName == ACTION_RELOAD and inputState == Enum.UserInputState.Begin then
print("Reloading!")
end
end
tool.Equipped:Connect(function ()
ContextActionService:BindAction(ACTION_RELOAD, handleAction, true, Enum.KeyCode.R)
end)
You should take a look at the Wiki pages.
It sounds like you want to write a script to press the E key, but that isn't possible.
You can bind actions to keypresses, just like in the examples Giant427 provided, and you can also manually call the functions that are bound to those actions, but you cannot write a script to fire keyboard inputs.

StarterGui elements still appearing when set to invisible

I'm trying to use the PlayerAdded to make a screen gui disappear and make another one appear. It makes both guis appear though. I also am trying to make the player not spawn in when they join the game and that doesn't work either.
game.Player.CharacterAutoLoads = false
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
game.StarterGui.StartScreenFrame.Visible = true
game.StarterGui.PlayGui.Visible = false
end)
Take a look at the documentation for StarterGui.
When a Players’ character respawns, the contents of their PlayerGui is emptied. Children of the StarterGui are then copied (along with their descendants) into the
PlayerGui.
Stuff in the StarterGui acts as a template. The actual labels that show up on a player's screen are found in the player's PlayerGui. Your code is modifying the template, not the elements that are cloned to the PlayerGui.
Normally, to fix your problem, you would just need the correct paths to the UI elements. But you have a complicating factor, you've specified that game.Players.CharacterAutoLoads = false. This means that stuff in the StarterGui doesn't get cloned into the PlayerGui until the player's character spawns.
So I would recommend a few things :
Move your UI Elements into ReplicatedStorage, this will prevent UI element duplication when you eventually tell your character to spawn into the world.
Clone the elements into each player's UI manually. This will make the UI elements show up.
Configure the cloned elements. This way you can script the UI behaviors yourself.
And then update your Script like this :
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- get a reference to the UI templates
local startScreen = ReplicatedStorage.StartScreenFrame
local playScreen = ReplicatedStorage.PlayGui
-- disable character's auto loading
Players.CharacterAutoLoads = false
-- listen for when players first join the game
Players.PlayerAdded:Connect(function(player)
-- add the UI Elements to the PlayerGui
local startGui = startScreen:Clone()
startGui.Visible = true
startGui.Parent = player.PlayerGui
local playGui = playScreen:Clone()
playGui.Visible = false -- enable this later
playGui.Parent = player.PlayerGui
end)
Be aware that because you are manually placing UI Elements into the PlayerGui, you should be careful that when a Player dies and respawns, their PlayerGui will be cleared. You can avoid this by setting the ResetOnSpawn property to false on each ScreenGui.

When player interacts with proximity prompt it checks if the player has a tool, if not then it does a function -ROBLOX STUDIO

I'm making a gui that shows up when a player interacts with a proximity prompt, but, i want the script to check if the player has the tool in his inventory. If it has the tool then do nothing (don'
t show the gui), if it doesn't have the tool then fire an event. I tried making it but this error keeps showing up Workspace.Part.Script:6: attempt to index nil with 'Backpack'
Here's the script:
debounce = true
script.Parent.ProximityPrompt.Triggered:Connect(function(player)
if debounce then
debounce = false
local noob = game.Players:GetPlayerFromCharacter(player.Parent)
local Tool = noob.Backpack:FindFirstChild("Gunball")
if Tool == nil then
game.ReplicatedStorage.RemoteEvent:FireClient(player)
debounce = true
end
end
end)
Here's the gui script (local), even if i don't really think that is usefull..:
game.ReplicatedStorage.RemoteEvent.OnClientEvent:Connect(function()
script.Parent.Visible = true
end)
Workspace.Part.Script:6: attempt to index nil with 'Backpack'
local Tool = noob.Backpack:FindFirstChild("Gunball")
Here noob is nil.
So in local noob = game.Players:GetPlayerFromCharacter(player.Parent) game.Players:GetPlayerFromCharacter(player.Parent) returns nil.
According to the Roblox documentation
This function returns the Player associated with the given
Player.Character, or nil if one cannot be found. It is equivalent to
the following function:
local function getPlayerFromCharacter(character)
for _, player in pairs(game:GetService("Players"):GetPlayers()) do
if player.Character == character then
return player
end
end
end
So it seems that the Parent of player, player.Parent is not a Character associated with any player.
Why should a player property like Character be a parent of a player? I'm no Roblox expert but that doesn't seem to make any sense to me.
If you want to check wether the player who triggered the ProximitPrompt has some item, why not work with player? I mean that's the player who triggered it. So check its backpack, not some parent character stuff.

Remote event problems

I'm making a game using ROBLOX about the tank battle system of Dragon Quest Heroes Rocket Slime (although not using 100% GUI's)
Since I didn't know how to make ammo become locked onto a path in normal ROBLOX, I decided to use a gui to show the ammo "firing"
To do this, I have a remote event that fires a function inside the main script of the GUI system when ammo is loaded
cannon.Touched:connect(function(v)
if fireable[v.Name] and v.Parent == workspace then
event:FireAllClients("Left",v.Name)
v:Destroy()
end
end)
Then, the GUI gets the correct sprite for the ammo loaded and fires it out of the right tank (the first argument in the FireAllClients part)
This is one of the two if statements for firing ammo (the other one is literally the same except that it's for the right side tank)
local tank = tankFiring == "Left" and tank1 or tankFiring == "Right" and tank2
if tank == tank1 then
print("yo!")
script.Fire:Play()
local ammoFrame = sp.Ammo:Clone()
ammoFrame.Parent = tank
ammoFrame.Visible = true
ammoFrame.Position = UDim2.new(0,120,0,68)
playAnimation("Cannon Fire",UDim2.new(0,120,0,68-25),tank.Frame)
ammoFrame.Image = ammoTypes[type]["img"]
ammoFrame.Size = ammoTypes[type]["Size"]
repeat
wait(.1)
ammoFrame.Rotation = ammoTypes[type]["Rotatable"] == true and ammoFrame.Rotation + 15 or 0
ammoFrame.Position = ammoFrame.Position + UDim2.new(0,1,0,0)
until
tank2:FindFirstChild("Ammo") and isTouching(ammoFrame,tank2:GetChildren()[3]) or isTouching(ammoFrame,tank2) or ammoFrame == nil
if tank2:FindFirstChild("Ammo") and isTouching(ammoFrame,tank2:GetChildren()[3]) then
script.Collision:Play()
local lastAmmoPos = ammoFrame.Position
playAnimation("Explosion",lastAmmoPos-UDim2.new(0,15,0,25),tank.Frame)
ammoFrame:Destroy()
tank2:GetChildren()[3]:Destroy()
end
if isTouching(ammoFrame,tank2) then
script.Collision:Play()
ammoFrame:Destroy()
workspace["Tank2"].Health.Value = workspace["Tank2"].Health.Value - ammoTypes[type]["dmg"]
end
end
The problem with this, is that if a player joins AFTER the ammo has been shot, they will not see the ammo on the GUI
Is there anyway to fix this? I can't just FireAllClients again, since that'll just fire another piece of ammo for all the players.
Have a service that keeps track of what ammo is in what state. Then all clients first asks the servers of current ammo states when joining, and then subscribes to further changes.

Resources