Hello,
I have been trying to make a selection box system for a RTS type game. However, lots of errors have been happening. I have been trying to search a table from a replicated storage folder using :getchildren() and its just been so confusing. What i am trying to do is:
get the names of the children of the replicated storage folder
compare it with the names of the selected parts parents, parents parents, and so on
return the instance that is succesfully compared
then put the parents parents whatever in a table
Im so sorry if this is hard to understand - im confused myself. Please feel free to ask any questions!!!
The places where i am having most trouble on is lines 24 - 31.
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local Player = Players.LocalPlayer
local Mouse = Player:GetMouse()
local SelectionBox = script.Parent -- "SelectionBox is not a valid member of ScreenGui"
local ClickedOn = nil
local Camera = workspace.CurrentCamera
local SelectionLocation = workspace.World.Soldiers -- the location of where the selections thingy be
local function GetSelectedParts()
local SelectedParts = {}
for i, Part in SelectionLocation:GetDescendants() do
if not Part:IsA("BasePart") then
continue
end
local PosOnScreen = Camera:WorldToScreenPoint(Part.Position)
-- convert the position to the screen thingy
if
PosOnScreen.X < (SelectionBox.AbsolutePosition.X + SelectionBox.AbsoluteSize.X) and PosOnScreen.X > (SelectionBox.AbsolutePosition.X) and
PosOnScreen.Y < (SelectionBox.AbsolutePosition.Y + SelectionBox.AbsoluteSize.Y) and PosOnScreen.Y > (SelectionBox.AbsolutePosition.Y)
-- check if the pos on screen is inside the selection frame
then
local jim = game.ReplicatedStorage.Units:GetChildren().Name
local jims = jim[Part.Parent.Name] or jim[Part.Parent.Parent.Name] or jim[Part.Parent.Parent.Parent.Name] or jim[Part.Parent.Parent.Parent.Parent.Name]
if jims then
table.insert(SelectedParts, nil) -- how do i get the successful one? im so confused on what i am doing lol
print(nil)
end
end
end
return SelectedParts
end
UserInputService.InputBegan:Connect(function(Input, GameProcessed)
if GameProcessed then
return
end
if Input.UserInputType == Enum.UserInputType.MouseButton1 then
ClickedOn = Vector2.new(Mouse.X, Mouse.Y)
SelectionBox.Visible = true
SelectionBox.Position = UDim2.fromOffset(ClickedOn.X, ClickedOn.Y)
end
end)
UserInputService.InputEnded:Connect(function(Input)
if Input.UserInputType == Enum.UserInputType.MouseButton1 then
ClickedOn = nil
SelectionBox.Visible = false
GetSelectedParts()
end
end)
UserInputService.InputChanged:Connect(function()
if ClickedOn then
local X = Mouse.X - ClickedOn.X
local Y = Mouse.Y - ClickedOn.Y
SelectionBox.Size = UDim2.fromOffset(X, Y)
end
end)
are you trying to get all of the soldiers that are in the selection area? and why do you need to get all of the individual parts
you can make a recursive function that goes up the hierarchy until it finds a model, and then compare that with the units in replicated storage
edit: you should also optimize by removing all descendants of the character from the parts table, so it doesnt search and get the same character again
local function findCharacter(instance : Instance)
if (instance:IsA("Model")) then return instance end
if (instance == workspace) then return end -- or instance == nil if its not guaranteed to be in workspace
return findCharacter(instance.Parent)
end
--example code inside the loop
local Part
local units = game.ReplicatedStorage.Units:GetChildren()
local character = findCharacter(Part)
for _, unit in ipairs(units) do
if unit.Name == character.Name then
--found matching unit
break
end
end
you can iterate through the selected parts and remove if its a descendant of the character
--in the loop
for i = 1, #SelectedParts do
if not SelectedParts[i]:IsDescendantOf(character) then continue end -- if valid then skip
table.remove(SelectedParts, i)
i -= 1 -- shift index because of the .remove()
end
it might also be faster to store the characters found in a dictionary (acting as a hashset) and skip if its a repeat character
--outside loop
local seenCharacters = {}
--in the loop
if (seenCharacters[character]) then continue end
seenCharacters[character] = true
I did the second solution, but it isnt producing any errors and isnt working. Heres the part of my script which i put it in:
then
local units = game.ReplicatedStorage.Units:GetChildren()
local character = findCharacter(Part)
local seenCharacters = {}
for _, unit in ipairs(units) do
if (seenCharacters[character]) then continue end
seenCharacters[character] = true
if unit.Name == character.Name then
print(character.Name)
table.insert(SelectedParts, character)
break
end
end
end
put it in the loop that iterates through the parts, because its supposed to skip the current part if its part of a repeated character
also units:GetChildren() at the start of the script will save a lot of processing power
local units = game.ReplicatedStorage.Units:GetChildren() -- put this outside at the start
if (seenCharacters[character]) then continue end
seenCharacters[character] = true
then
local character = findCharacter(Part)
local seenCharacters = {}
for _, unit in ipairs(units) do
if unit.Name == character.Name then
print(character.Name)
table.insert(SelectedParts, character)
break
end
end
end
Its still isnt working - but the performance saving tip helped! Heres my full script so far:
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local Player = Players.LocalPlayer
local Mouse = Player:GetMouse()
local SelectionBox = script.Parent -- "SelectionBox is not a valid member of ScreenGui"
local units = game.ReplicatedStorage.Units:GetChildren() -- put this outside at the start
local ClickedOn = nil
local Camera = workspace.CurrentCamera
local SelectionLocation = workspace.World.Soldiers -- the location of where the selections thingy be
local function findCharacter(instance : Instance)
if (instance:IsA("Model")) then return instance end
if (instance == workspace) then return end -- or instance == nil if its not guaranteed to be in workspace
return findCharacter(instance.Parent)
end
local function GetSelectedParts()
local SelectedParts = {}
for i, Part in SelectionLocation:GetDescendants() do
local character = findCharacter(Part)
local seenCharacters = {}
if (seenCharacters[character]) then continue end
seenCharacters[character] = true
if not Part:IsA("BasePart") then
continue
end
local PosOnScreen = Camera:WorldToScreenPoint(Part.Position)
-- convert the position to the screen thingy
if
PosOnScreen.X < (SelectionBox.AbsolutePosition.X + SelectionBox.AbsoluteSize.X) and PosOnScreen.X > (SelectionBox.AbsolutePosition.X) and
PosOnScreen.Y < (SelectionBox.AbsolutePosition.Y + SelectionBox.AbsoluteSize.Y) and PosOnScreen.Y > (SelectionBox.AbsolutePosition.Y)
-- check if the pos on screen is inside the selection frame
then
for _, unit in ipairs(units) do
if unit.Name == character.Name then
print(character.Name)
table.insert(SelectedParts, character)
break
end
end
end
end
return SelectedParts
end
UserInputService.InputBegan:Connect(function(Input, GameProcessed)
if GameProcessed then
return
end
if Input.UserInputType == Enum.UserInputType.MouseButton1 then
ClickedOn = Vector2.new(Mouse.X, Mouse.Y)
SelectionBox.Visible = true
SelectionBox.Position = UDim2.fromOffset(ClickedOn.X, ClickedOn.Y)
end
end)
UserInputService.InputEnded:Connect(function(Input)
if Input.UserInputType == Enum.UserInputType.MouseButton1 then
ClickedOn = nil
SelectionBox.Visible = false
local john = GetSelectedParts()
game.ReplicatedStorage.TableSend:FireServer(john)
game.ReplicatedStorage.TableSendClient:Fire(john)
end
end)
UserInputService.InputChanged:Connect(function()
if ClickedOn then
local X = Mouse.X - ClickedOn.X
local Y = Mouse.Y - ClickedOn.Y
SelectionBox.Size = UDim2.fromOffset(X, Y)
end
end)