Run animation when player is in a range

Hi, I have many spinning coins in my game so when they are spinning at once it does not work as expected.

How it should look like:

How it is:

I was trying to use an extra part as a “player detector”:

local Activator = script.Parent.AnimationActivator
local AnimationScript = script.Parent.Coin["Rotate animation"]

Activator.Touched:Connect(function(otherPart: BasePart)  
	AnimationScript.Enabled = true
end)

Activator.TouchEnded:Wait(function(otherPart: BasePart) 
	if #Activator:GetTouchingParts() == 0 then
		AnimationScript.Enabled = false
	end
end)

But there are some issues with that approach:

  1. even when I changed the collision group GetTouchingParts returned all parts
  2. GetTouchingParts also included parts included in the coin model
  3. depending on how fast the user leaves the area GetTouchingParts sometimes returns 5+ parts related to a player in case the character is out of range.

How in another way I can enable the animation script without looping through all coins and manually counting the distance between a coin and a player?

1 Like

Getting touching parts is gimmicky niche keyword and you shouldn’t rely your entire system on it their is countless ways to achieve this that are many times more reliable here is some example code. Heck your better off just using .touched your use of GetTouchingParts is reinventing the wheel. I will create a code for you okay :slight_smile:

I need to Finnish breakfast first lol :joy:

That sounds interesting, I would appreciate some example :bowing_man:

Can you show me a image from the explorer I want to see how the ancestory works so I can have the script work perfectly

You know for the referencing in the code
If you don’t you will just have to modify the code to work for your game change some reference etc
nvm i can figure out from the code you showed me

Here it is:
image

It doesn’t have to work perfectly, when I have an idea + example I think I will be able to figure out how to fit it into my case :wink:

should i add notes explaining what some of the stuff does in the code or nah? sorry for all the questions hope you don’t mind

No problem, it is always nice to have some documentation but I think I will also be able to understand that without comments :smiley:

This code i made works perfectly no flaws hope this helped

local CoinSystem = script.Parent
local Speed = 0.1

game.Players.PlayerAdded:Connect(function(Plr)
Plr.CharacterAdded:Connect(function(Char)
while task.wait() do
local Mag = math.floor((Char:WaitForChild("HumanoidRootPart").Position - CoinSystem.AnimationActivator.Position).Magnitude)	
if Mag <= 35 then
CoinSystem.Coin.CFrame = CoinSystem.Coin.CFrame * CFrame.fromAxisAngle(Vector3.new(0,Speed,0),Speed)						
end		
end	
end)	
end)


the parts size is 70,1,70 and 35 is the range where the coin spins you can alter it if you want

1 Like

Unfortunately it does not work as expected :frowning:

I think in my case the issue is calling wait().
On the last level, the user will be able to have up to 8450

Try to apply that script

local distance = 5
local currentOffset = distance

for i = 1, 8450 do
	local CoinClone = workspace.Camp.Coin:Clone()
	CoinClone.Position += Vector3.new(currentOffset, 0, 0)
	CoinClone.Parent = workspace.Camp
	currentOffset += distance
end 

and please let me know is it work correctly for you. Maybe I’m trying to resolve the issue in the wrong place

i don’t really fully understand what you mean but did my response help you at all? i mean the script makes the coin spin when close and when you get farther away it stops isn’t that what you wanted?

on the contrary, it helped me a lot :slight_smile:

Ok, so, as I understand in my case the issue was when we had a lot of wait() calls their wait for each other (what the surprise :stuck_out_tongue:) so at the end between each tick it takes a while for > 8k objects on a map.

:warning: There is horrible naming in POC code Coin.Coin → MasterCoin.Coin, you were worn, now you can continue :stuck_out_tongue:

The solution is to combine @swag4lifex solution with the “PlayerDetector” part.

For us, the most important information is the size (Z and Y because the object is rotated by 90 deg) and the fact that the animation script is disabled at the start.

When the player enters the “PlayerDetector” area the animation will be fired:

local Coin = script.Parent
local PlayerDetector = script.Parent.PlayerDetector
local AnimatinoScript = script.Parent.Coin["Rotate animation"]

PlayerDetector.Touched:Connect(function(otherPart: BasePart) 
	local HumanoidRootPart = otherPart.Parent:FindFirstChild("HumanoidRootPart")
	if HumanoidRootPart  then
		local player = game.Players:GetPlayerFromCharacter(HumanoidRootPart.Parent)
		
		if not Coin:GetAttribute(player.UserId) then
			AnimatinoScript.Enabled = true
		end
		
		Coin:SetAttribute(player.UserId, true)
	end
end)

In the animation script we count the distance between the coin and the player and when it is larger than half the size of “PlayerDetector” we disable the animation

local MasterCoin = script.Parent.Parent
local PlayerDetector = MasterCoin.PlayerDetector
local Coin = script.Parent
local speed = .1

while wait() do
	for userId, _ in pairs(MasterCoin:GetAttributes()) do
		local player = game.Players:GetPlayerByUserId(userId)
		
		local HumanoidRootPart = player.Character:WaitForChild("HumanoidRootPart")

		if not HumanoidRootPart then
			continue
		end

		local characterPosition = HumanoidRootPart.Position
		local coinPosition = Coin.Position

		local distance =  math.floor((characterPosition - coinPosition).Magnitude)
		
		if distance <= (PlayerDetector.Size.Z / 2) then
			Coin.CFrame = Coin.CFrame * CFrame.fromAxisAngle(Vector3.new(speed, 0,0),speed)		
		else
			script.Enabled = false
			MasterCoin:SetAttribute(userId, nil)
			return
		end
	end
end

Conclusion: there are only a few wait() calls at the same time and the animations are smooth

If your going to check if the player is in range of something you should use magnitude or region3 and possibly raycast in more advanced cases just don’t use touched it will just cause lag and be innificent

.touched should only be used to see if a play is touching sonething eg. Kill brick speed brick that kind of thing.

(Object1.position - object2.position).magnitude
Put this in a loop use a simple if then logic and boom you can check if the players within distance of an object

Theoretically, I agree but… as you mention I need to

Put this in a loop

So I need some condition for the loop, putin 8k while true do don’t look like a good idea :stuck_out_tongue:

As I described before using wait() as a “condition” also does not resolve the issue. So when the user touches the invisible part I know he is in a range and touched the handle do nothing more than enable another script once. If there will be a big performance issue, an option is to disconnect the Touched handler (or use :Once instead of :Connect) after enabling AnimationScript and bind it when the user is out of the range.

In my case I don’t need really big precision I just need to know aproxymently how far from the coin model the user is, if it will be exactly half of the Size.Z or +/- 10 - 20 it is ok in my case, for r = 50 (Size.Z = 100) the maring of mismatch is acceptable.

The next thought is to create a “detect area” around the player instead of for each coin separately. In that case, I will have only one while wait() do loop instead of 8k, but:

  • it required a loop through all objects in the area and check whether is it a coin or not
  • it will also work when there are will be no coins on the map
  • it looks like a more complicated solution and does not necessarily have a performance advantage

P.S. I enjoy the conversation :slight_smile:

You could use one script with for i,v pairs() but judging on your last few responses it seems if you’ve figured it out so I will remain silent

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.