Bee Movement like Bee Swarm Simulator

Hey!
I’m trying to replicate how Bee Swarm Simulator made the bees movement, so far it works fine when you’re idle, but I don’t like the way it looks when you move.

I’m using client-side rendering to reduce server-side lag and do smooth tweens on the client, the server just tells the cframe where the “bee” would move and the client detect the change and move the “bee” to the position.

What I want to achieve: (this is not bee swarm simulator, but a fan-made of it)

My version:

Scripts:

Server:

function Bee:Init()
	task.spawn(function()
		while self.Object.Parent do
			local character = self.Player.Character or self.Player.CharacterAdded:Wait()
			local humanoid = character.Humanoid
			
			local oldCFrame = self.LastCFrame
			local newCFrame = self:GetCFrame()

			local distance = (newCFrame.Position - oldCFrame.Position).Magnitude
			local t = (distance/self.Speed) + .5
			
			if humanoid.MoveDirection.Magnitude > 0 then
				self.Object:SetAttribute("CFrame", newCFrame)
				task.wait(t)
			else
				self.Object:SetAttribute("CFrame", newCFrame)
				task.wait(t + rng:NextNumber(3,6))
			end
		end
	end)
end

function Bee:CreateObject()
	local object = Instance.new("Configuration")
	object.Name = self.ID
	
	object:SetAttribute("Name", self.Name)
	object:SetAttribute("CFrame", CFrame.new(0, 0, 0))
	object:SetAttribute("Speed", self.Speed)
	
	object.Parent = bees[self.Player.Name]
	
	return object
end

function Bee:GetCFrame()
	local character = self.Player.Character or self.Player.CharacterAdded:Wait()
	local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
	
	local X = rng:NextNumber(-15, 15)
	local Y = rng:NextNumber(-1, 1)
	local Z = rng:NextNumber(-15, 15)
	
	self.Offset = CFrame.new(X, Y, Z)
	self.LastCFrame = humanoidRootPart.CFrame * self.Offset
	return self.LastCFrame
end

Client:

function BeeController:CreateBee(playerFolder, beeObject)
	if not clientBees[playerFolder] then clientBees[playerFolder] = {} end
	if not clientBees[playerFolder][beeObject.Name] then
		
		local model = ReplicatedStorage.Bees:FindFirstChild(beeObject:GetAttribute("Name")):Clone()
		model:PivotTo(beeObject:GetAttribute("CFrame"))
		model.Parent = beeObject
		
		beeObject:GetAttributeChangedSignal("CFrame"):Connect(function()
			local distance = (model.PrimaryPart.Position - beeObject:GetAttribute("CFrame").Position).Magnitude
			local direction = (model.PrimaryPart.Position - beeObject:GetAttribute("CFrame").Position).Unit
			
			local lookCF = CFrame.lookAt(model.PrimaryPart.Position, model.PrimaryPart.Position + direction * -1)
			local tween = TweenService:Create(model.PrimaryPart, TweenInfo.new(.25), {CFrame = lookCF})
			tween:Play()
			tween.Completed:Wait()
			
			local tween = TweenService:Create(model.PrimaryPart, TweenInfo.new(distance/beeObject:GetAttribute("Speed"), Enum.EasingStyle.Linear), {CFrame = CFrame.new(beeObject:GetAttribute("CFrame").Position, beeObject:GetAttribute("CFrame").Position+ direction * -1)})
			tween:Play()
			tween.Completed:Wait()
			
			local tween = TweenService:Create(model.PrimaryPart, TweenInfo.new(.25), {CFrame = CFrame.new(model.PrimaryPart.CFrame.Position) * CFrame.Angles(beeObject:GetAttribute("CFrame"):ToEulerAnglesXYZ())})
			tween:Play()
			tween.Completed:Wait()
		end)
		
		clientBees[playerFolder][beeObject.Name] = beeObject
	end
end

Any help is appreciated,

Thanks.

2 Likes

For the final position is looks like most of them seem to face the same orientation once they reach their goal. Is that because all the created parts have the same look vector?

The Bee Swarm bees also seem to have a Y-axis bob as they move. Replicating this might make them look more natural overall.

2 Likes

I made it so, when they reach the final position, they face where the character is looking at, in case you didn’t notice Bee Swarm does the same.

For the Y axis, I’m already doing it, but the offset is so small that it’s kinda hard to see in the video.

Thanks for your reply!

1 Like

Hello! If you finished your Bob system, could you give details on how you made one? (And also if its the same type of Bobbing as in the original game)

(Sorry for the reply so far away in time of the post date)

1 Like

They use like sines i believe in the fangame

1 Like

I don’t know what you mean by “bob system”, can you be more specific?

1 Like

You said you were making you Bees bob on the Y Axis, i’d like to know how you did that exactly

1 Like

I think to create the y axis bob effect, there are 2 layers of CFrame manipulation with sines/cosines.

(Put this all in a loop, render stepped, while loop, whatever works better for you.)

• The first layer is just the position. Just do something like


local speed = 2

local bob = math.sin(tick() * speed)

local part = script.Parent — place inside an anchored part

part.CFrame = part.CFrame * CFrame.new(0,bob,0)

What we have now is a simple bob with no rotation, just position, but we will do that in the next layer.

• The second layer is now the rotation. Just use the code from the first layer, and add this.


part.CFrame = part.CFrame * CFrame.new(0,bob,0) * CFrame.Angles(math.rad(bob),0,0)

You will more than definitely have to modify these little snippets of code, as I’m not at my computer at the moment.

So yea, that’s the basics.

Sorry, I completely forgot about this.
Anyways, I didn’t add the bob, but you could try what @Vixionxry is suggesting, I’m not sure if it would work if you’re using tweens to move, like I did in the post.

But you can use deltaTime to calculate the CFrame and calculate the T that can be used to lerp between 2 positions to move them here’s an example:

function self:getT(data)
	if not data.target then return end
	
	local deltaTime = workspace:GetServerTimeNow() - data.time
	local distance = (data.target - data.start).Magnitude
	local distanceTraveled = (data.speed * deltaTime) + data.distance

	local t = distanceTraveled / distance
	return math.clamp(t, 0, 1)
end

Now with this data you can calculate the CFrame like so:

function self:getCFrame(data)
	local t = self:getT(data)
	local position = lerp(t, data.start, data.target)

	local lookAtCFrame = CFrame.new(data.start, data.target)
	
	local cframePosition = CFrame.new(position)
	local rotation = lookAtCFrame.Rotation
	return cframePosition * rotation
end
1 Like

I think i know of a solution that might help.

Instead of choosing 1 postion inside the ring around the player and going there
This would make it so that if the player moves its delayed. Which is what you did and not what you want.

Solution:
redefine postion every movement frame with the set offset.
determine the offset and apply that to the current player location.
Dont refresh the offset unless it needs to wander around the player.
Only update the target position to be player.pos+offset and then move to the point.

Also to reduce lag maybe check if the player is moving, so you dont have to keep sending new postion data thats the same.

1 Like

Oh ok! Thanks for the reply, i’m going to test this, and try to create something based on your code snippet.