Is it possible to "simulate" AbsolutePosition/Size?

I’m working on a mini horse race game using UI elements, and I need to calculate the winner with high precision. Initially, I did this on the server by checking if the frames were touching each other using their absolute position and size, but I was tweening everything on the server, which caused lag. To solve this, I decided to send the X position of the horses from the server to the client, which helped reduce the lag. However, there’s a problem now: since I moved the animations to the client, it became harder to determine if the frames are touching. I thought about creating invisible frames with the exact size of the original ones and comparing their absolute positions, but this solution feels inefficient.

Also, I need to clarify some important things. The frames I want to compare are not in the same frame. To be more specific, I’ll explain the way I was doing before. There are two frames parented to a main frame: HorseFrame (which haves all the horses) and BackgroundFrame (which haves the finish line). Both BackgroundFrame and HorseFrame are animated. If the horses were in the same frame, I could easily compare the scale values, but since they’re not, I had to compare the absolute positions and sizes. Here’s the code I used:

function RaceLogic.IsFrameTouching(frame1, frame2)
	local abs1Pos = frame1.AbsolutePosition
	local abs1Size = frame1.AbsoluteSize
	local abs2Pos = frame2.AbsolutePosition
	local abs2Size = frame2.AbsoluteSize

	local xOverlap = abs1Pos.X < abs2Pos.X + abs2Size.X and 
		abs1Pos.X + abs1Size.X > abs2Pos.X

	local yOverlap = abs1Pos.Y < abs2Pos.Y + abs2Size.Y and 
		abs1Pos.Y + abs1Size.Y > abs2Pos.Y

	return xOverlap and yOverlap
end

This worked fine until I decided to do animations on client (the best decision I made). Everything I could think of was trying to simulate the AbsolutePosition and AbsoluteSize. So, any suggestions?

Just to clarify:

  • Is the IsFrameTouching code done on the server side or the client side?

Server side. The server decides the position of the horses and the client animates it. I am also tweening a NumberValue on the server to keep track of the horses position in real time without actually tweening the horses (I don’t think this is good, because it is still tweening something).

I’ve managed to do the thing I want in a very “beautiful” way. It is definitely not the best way to do it, but it was the way I found to do it. I hope someone sees this and thinks in a better way to do what I need ;(

EDIT: Yeah, this method is definitely not the best and not precise.

	-- Convert position to absolute
	local absolutePosition = Vector2.new(
		position.X.Scale * parentAbsoluteSize.X + position.X.Offset,
		position.Y.Scale * parentAbsoluteSize.Y + position.Y.Offset
	)

	-- Apply AnchorPoint to the position
	local adjustedPosition = Vector2.new(
		parentAbsolutePosition.X + absolutePosition.X - (size.X.Scale * parentAbsoluteSize.X + size.X.Offset) * anchorPoint.X,
		parentAbsolutePosition.Y + absolutePosition.Y - (size.Y.Scale * parentAbsoluteSize.Y + size.Y.Offset) * anchorPoint.Y
	)

	-- Convert size to absolute
	local absoluteSize = Vector2.new(
		size.X.Scale * parentAbsoluteSize.X + size.X.Offset,
		size.Y.Scale * parentAbsoluteSize.Y + size.Y.Offset
	)

	return adjustedPosition, absoluteSize
end```

```lua
function RaceLogic.IsFrameTouching(info)
	local abs1Pos = info[1]
	local abs1Size = info[2]
	local abs2Pos = info[3]
	local abs2Size = info[4]

	local xOverlap = abs1Pos.X < abs2Pos.X + abs2Size.X and 
		abs1Pos.X + abs1Size.X > abs2Pos.X

	local yOverlap = abs1Pos.Y < abs2Pos.Y + abs2Size.Y and 
		abs1Pos.Y + abs1Size.Y > abs2Pos.Y

	return xOverlap and yOverlap
end

function RaceLogic.CheckWinner()
	local horses = RaceLogic.Horses
	
	local leader = horses[1]
	local maxX = horses[1].Object.Xcoordinate.Value
	local Xcoordinate 
	for _, horse in pairs(horses) do
		Xcoordinate = horse.Object.Xcoordinate
		if Xcoordinate.Value > maxX then
			maxX = Xcoordinate.Value
			leader = horse
		end
	end
	
	local horse = leader.Object
	
	local bg_absolutePos, bg_absoluteSize = RaceLogic.ConvertUDim2ToAbsolute(
		UDim2.new(BackgroundFrame.Xcoordinate.Value, BackgroundFrame.Position.X.Offset, BackgroundFrame.Position.Y.Scale, BackgroundFrame.Position.Y.Offset),
		BackgroundFrame.Size,
		BackgroundFrame.AnchorPoint,
		GameFrame.AbsolutePosition,
		GameFrame.AbsoluteSize
	)
	
	local finish_absolutePos, finish_absoluteSize = RaceLogic.ConvertUDim2ToAbsolute(
		FinishLine.Position,
		FinishLine.Size,
		FinishLine.AnchorPoint,
		bg_absolutePos,
		bg_absoluteSize
	)
	
	local hf_absolutePos, hf_absoluteSize = RaceLogic.ConvertUDim2ToAbsolute(
		UDim2.new(HorseFrame.Xcoordinate.Value, HorseFrame.Position.X.Offset, HorseFrame.Position.Y.Scale, HorseFrame.Position.Y.Offset),
		HorseFrame.Size,
		HorseFrame.AnchorPoint,
		GameFrame.AbsolutePosition,
		GameFrame.AbsoluteSize
	)
	
	local h_absolutePos, h_absoluteSize = RaceLogic.ConvertUDim2ToAbsolute(
		UDim2.new(horse.Xcoordinate.Value, horse.Position.X.Offset, horse.Position.Y.Scale, horse.Position.Y.Offset),
		horse.Size,
		horse.AnchorPoint,
		hf_absolutePos,
		hf_absoluteSize
	)
	
	if RaceLogic.IsFrameTouching({h_absolutePos, h_absoluteSize, finish_absolutePos, finish_absoluteSize}) then
		return leader 
	end
	
	return nil 
end```