Automate finding nearest edge of part

without getting super math heavy (because, will it always be a circle or do you want any arbitrary shape)
what you could do is something like this (forgive the random sizes, assume these are all just parts in the pink color
image

and the idea is when the npc is hitting the player, to find the closest node (pink) and hit towards that node. This would work even on square or custom shaped-maps

this doesn’t take advantage of any geometry though, but should be decently efficient. If you know you’re always going to be in a circle, you could consider the angle and distance from the center of the stage
image

knowing that angle means that you would probably just want to hit towards the same angle, as a straight line to the edge is probably going to be much closer than a curved one. You could make it feel a bit more “human” if you consider the distance too, where if the NPC is close to the center of the stage, just hit in a random direction until you feel like they’re close enough to start pushing to the edge.

Divide the distance from the circle’s center to the NPC by the radius. This will give you the percentage of how far the NPC has moved away from the circle.
Then you need to understand at what speed the refinery can move the player if he stands in the center of the circle and multiply by a percentage

local radius = 10
local magnitude = (Body.PrimaryPart.Position - Player.HumanoidRootPart.Position).Magnitude
local percentage = 1 - radius / magnitude 
local power = 1000000 * percentage 

maybe you could try using a pathfinding system / voxel grid to detect the edge of a part?
You could run something like that in a loop and update it every 5 seconds or so to save on performance

this was my sort of “last resort” plan if a system could not be found
and will be used if no optimized method could be found

thats a good idea for automating it, i already have a pathfinding system that can detect edges to trigger pathfinding and etc

using such system with a voxel map of the desireed part is a good idea, ill see what i can do with that
(if i can figure out how to make a voxel map of course)

I’ve used something like this for an audio muffling system. It’s very efficient and barely effects performance at all. Voxel grids are a little complicated to make, but it’s worth it in my opinion.

ive had massive struggles with voxel grids before as i never could figure out how to make one

(was originally making one for a randomly generated backrooms i was going to make, but gave up due to not figuring it out)

perhaps to speed up the process, do you have any information to share on how to create a voxel grid?

If your maps could potentially be any shape, an intuitive and not (too) costly solution would be to create “barriers” for each map, parts that surround it on the edges. When the player is hit by an enemy, to get the knockback direction, loop through the list of barriers and call :GetClosestPointOnSurface() for each one. Whichever returned point is closest is where the knockback for the player should be directed.

This does take a bit more manual work of placing parts around the edges, but it’s easy to understand and code, and it’s not too much of a hassle. Plus you get concavities for free, since you’re specifying where players should be knocked back to, meaning you can have pits in the middle of your map.

I can try to explain it

Make a preset grid. Think of the Default Baseplate grid, you know how it has those box texture things in it? Think of those as your grids

Put that in a distance you’re comfortable with, and then make parts in a and put in in something like 10x1x10 grid (x,y,z)
Heres a piece of code you can put in the command line to get an example of what im talking about cuz im not good with words lol

local gridSize = 10  -- Size of the grid (10x10x10)
local voxelSizeXZ = 4  -- Size of each voxel on X and Z axes
local voxelSizeY = 1  -- Size of each voxel on Y axis
local offset = voxelSizeXZ / 2  -- Offset to center the grid

for x = 1, gridSize do
    for y = 1, gridSize do
        for z = 1, gridSize do
            local voxel = Instance.new("Part")
            voxel.Size = Vector3.new(voxelSizeXZ, voxelSizeY, voxelSizeXZ)  -- Y is 1 stud
            voxel.Position = Vector3.new(
                x * voxelSizeXZ - offset,
                y * voxelSizeY,  -- Y position is based on voxelSizeY
                z * voxelSizeXZ - offset
            )
            voxel.Anchored = true
            voxel.Parent = workspace
        end
    end
end

print("Voxel grid generated with Y = 1 stud")

maybe you can use raycasts and cast directly down from the grid and match it to the nearest edge of a part? Or you can use this grid for the pathfind, which is what i would do.

Also, using grids for your NPC system would make it much more precise. Think of this grid as echolocation for the NPC, you can detect parts inside of the grid if you wanted to on a precise level compared to using something like magnitude and distance.

i guess this would work too. I didn’t know that was even a thing xd

Even if it weren’t a thing at the engine level, you could implement it yourself! Here’s a post from 2018 when it didn’t exist, it’s some pretty cool math!

1 Like

i have some issues with this

  1. this causes some MAJOR lag with higher numbers, and with my map being relativly large, it lags alot. so maybe adding offset in between the grid parts will be required, it’ll be less accurate, but hopefully alot less laggy
  2. i need a pivot point where the grid is generated from so it can be generated around the npc

i apologise for not being to experienced in this

heres my slightly altered code since it only needs to be on 1 y

local gridSize = 500  -- Size of the grid (10x10x10)
local voxelSizeXZ = 1  -- Size of each voxel on X and Z axes
local voxelSizeY = 1  -- Size of each voxel on Y axis
local offset = voxelSizeXZ / 2  -- Offset to center the grid
local PivotPoint = Vector3.new(0,50,0)

for x = 1, gridSize do
		for z = 1, gridSize do
			local voxel = Instance.new("Part")
			voxel.Size = Vector3.new(voxelSizeXZ, voxelSizeY, voxelSizeXZ)  -- Y is 1 stud
			voxel.Position = Vector3.new(
				x * voxelSizeXZ - offset,
				PivotPoint.Y,  -- Y position is based on voxelSizeY
				z * voxelSizeXZ - offset
			)
			voxel.Anchored = true
			voxel.Parent = workspace
		end
end

this would be good for simple maps that forever stay the same, but with player input the map can change quite a bit, but this is something i didnt actually know and mgith be pretty handy in the future!

1 Like

It was meant to be an example of what it would look like. I’d recommend building your own grid and fine tuning it. It doesn’t need to expand the entire map but rather just a certain radius (i think anyway xd) Also I’m pretty sure you don’t need to use parts for this, you can use attachments to get the positions of the grid too

that also works, also i jsut figured out how the offsets worked, i jsut wanted the displays to be 1 stud nto realising that it also controls the offsets, so i can do that

but for the raidius thing,
that might have to be used, and with the suggestion from one of the posts above saying to make it only aim for edge if its sort of close, its also a pretty good idea for that, ill see what i can do with this and report back, tho i still have 0 clue on how to put a pivot point for the grid, but ill try and figure it out


Heres an example

im struggling with being able to position the center of the grid, how would i do this?

(heres what i did, making the position in the corner)

local gridSize = 50  -- Size of the grid (10x10x10)
local voxelSizeXZ = 5  -- Size of each voxel on X and Z axes
local offset = voxelSizeXZ / 2  -- Offset to center the grid
local PivotPoint = Vector3.new(54.863, 775.262, -57.502)

for x = 1, gridSize do
		for z = 1, gridSize do
			local voxel = Instance.new("Part")
			voxel.Size = Vector3.new(1,1,1)  -- Y is 1 stud
			voxel.Position = Vector3.new(
				(x * voxelSizeXZ - offset)+PivotPoint.X,
				PivotPoint.Y,
				(z * voxelSizeXZ - offset)+PivotPoint.Z
			)
			voxel.Anchored = true
			voxel.Parent = workspace
		end
end

to center it u need to adjust the position calculation is relative to the pivot point

The issue is the grid starts at 1,1 and extends to (gridsize, gridsize) which places the grid in the corner rather than centered around the PivotPoint

Try this;

local gridSize = 50  -- Size of the grid (50x50)
local voxelSizeXZ = 5  -- Size of each voxel on X and Z axes
local PivotPoint = Vector3.new(54.863, 775.262, -57.502)

-- Calculate the total size of the grid
local totalGridSizeXZ = gridSize * voxelSizeXZ

-- Calculate the offset to center the grid
local offsetXZ = totalGridSizeXZ / 2

for x = 1, gridSize do
    for z = 1, gridSize do
        local voxel = Instance.new("Part")
        voxel.Size = Vector3.new(1, 1, 1)  -- Y is 1 stud
        
        -- Adjust the position to center the grid around the PivotPoint
        voxel.Position = Vector3.new(
            PivotPoint.X + (x * voxelSizeXZ - offsetXZ),
            PivotPoint.Y,
            PivotPoint.Z + (z * voxelSizeXZ - offsetXZ)
        )
        
        voxel.Anchored = true
        voxel.Parent = workspace
    end
end

also you can put all of the parts in a model if you havent already

This actually works great!
im goign to head off to bed since its getting late, but here is the code i have used, and i will put it to use when i can!

local gridSize = 50  -- Size of the grid (50x50)
local voxelSizeXZ = 5  -- Size of each voxel on X and Z axes
local PivotPoint = Vector3.new(54.863, 775.262, -57.502)

-- Calculate the total size of the grid
local totalGridSizeXZ = gridSize * voxelSizeXZ

-- Calculate the offset to center the grid
local offsetXZ = totalGridSizeXZ / 2


local function IsLocationTravelable(location:Vector3)

	local rayinfo = RaycastParams.new()
	rayinfo.FilterType = Enum.RaycastFilterType.Exclude
	rayinfo.RespectCanCollide = true
	--rayinfo.FilterDescendantsInstances = char:GetDescendants()
	rayinfo.CollisionGroup = "Default"

	local info = workspace:Raycast(location,Vector3.new(0,-20,0),rayinfo)

	if info then
		return info.Instance
	end
	return false
end

for x = 1, gridSize do
	for z = 1, gridSize do
		local pos = Vector3.new(
			PivotPoint.X + (x * voxelSizeXZ - offsetXZ),
			PivotPoint.Y,
			PivotPoint.Z + (z * voxelSizeXZ - offsetXZ)
		)
		
		if not IsLocationTravelable(pos) then
		local voxel = Instance.new("Part")
		voxel.Size = Vector3.new(1, 1, 1)  -- Y is 1 stud

		-- Adjust the position to center the grid around the PivotPoint
		voxel.Position = pos

		voxel.Anchored = true
		voxel.Parent = workspace
		end
	end
end

Thank you so much!

no problem :slight_smile: im happy this worked for u :sparkling_heart:

1 Like