Hello, new user here.
I have been tinkering with roblox scripting for a few weeks now and I have been trying to emulate the bee swarm style mining system (or rather pollen gathering system). right now I have a setup of raycasts to come from the players head in various directions and then detect the part they are hitting and then move the part down by a marginal amount as the flowers do in bee swarm. Everything I have done is from browsing the internet for hours upon hours to get this to work lol. As of right not it works, however its not perfect like it is in bee swarm. So to get to my question, is there a better way I could be doing this? Should I be using raycasting? Originally I was using onotouched, with an invisible "scoop" which would do the same onTouched as the rays with different shapes.
Again, I am very new to scripting and have been doing it for fun in my free time via watching videos, reading and just trying things out. So my code most certainly isn't pretty or perfect, just asking for some direction so I can continue this fun project! thanks
game.Players.PlayerAdded:Connect(function(player) player.CharacterAdded:Connect(function(character) -- get the event from client side local ReplicatedStorage = game:GetService("ReplicatedStorage") local rayCastEvent1 = ReplicatedStorage:FindFirstChild("rayCastEvent1") -- raycast function local function rayCastEvent1(player,var) local raycastParams = RaycastParams.new() raycastParams.FilterType = Enum.RaycastFilterType.Blacklist raycastParams.FilterDescendantsInstances = {character} raycastParams.IgnoreWater = true -- fire multiple raycast to move multiple parts of the mine at once local raycastResult = workspace:Raycast(character.Head.Position, Vector3.new(0, -50, -20), raycastParams) local raycastResult2 = workspace:Raycast(character.Head.Position, Vector3.new(0, -100, 0), raycastParams) local raycastResult3 = workspace:Raycast(character.Head.Position, Vector3.new(0, -50, 20), raycastParams) local raycastResult4 = workspace:Raycast(character.Head.Position, Vector3.new(20, -50, 0), raycastParams) if raycastResult or raycastResult2 or raycastResult3 or raycastResult4 then -- find parts hit by raycast local hitPart, hitPosition = unpack({raycastResult.Instance.Parent, raycastResult.Position}) local hitPart2, hitPosition2 = unpack({raycastResult2.Instance.Parent, raycastResult2.Position}) local hitPart3, hitPosition3 = unpack({raycastResult3.Instance.Parent, raycastResult3.Position}) local hitPart4, hitPosition4 = unpack({raycastResult4.Instance.Parent, raycastResult4.Position}) -- set model# to part hit by each raycast local model = hitPart local model2 = hitPart2 local model3 = hitPart3 local model4 = hitPart4 wait() -- find the child of the part hit since the part hit is the model if hitPart.Parent or hitPart2.Parent or hitPart3.Parent or hitPart4.Parent == game.Workspace.mineParts then hitPart.PrimaryPart = hitPart:FindFirstChild("aMinePart") hitPart2.PrimaryPart = hitPart2:FindFirstChild("aMinePart") hitPart3.PrimaryPart = hitPart3:FindFirstChild("aMinePart") hitPart4.PrimaryPart = hitPart4:FindFirstChild("aMinePart") -- move parts hit by ray with cframe hitPart:SetPrimaryPartCFrame(hitPart:GetPrimaryPartCFrame()*CFrame.new(0,-.3,0)) hitPart2:SetPrimaryPartCFrame(hitPart2:GetPrimaryPartCFrame()*CFrame.new(0,-.3,0)) hitPart3:SetPrimaryPartCFrame(hitPart3:GetPrimaryPartCFrame()*CFrame.new(0,-.3,0)) hitPart4:SetPrimaryPartCFrame(hitPart4:GetPrimaryPartCFrame()*CFrame.new(0,-.3,0)) wait(5) hitPart:SetPrimaryPartCFrame(hitPart:GetPrimaryPartCFrame()*CFrame.new(0,.3,0)) hitPart2:SetPrimaryPartCFrame(hitPart2:GetPrimaryPartCFrame()*CFrame.new(0,.3,0)) hitPart3:SetPrimaryPartCFrame(hitPart3:GetPrimaryPartCFrame()*CFrame.new(0,.3,0)) hitPart4:SetPrimaryPartCFrame(hitPart4:GetPrimaryPartCFrame()*CFrame.new(0,.3,0)) end end end game.ReplicatedStorage.rayCastEvent1.OnServerEvent:Connect(rayCastEvent1) end) end)
Hello,
Very fun question. Let's unpack it to make sure we're on the same page:
After going and playing "Bee Swarm Simulator" for about a minute, I seem to notice something. It appears as if each Bee chooses a random part that is within a certain radius to the player, farms the part, and then chooses another part within a certain radius of the player. This means that we have to construct an algorithm to:
Figure out which flower field the player is on.
Search through each flower part in the field and measure the distance between a given flower and the player.
Choose a random flower part that is within the distance given.
Pretty straightforward.
To figure out which field the player is located on, we could use various methods. The method I am going to use will be checking if the player's location is within a certain area. To do this I create minimum and maximum x and z values to check against the player's position. We encapsulate this code in a function so that we can use it later.
Implementation of said function:
local minx = 0 local maxx = 100 local minz = 0 local maxz = 100 local function infield(player) --input with player object local char = player.Character or workspace:WaitForChild(player.Name) local plrpos = char.PrimaryPart.Position local plrposx = plrpos.X local plrposz = plrpos.Z if (plrposx >= minx) and (plrposx <= maxx) and (plrposz >= minz) and (plrposz <= maxz) then return true, workspace.Field1 else return false, nil end end)
I will note that this function is very simple and only accounts for one field, but in reality, you're probably going to be using touch interests to be figuring out the position/field anyway. If you really do want to use a function like this though, we could create a 2d array to store the info of each field.
An example of said system is shown below. We assume that you have a folder in the workspace called "Fields" that contains invisible, anchored, and non-collidible parts to represent the boundaries of each field. We also assume that the parts are named the same as each real field that is parented to the workspace. Using these parts, we can create many arrays of many maximum and minimum x and z values.
local allfields = {} for i,v in pairs(workspace.Fields:GetChildren()) do local ms = v.Size local mp = v.Position local xl = mp.X - ms.X/2 local xm = mp.X + ms.X/2 local zl = mp.Z - ms.Z/2 local zm = mp.Z + ms.Z/2 table.Insert(allfields,{v.Name,xm,xl,zm,zl}) --insert min/max info into big table end local function infield(player) --input with player object local char = player.Character or workspace:WaitForChild(player.Name) local plrpos = char.PrimaryPart.Position local plrposx = plrpos.X local plrposz = plrpos.Z for i,v in pairs(allfields) do if (plrposx >= v[2]) and (plrposx <= v[3]) and (plrposz >= v[4]) and (plrposz <= v[5]) then return true, workspace[v[1]] end return false, nil end)
Alright, now that we've gone over figuring out the player's field, we can go on searching through all the flower objects in the field. To do this, we'll create a function that searches through the field and checks the distance between the player and the flower part. Luckily for us, Roblox already has a function that can check the distance between two points called Magnitude
. This means that we just have to loop, use the function, and then add it to a table if it's within our threshold.
local distance = 10 local function searchfield(player,field) local char = player.Character or workspace:WaitForChild(player.Name) local plrpos = char.PrimaryPart.Position local accepted = {} for i,v in pairs(field:GetChildren()) do if (plrpos - v.Position).Magnitude <= distance then table.Insert(accepted,v) end end return accepted end)
Now, we simply choose a random value within our table. To do this cleanly, I'm going to create yet another function that incorporates our previous functions
local function pickflower(player) local in, field = infield(player) if in == false then return false end local flowers = searchfield(player,field) if #flowers == 0 then return false end return flowers[math.random(1,#flowers)] end)
Let's put all our code together:
local allfields = {} for i,v in pairs(workspace.Fields:GetChildren()) do local ms = v.Size local mp = v.Position local xl = mp.X - ms.X/2 local xm = mp.X + ms.X/2 local zl = mp.Z - ms.Z/2 local zm = mp.Z + ms.Z/2 table.Insert(allfields,{v.Name,xm,xl,zm,zl}) end local function infield(player) local char = player.Character or workspace:WaitForChild(player.Name) local plrpos = char.PrimaryPart.Position local plrposx = plrpos.X local plrposz = plrpos.Z for i,v in pairs(allfields) do if (plrposx >= v[2]) and (plrposx <= v[3]) and (plrposz >= v[4]) and (plrposz <= v[5]) then return true, workspace[v[1]] end return false, nil end) local distance = 10 local function searchfield(player,field) local char = player.Character or workspace:WaitForChild(player.Name) local plrpos = char.PrimaryPart.Position local accepted = {} for i,v in pairs(field:GetChildren()) do if (plrpos - v.Position).Magnitude <= distance then table.Insert(accepted,v) end end return accepted end) local function pickflower(player) local inA, field = infield(player) if inA == false then return false end local flowers = searchfield(player,field) if #flowers == 0 then return false end return flowers[math.random(1,#flowers)] end)
Why is this any better?
With a function-oriented system, we've made it easier to change our code, easier to scale up our game/functions, easier for others to read your code, AND we've created a robust system that is able to properly manage which flowers to pick, instead of just firing Rays all around.
Implementation
Ok, we can pick a flower! Now what? Let's implement your code:
We can create a variable named 'Bees'
that will dictate how many bees we have access to, we then take this number and use it to dictate how many parts should be moved down. Another thing we can add is a loop that tells the bees when to mine blocks.
An implementation of this is seen below:
local bees = 5 while true do for i,v in pairs(game:GetService("Players"):GetChildren()) do local pick = pickflower(v) if pick ~= false then local maxbees = bees if maxbees > #pick then maxbees = #pick end for bee = 1, maxbees do local num = math.random(1,#pick) local picked = pick[num] table.remove[pick,num] --do whatever you want with picked end end end wait(1) end
Practice Exercises
Implement the code so that many different people can farm
Make the number of bees differ from person to person
Make the number of bees save when you rejoin
Resources
Below is documentation discussing everything I have used and explained to you in this post:
Conclusion
I hope I was able to help set you in a positive direction!
Cheers,
Chase