Hey there, I’m trying to make a coin-placement system but there is a slight problem, sometimes the coins decide to spawn on top of each other and it makes for some really weird spawning. Can I get some help please?
Here is the function that shows the code:
coinEvent.OnServerEvent:Connect(function(player: Player, coinId: string, ChosenLS)
if not currentCoins[coinId] then return end
local coinInfo = currentCoins[coinId]
local coinType: string = coinInfo.Name
local leaderstats = player:WaitForChild("leaderstats") :: leaderstatsDataType
if player:GetAttribute("DoubleTokens") then
leaderstats[ChosenLS].Value += coinInfo.Amount * 2
else
leaderstats[ChosenLS].Value += coinInfo.Amount
end
local respawnTimer: number = 5
if coinInfo.Timer then
respawnTimer = coinInfo.Timer
end
local previousPosition: Vector3 = coinInfo.Position
currentCoins[coinId] = nil
for i, plr in plrs:GetPlayers() do
if plr == player then continue end
coinEvent:FireClient(plr, "DestroyCoin", {Id = coinId})
end
task.delay(respawnTimer, function()
new(previousPosition, coinType)
end)
end)
I would have something like this. Basically when you are checking a new position you would loop through all current coins and check how close they are to the given position. If even one is close enough it’ll return false.
local Distance = 1 --> Studs
--@@ Checks if the magnitude of 2 positions, if they are below the distance variable then it's not a valid position
local function IsValid(target: Vector3, position: Vector3): boolean
return (target - position).Magnitude > Distance
end
--@@ This just loops through all coins. Should return true if you can place.
local function ValidatePosition(target: Vector3): boolean
for _, coin in currentCoins do
if not IsValid(target, coin.Position) then
return false
end
end
return true
end
--> Use ValidatePosition function and send in the new position you want to check.
Alright, I’ve just applied what you have told me and I managed to understand it! Sort of checking if any are overlapping etc or are really bunched up. Would this code seem correct to do?
(My game takes some time to load so I just wanted to double check)
--!strict
local Coin = {}
--// Services
local debris = game:GetService("Debris")
local ts = game:GetService("TweenService")
local market = game:GetService("MarketplaceService")
local plrs = game:GetService("Players")
local servstorg = game:GetService("ServerStorage")
local rs = game:GetService("ReplicatedStorage")
local httpserv = game:GetService("HttpService")
--// Other Variables
local Tokens = workspace:WaitForChild("DefaultService"):WaitForChild("Tokens")
local coinsFolder = rs.Assets.Coins
local remotes = rs.Assets.Remotes
local coinEvent = remotes.CoinEvent
type leaderstatsDataType = typeof(servstorg.leaderstats)
type coinDataType = {typeof(coinsFolder.Token)}
local onCoinTaken, new
local currentCoins = {}
local OutDist = 0.5
--// Main Code
new = function(position: Vector3, coinType: string, specificPlayer: Player?, specificId: string?)
local desiredCoin = coinsFolder:FindFirstChild(coinType)
if not desiredCoin then
error("Coin ".. tostring(coinType) .. " not found!")
end
local newCoin = {
CoinInstance = desiredCoin,
Position = position,
Id = if specificId then specificId else httpserv:GenerateGUID()
}
currentCoins[newCoin.Id] = {
Name = coinType,
Timer = desiredCoin.Timer.Value,
Amount = desiredCoin.Amount.Value,
Position = position
}
if specificPlayer then
coinEvent:FireClient(specificPlayer, "CreateCoin", newCoin)
else
coinEvent:FireAllClients("CreateCoin", newCoin)
end
end
plrs.PlayerAdded:Connect(function(player)
player.CharacterAdded:Wait()
for i, v in currentCoins do
new(v.Position, v.Name, player, i)
end
end)
local function IsValid(target: Vector3, position: Vector3): boolean
return (target - position).Magnitude > OutDist
end
local function ValidatePosition(target: Vector3): boolean
for _, coin in currentCoins do
if not IsValid(target, coin.Position) then
return false
end
end
return true
end
coinEvent.OnServerEvent:Connect(function(player: Player, coinId: string, ChosenLS)
if not currentCoins[coinId] then return end
local coinInfo = currentCoins[coinId]
local coinType: string = coinInfo.Name
local leaderstats = player:WaitForChild("leaderstats") :: leaderstatsDataType
if player:GetAttribute("DoubleTokens") then
leaderstats[ChosenLS].Value += coinInfo.Amount * 2
else
leaderstats[ChosenLS].Value += coinInfo.Amount
end
local respawnTimer: number = 5
if coinInfo.Timer then
respawnTimer = coinInfo.Timer
end
local previousPosition: Vector3 = coinInfo.Position
if ValidatePosition(previousPosition) == true then
currentCoins[coinId] = nil
for i, plr in plrs:GetPlayers() do
if plr == player then continue end
coinEvent:FireClient(plr, "DestroyCoin", {Id = coinId})
end
task.delay(respawnTimer, function()
new(previousPosition, coinType)
end)
end
end)
Coin.new = new
return Coin
There seems to be no errors / no issues. Probably because there is only 1 coin spawn-part as of now. But once I start adding more and issues possibly occuring, I may instead Message you instead of bumping this post. Thank you for your help!
Ah I see the issue in that case. So making a slight adjustment to the ValidateCoin function.
--@@ This just loops through all coins. Should return true if you can place.
local function ValidatePosition(target: Vector3, coinId: string): boolean
for id, coin in currentCoins do
if id == coinId then
continue
end
if not IsValid(target, coin.Position) then
return false
end
end
return true
end
Then you would just pass the coin id through the function too. The reason this is happening is because it’s all checking it’s own position.
Edit: I completely misread the reply, I thought you meant only one part was spawning. In any case this should fix the issue.