So I have this function that starts when the player needs to aim to cast a certain skill. This function should return, when the player clicks the left mouse button or taps on the screen, a Vector3.
The problem I have is that for some reason when I call the function, it doesn’t yield what comes AFTER said function, which is important because it needs to wait for the Vector3 returned. So what happens is that, instead, it returns nil.
This is the function:
function M:clickToAim()
local canSelect = true--TargetSelection:Start(), ignore this
if canSelect == true then
Service.CAS:SetTitle("startTargetSelection","Exit")
if Service.UIS.TouchEnabled then
click2Aim = Service.UIS.TouchTapInWorld:Connect(function(pos,ui)
if ui then return end
targetRaycast.FilterDescendantsInstances = {}
local ray = workspace.Camera:ViewportPointToRay(pos.X, pos.Y)
local result = workspace:Raycast(ray.Origin,ray.Direction*100)
if result then
click2Aim:Disconnect(); click2Aim = nil
return result
end
end)
else
click2Aim = Service.UIS.InputBegan:Connect(function(input,isTyping)
if isTyping then return end
if input.UserInputType == Enum.UserInputType.MouseButton1 then
local pos = Service.UIS:GetMouseLocation();
local ray = workspace.Camera:ViewportPointToRay(pos.X, pos.Y)
local result = workspace:Raycast(ray.Origin,ray.Direction*100)
if result then
warn(result)
click2Aim:Disconnect(); click2Aim = nil
TargetSelection:End()
return result
end
end
end)
end
end
end
Ideally the function should yield until I press the mouse button, so that it returns the raycast’s result value.
This is the function that calls the previous one, attached to a RemoteFunction, InvokeClient:
local function selectTarget(targetMode)
if targetMode == "clickToAim" then
local result = targeting:clickToAim() --should yield the function here until it returns the vector3 value i need, but it doesn't
if result then
print("pls work :))")
else
warn("oh me oh my") --instead this is printed
end
warn(result) --and this is equal to nil
return result
end
end
RemoteFunction.OnClientInvoke = selectTarget
Before I get people saying “but you shouldn’t be using onClientInvoke!!”: I know. I have a mechanic on the server-end of the system to prevent errors (for example, wrapping the whole thing in a pcall & a time limit for how long the server should wait for the client’s response).
I’m open to alternative systems to implement to avoid using OnClientInvoke of course, but I’m mostly interested in understand why my first function returns nil immediately instead of yielding until the player clicks\taps on the screen.
You made connected function which do not yield, the returns inside of connected functions do not return to outer functions. In fact returns inside of connected functions do nothing expect end the function early. You need to either use :Wait() or create a new bindable event and :Wait() on it.
I am not sure the best solution since you aren’t giving us why you are doing this, chances are you should just be using a :FireServer(localRaycast) to avoid OnClientInvoke
Ah, that makes a lot of sense actually… could you show me where to put the :Wait() exactly? I’m not sure where to place them to be honest.
I also did consider bindables but wanted to avoid them to keep the code less cluttered. If necessary, I’ll use them though.
To put it simply, my system works like this:
player presses skill keybind
client tells to server they selected X spell and the server checks if the player unlocked the skill and can use the skill.
If the conditions are met, the server will fire a RemoteFunction:InvokeClient(), where it’ll wait for the client to return where the player aimed to fire the skill in that direction.
if the position is returned within the time limit (15 seconds), the skill will be fired. Otherwise, the skill casting is canceled.
I used InvokeClient() because it made the most sense based on my needs tbh and it felt unnecessary to have 3-4 different remotes passing around data when I could’ve just used 2.
Since you have conditions in each connection it’s difficult to just use wait. Here’s an example of using a bindable event to wait for deeper connected result.
For your skill system I would say reducing the back-and-forth talking is very important, try out letting the client send both X spell and position at the same time. Do all the client side information gathering in one batch and all the server side checking in one batch.
player presses skill keybind
:FireServer(spellX, localRaycast)
Server checks conditions, done!
This list is much simpler and chances are players will know locally if they can use spells.
function M:clickToAim()
local resultEvent = Instance.new("BindableEvent")
Service.CAS:SetTitle("startTargetSelection","Exit")
if Service.UIS.TouchEnabled then
click2Aim = Service.UIS.TouchTapInWorld:Connect(function(pos,ui)
if ui then return end
targetRaycast.FilterDescendantsInstances = {}
local ray = workspace.Camera:ViewportPointToRay(pos.X, pos.Y)
local result = workspace:Raycast(ray.Origin,ray.Direction*100)
if result then
click2Aim:Disconnect(); click2Aim = nil
resultEvent:Fire(result)
end
end)
else
click2Aim = Service.UIS.InputBegan:Connect(function(input,isTyping)
if isTyping then return end
if input.UserInputType == Enum.UserInputType.MouseButton1 then
local pos = Service.UIS:GetMouseLocation();
local ray = workspace.Camera:ViewportPointToRay(pos.X, pos.Y)
local result = workspace:Raycast(ray.Origin,ray.Direction*100)
if result then
warn(result)
click2Aim:Disconnect(); click2Aim = nil
TargetSelection:End()
resultEvent:Fire(result)
end
end
end)
end
-- waiting for result extracted by :Fire
local result = resultEvent.Event:Wait()
resultEvent:Destroy()
return result
end