Race system with player positions, 1st, 2nd, etc

  1. What do you want to achieve? Keep it simple and clear!
    I’m trying to make a race system where the person farthest in the game is 1st place, and the person behind him is 2nd place, etc.

  2. What is the issue? Include screenshots / videos if possible!
    I can detect how far away the person is to the next checkpoints via magnitude, however I am unsure how proceed with detecting if two players are on the same checkpoints and which is first with said magnitude and sorting who is 1st, 2nd, 3rd. Also, tables aren’t my expertise.

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I have tried looking for solutions, however there is very few, and the ones that I did find, were not very much help as either they talked about putting many detection parts which would be very tedious and would not work for my game, or were way too complicated for me to understand.

3 Likes

You can use magnitude like you said, here is an example of what I would do.

local firstPlace= [[ your values in replicated/server storage named the correct positions in race (FirstPlace, SecondPlace, etc.) ]]

local secondPlace= [[ your values in replicated/server storage named the correct positions in race (FirstPlace, SecondPlace, etc.) ]]

((thirdplace, fourth, 5, etc..))

if RaceCar.PrimaryPart.Position.Magnitude > OtherRaceCar.PrimaryPart.Position.Magnitude then
    firstPlace = RaceCar.Name
    secondPlace = OtherRaceCar.Name
