# Preventing character from leaving map boundary

I thought it would be fun to remake a .io game on Roblox as a break from my current project. It is pretty much complete, except I need to ensure the player can’t leave the playspace. To do this, I am trying to make it so they can touch the edge of the playspace, but they can’t go any further.

However, this is not exactly working as intended. Currently, there are three outcomes (none of which are what I expected):

A) https://gyazo.com/78a99985d94f5571e78309b876918117
The character gets sent to some magical realm (typically, it is -3000x, 3000x, -3000z, or 3000z).

B) https://gyazo.com/6032e43747aebda2468c618a868ca41a
The character boundary works as intended… except it is roughly 1/5 studs too far inside the play area.

C) https://gyazo.com/a3bbfd1ea0d10b86c5cf064f818895fe
This is the closest it gets. The character can’t go past the edge; However, it glitches rapidly between being on the edge as it should and the behavior described in point B.

NOTE: All of these outcomes are from the same code.

The following is the section of code responsible for calculating the movement (some parts were removed to skim it down to its bones for the sake of your sanity):

``````local StudsPerStep = 0.5
local LastUpdatedSize = 5

local function ReturnLargestNumber(a: number, b:number)
if a > b then
return a,b
elseif b > a then
return b,a
end

return a,b
end

local function CalculateDifference(a: number, b:number)
local largest, smallest = ReturnLargestNumber(a,b)

return largest - smallest
end

local function isNegative(n: number)
if n >= 0 then
return true
end

return false
end

local function CalculateMovementInBorders(position: Vector3, size: Vector3, rate: number, x: number, z:number)
if (x == 0) and (z == 0) then
return x, z
end

local positionX
local positionZ

if isNegative(position.X) then
positionX = position.X + (size.X / 2)
else
positionX = position.X - (size.X / 2)
end

if isNegative(position.Z) then
positionZ = position.Z + (size.Z / 2)
else
positionZ = position.Z - (size.Z / 2)
end

local calculatedX = positionX + x
local calculatedZ = positionZ + z

local insidePositiveX = if (calculatedX < 1024) then true else false
local insideNegativeX = if (calculatedX > -1024) then true else false
local insidePositiveZ = if (calculatedZ < 1024) then true else false
local insideNegativeZ = if (calculatedZ > -1024) then true else false

if insidePositiveX and insideNegativeX and insidePositiveZ and insideNegativeZ then
return x,z
end

local differenceX = 0
local differenceZ = 0

if (not insidePositiveX) or (not insideNegativeX) then
if isNegative(calculatedX) then
differenceX = CalculateDifference(calculatedX,-1024)
else
differenceX = CalculateDifference(calculatedX,1024)
end
end

if (not insidePositiveZ) or (not insideNegativeZ) then
if isNegative(calculatedZ) then
differenceZ = CalculateDifference(calculatedZ,-1024)
else
differenceZ = CalculateDifference(calculatedZ,1024)
end
end

local updatedX = if (insidePositiveX and insideNegativeX) then x else differenceX
local updatedZ = if (insidePositiveZ and insideNegativeZ) then z else differenceZ

return updatedX,updatedZ
end

RunService.RenderStepped:Connect(function()
if (not Character) or (not HumanoidRootPart) then
return
end

local x = 0
local z = 0

if Keys.W or Keys.Up then
x = x + StudsPerStep
end

if Keys.A or Keys.Left then
z = z - StudsPerStep
end

if Keys.S or Keys.Down then
x = x - StudsPerStep
end

if Keys.D or Keys.Right then
z = z + StudsPerStep
end

local CharacterCFrame = HumanoidRootPart.CFrame

x, z = CalculateMovementInBorders(CharacterCFrame.Position,HumanoidRootPart.Size,StudsPerStep,x,z)

if (x == 0) and (z == 0) then
if Tween then
Tween:Cancel()
Tween = nil
end
else
local CalculatedCFrame = CharacterCFrame + Vector3.new(x,0,z)

if CalculatedCFrame ~= CharacterCFrame then
CharacterCFrame = CalculatedCFrame

local difference = (CalculatedCFrame.Position - CharacterCFrame.Position).Magnitude

if Tween then
Tween:Cancel()
end

local properties = {CFrame = CalculatedCFrame}

Tween = TweenService:Create(HumanoidRootPart,TweenInfo.new(difference,Enum.EasingStyle.Linear,Enum.EasingDirection.InOut),properties)
Tween:Play()
end
end
end)
``````