end```

Maybe I’m not understanding it right, but wouldn’t that only work for one checkpoint?

Here’s my code for detecting magnitude from the player to the next checkpoint:

game.Players.PlayerAdded:Connect(function(player)
	local loaded = player:HasAppearanceLoaded()
	while not loaded do
		wait()
		loaded = player:HasAppearanceLoaded()
	end

	for _, v in pairs(game.Workspace.Checkpoints:GetChildren()) do
		while task.wait(.5) do
			if player.leaderstats.Checkpoints.Value < game.ReplicatedStorage.RoundInfo.NumOfCheckpoints.Value then
				local pos = workspace.Checkpoints[player.leaderstats.Checkpoints.Value+1].Position
				for _, player in pairs(game.Players:GetPlayers()) do
					local character = player.Character
					if not character then
						continue
					end
					local HRP = character:FindFirstChild("HumanoidRootPart")
					if not HRP then
						continue
					end
					local hrppos = HRP.Position
					local distance = (hrppos - pos).Magnitude
					print(distance)
				end
			else
				local pos = game.Workspace.Checkpoints:FindFirstChild("0").Position
				for _, player in pairs(game.Players:GetPlayers()) do
					local character = player.Character
					if not character then
						continue
					end
					local HRP = character:FindFirstChild("HumanoidRootPart")
					if not HRP then
						continue
					end
					local hrppos = HRP.Position
					local distance = (hrppos - pos).Magnitude
					print(distance)
				end
			end
		end
	end
end)

I think you can put all the current values in a table, then use table.sort() to sort it out, then get the first three people in descending order.

This would requre alot more if-else statements, because if there would be 50 players, how much if-else would you write for comparing everyone’s value?

1 Like

I answered this same question about 2 weeks ago but I assume my response falls under the category that it was either too tedious or too complicated (How to detect who’s 1st, 2nd, 3rd, etc in my racing game - #6 by uwuCulturist)

If you want a simple answer for how to store which checkpoint a player is on, you can use a table like so

local Players = game:GetService("Players")
local PlayerProgress = {
    
}

-- Setting every player's progress to 0 at the start of the race

for i,v in next, Players:GetPlayers() do
    PlayerProgress[v.Name] = 0
end

--[[ Now the player progress will look like this
{
    PlayerName1 = 0,
    PlayerName2 = 0,
    PlayerName3 = 0,
    PlayerName4 = 0,
}
]]

To add or remove progress from a player, do this

     PlayerProgress[PlayerNameHere] += 1 -- Adds 1 progress
     PlayerProgress[PlayerNameHere] -= 1 -- Subtracts 1 progress

Once a player passes a checkpoint (Checkpoint.Touched signal), add 1 to their progress or set the progress to some value that you can easily sort.

Because you’re dealing with a dictionary rather than a list, you should refer to Dictionary Sorting - #8 by Complextic to see how to sort it

3 Likes

Hmm… Can’t we just simply use table.sort() ? Also, what if we just need to find who’s in lead, like who is the most closest ? So your post isn’t that reliable. It just checks who has the highest number of checkpoint. He doesn’t want who has the highest number of checkpoints, because it would be a problem as well if two persons have same checkpoint.

You can use a table to store the players and their positions.

You can use a table to store the players and their positions.

local players = {}

local function addPlayer(player)
	players[player] = 0
end

local function removePlayer(player)
	players[player] = nil
end

local function updatePlayerPosition(player, position)
	players[player] = position
end

local function getPlayerPosition(player)
	return players[player]
end

You can then use a table to store the positions and the players in them.

local positions = {}

local function addPlayerToPosition(player, position)
	if positions[position] == nil then
		positions[position] = {}
	end

	table.insert(positions[position], player)
end

local function removePlayerFromPosition(player, position)
	if positions[position] == nil then
		return
	end

	for i, playerInPosition in ipairs(positions[position]) do
		if playerInPosition == player then
			table.remove(positions[position], i)
			break
		end
	end
end

local function getPlayersInPosition(position)
	return positions[position]
end

Please see the OP’s post. He wants to find the magnitude and this can be easily done using table.sort() I guess…

Stop using AI and please make your own posts.

:skull:

What software u using?

That’s what I plan on using, however I have very little to no experience with tables, I only know the basics such as insert, delete, floor, find, and a bit more. I would need help with sorting like you said and probably alot more.

It’s ez. Lemme explain

table.sort() sorts a table. You’ll easily understand with this example

local t = {1,2,3,4,5} --create a table

local function descending(a,b) --this is a function used to choose whethere to sort the table in ascending or descending
return a > b
end

table.sort(t, descending) --sorts it and the function determines whether it should sort in ascending or descending order.

for i, value in pairs(t) do
print(value) -- this will print as descending order as we sorted like that
end

Hm, alright I think I understand sorting, but how would I assign a player to a position though? Would I use a dictionary like fox said?

table.sort only works on lists/arrays, not dictionaries

also if you look at the post I linked, it explains how to use table.fort on a dictionary

if you’re wanting to use table.sort natively, there won’t be a way to add a key, you’re just going to be left with a sorted table of numbers without any way to tell which player it relates to

Easiest way to do this it to keep track of the current checkpoint and the amount of laps a player has completed.

You would want the checkpoints to be around 10 studs apart and follow the track.

You also need to track which checkpoint the player is currently at, as well as track how many laps a player has completed.

Next up you just need to write a simple loop that checks which player has Completed the most laps and is furthest along the list of Checkpoints.

Your post also don’t really explain the real problem OP have as I stated above. It just seems like promoting a topic here (sorry if I’m rude). It just tells about getting player’s checkpoint. What if many players have same checkpoint? Then how your code can work ? OP actually wants to get the magnitude…

Sorry, it’s my time to sleep, will tell you how to convert it into arrays.

For now just get the idea

local t = {}

for i=1, 7 do --example
t[i] = something --I'll tell the whole process tomorrow.
end

Till then, stay tuned!

local t = {}
local playertable = game.Players:GetPlayers()

for i=1, #game.Players:GetPlayers() do
t[i] = playertable[i]
end

function descending(a,b)
return a > b
end)

table.sort(t, descending)

That should pretty much do your work!

To determine the race position of players based on their progress through checkpoints, you can use a table to store the players’ distances from the checkpoints and then sort the table based on those distances. Here is an example code that can help you get started:

local checkpoints = game.Workspace.Checkpoints:GetChildren()

local playersDistances = {} -- table to store player distances from the checkpoints

-- function to update the playersDistances table
local function updatePlayerDistances()
for _, player in pairs(game.Players:GetPlayers()) do
local character = player.Character
if character then
local nearestDistance = math.huge
for _, checkpoint in ipairs(checkpoints) do
local distance = (character.HumanoidRootPart.Position - checkpoint.Position).Magnitude
if distance < nearestDistance then
nearestDistance = distance
end
end
playersDistances[player.Name] = nearestDistance
end
end
end

-- call updatePlayerDistances() every second
while wait(1) do
updatePlayerDistances()
end

-- function to sort the playersDistances table and print the race positions
local function printRacePositions()
local sortedPlayers = {}
for playerName, distance in pairs(playersDistances) do
table.insert(sortedPlayers, {playerName, distance})
end
table.sort(sortedPlayers, function(a, b) return a[2] < b[2] end)
for i, playerData in ipairs(sortedPlayers) do
local playerName, distance = playerData[1], playerData[2]
print("Player " .. playerName .. " is in " .. i .. " place with a distance of " .. distance)
end
end

-- call printRacePositions() every 10 seconds
while wait(10) do
printRacePositions()
end

This code updates the playersDistances table every second with the nearest checkpoint distance for each player, and then sorts the table every 10 seconds to print the race positions. Note that this is just a starting point and you may need to modify this code to fit your specific game needs.

So looking at the solutions here, they are somewhat confusing. Please allow me to shine some light on this issue.

Like OP said, he already knows how to sort players via magnitude. The real issue here is that he wants to add checkpoints on top of magnitude and probably loops too.

While provided solutions may work, they are unnecessarily complicated. Best way to go about this is to provide a custom sort function to table.sort:

-- keep all player data here
local Entries = {}

game.Players.PlayerAdded:Connect(function(plr)

   -- add a new entry when character is spawned
   plr.CharacterAdded:Connect(function(chr)

      table.insert(Entries,{
          player = plr,
          root = chr.HumanoidRootPart,
          checkpointRef = somePart, --add a starting checkpoint part here
          checkpointValue = 1 -- first checkpoint has a value of 1
          loop = 0, -- optional loops
      }
   end)
   -- remove an entry when player leaves or despawns
   plr.CharacterRemoving:Connect(function()
      for i, entry in pairs(Entries) do
         if entry.player == plr then
             table.remove(Entries,i)
             break
         end
      end
   end)

end)

-- adjust the checkpoint with this function
local function ChangeCheckpoint(plr,newCheckpoint,newCheckpointValue)
   for _, entry in pairs(Entries) do
      if entry.player == plr then
          entry.checkpointRef = newCheckpoint
          entry.checkpointValue = newCheckpointValue
          break
      end
   end
end
 
-- adjust the loop with this function
--EDIT: Corrected the name of the function
local function ChangeLoop(plr,newLoopValue)
   for _, entry in pairs(Entries) do
      if entry.player == plr then
          entry.loop = newLoopValue
          break
      end
   end
end

-- finally the sort function
local function CustomSort(entryA, entryB)
   --first check the loop 
   if entryA.loop ~= entryB.loop then
      -- players have different amount of loops, return the result
      return entryA.loop < entryB.loop
   -- if loop is the same, check if the checkpoints are different.
   elseif entryA.checkpointValue ~= entryB.checkpointValue then
      -- players have different checkpoints, return the result (each checkpoint has a different value)
      return entryA.checkpointValue < entryB.checkpointValue
   else
      -- finally if checkpoints are the same, see which player is further away from said checkpoint
      return (entryA.root.Position - entryA.checkpointRef.Position).Magnitude <
         (entryB.root.Position - entryA.checkpointRef.Position).Magnitude
   end
end

-- Example of sorting loop each second (adjust if needed)
task.defer(function()
   while true do
      task.wait(1) -- check every second
      table.sort(Entries,CustomSort)
      print(Entries)
   end
end)

EDIT: Corrected the name of the loop changing function

No? I don’t think though. OP isn’t really unsatisfied about my code.

I think you provided a big code which may do the work but quite difficult for OP to understand, as he stated he has a little knowledge of table.sort()