NOTE: The playspace is a 2048 x 2048 area position at 0,0,0.

I am guessing it is something with my math, but I have been staring at this for too long and am clearly overlooking something. Any help is much appreciated.

I think you can simplify your `CalculateMovementInBorders` function a bit.

``````local MapSize = Vector3.new(2048, 0, 2048)
local MapCenter = Vector3.new(0, 0, 0)

local MapMinimumBounds = MapCenter - MapSize / 2
local MapMaximumBounds = MapCenter + MapSize / 2

local function CalculateMovementInBorders(position: Vector3, size: Vector3, movement_x: number, movement_z: number)
local half_size = size / 2

local movement_vector = Vector3.new(movement_x, 0, movement_z)

-- Get the next position with the movement vector applied.
local movement_position = position + movement_vector

-- Find the maximum and minimum map boundaries relative to the character size.
local minimum_bound_size_offset = MapMinimumBounds + half_size
local maximum_bound_size_offset = MapMaximumBounds - half_size

-- Get the position clamped by the maximum and minimum boundaries relative to the character size.

-- Get the relative movement vector.

end
``````

In my mind something like this would work pretty well. I tested it a bit in my own place and from what I could tell it works correctly.

Note that I removed the `rate` parameter, I didn’t see it being used anywhere in the function.

I used a few built-in functions for getting the minimum and maximum vectors, see
Vector3 | Roblox Creator Documentation Max and Min on Vector3s.

Here’s some test data I put in to see how things were looking.

``````-- Should be -1, -1 (technically already outside of the map boundaries.)
print(CalculateMovementInBorders(Vector3.new(1020, 0, 1020), Vector3.new(10, 0, 10), 10, 10))

-- Should be 9, 9 since we can only move 9 studs until we hit the map boundary.
print(CalculateMovementInBorders(Vector3.new(1010, 0, 1010), Vector3.new(10, 0, 10), 10, 10))

-- Should be -9, 10 since we'll hit the minimum boundary on the X axis.
print(CalculateMovementInBorders(Vector3.new(-1010, 0, -1010), Vector3.new(10, 0, 10), -10, 10))

-- Should be -9, -9 since we'll hit the minimum boundary on the X and Z axis.
print(CalculateMovementInBorders(Vector3.new(-1010, 0, -1010), Vector3.new(10, 0, 10), -10, -10))

-- Should be 0, 0 since we're not moving at all.
print(CalculateMovementInBorders(Vector3.new(-1010, 0, -1010), Vector3.new(10, 0, 10), 0, 0))

-- Should be -9, 9 since we'll be running into the top left corner.
print(CalculateMovementInBorders(Vector3.new(-1010, 0, 1010), Vector3.new(10, 0, 10), -10, 10))

-- Should be 9, -9 since we'll be running into the bottom right corner.
print(CalculateMovementInBorders(Vector3.new(1010, 0, -1010), Vector3.new(10, 0, 10), 10, -10))

-- Should be 10, -10 since we aren't hitting any boundaries.
print(CalculateMovementInBorders(Vector3.new(0, 0, 0), Vector3.new(10, 0, 10), 10, -10))
``````

Results:

``````  > -1 -1
>  9 9
>  -9 10
>  -9 -9
>  0 0
>  -9 9
>  9 -9
>  10 -10
``````
1 Like

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