Automate finding nearest edge of part

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

Im attempting to make a super advanced npc to combat players when their alone.
what im trying to have it do is attack the players torwards the edge to knock them off, instead of just hitting them in the direction they came from

bad diagram:
red = npc
blue = player
yellow = desired edge position

  1. What is the issue? Include screenshots / videos if possible!
    i have very little idea on how i’d make an optomized version of this
    i do have a function to detect an edge, but using ti would mean i’d have to spam it in circles, (massivly unoptomized)

  2. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    nothing shows up about this is the dev forum.
    i have sort of tried the function i listed above, but as stated, it would cause major server lag

any ideas/help are taken!
feel free to assist in any way
and ask questions

1 Like

You can detect if the player is at the edge of a part by using raycasting.

print("Hello world!")

local plr = game:GetService("Players").LocalPlayer
local character = plr.Character or plr.CharacterAdded:Wait()
local hum = character:WaitForChild("Humanoid")
local hrp = character:WaitForChild("HumanoidRootPart")
while true do
	task.wait(0.1)
	local RayStart = hrp.Position + hrp.CFrame.LookVector * 5-- 5 studs ahead of player
local RayDirection = -hrp.CFrame.UpVector*1 -- tell the ray to go straight down
local RayResult = workspace:Raycast(RayStart, RayDirection * 10) -- We ray 10 studs downwards
print(RayResult)
if not RayResult and hum.FloorMaterial ~= Enum.Material.Air then
	-- player is at the edge!!!
print("player at edge of part :)")
end
end

Try putting this in a render stepped loop of some sort. Put this in a local script
hope this helps :slight_smile:

this is not what i asked, i asked to “find the position of the nearest edge”
to use as base lien for where to go

let me explain in more detail.

the goal of said npc is to hit the player off the map,
so im trying to find the position of the nearest edge in an optomized matter so the npc knows what direction to hit torwards rather then hitting in a random direction

if you know the center of the map, just have the npc hit away from the center of the map

thats a good idea for this purpose, but i was also hoping to use a system like this to make it able to avoid the edge alot better, so a system as i described will still be greatly appreciated, but meanwhile i will do this

Sorry, i misread the title.
You could use raycasting to find the nearest edge of the part, and you can cast rays in multiple directions from the NPCs position to find the cloest edge

Once you have the nearest edge you can calculate the drection vector from the npc to the edge and use this drection vector to determine the direction in which the npc should attack the player

What you could do is you could make a preset list of ray directions like this

local raycastDirections = {
    Vector3.new(1, 0, 0),
    Vector3.new(-1, 0, 0),
    Vector3.new(0, 0, 1),
    Vector3.new(0, 0, -1),
    Vector3.new(1, 0, 1).unit,
    Vector3.new(-1, 0, 1).unit,
    Vector3.new(1, 0, -1).unit,
    Vector3.new(-1, 0, -1).unit
}

here are some functions that use this

local function findNearestEdge()
    local nearestEdgePosition = nil
    local shortestDistance = math.huge

    for _, direction in pairs(raycastDirections) do
        local ray = Ray.new(npc.Position, direction * 100)
        local hit, position = workspace:FindPartOnRay(ray, npc)

        if hit then
            local distance = (position - npc.Position).magnitude
            if distance < shortestDistance then
                shortestDistance = distance
                nearestEdgePosition = position
            end
        end
    end

    return nearestEdgePosition
end

local function attackTowardsEdge(player)
    local edgePosition = findNearestEdge()
    if edgePosition then
        local direction = (edgePosition - npc.Position).unit
        -- Apply force or move NPC towards the edge to attack the player
        -- Example: Apply a force to the player in the direction of the edge
        local playerCharacter = player.Character
        if playerCharacter then
            local humanoidRootPart = playerCharacter:FindFirstChild("HumanoidRootPart")
            if humanoidRootPart then
                local bodyVelocity = Instance.new("BodyVelocity")
                bodyVelocity.Velocity = direction * 50 -- Adjust the force as needed
                bodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
                bodyVelocity.Parent = humanoidRootPart
                game:GetService("Debris"):AddItem(bodyVelocity, 0.1)
            end
        end
    end
end

Although it’s a little basic, maybe this could give you an idea of how it would look

that is how i imagined a very basic system of it, but with the map using mosts circles, it wouldnt be as great for alot of situations, and might not work well either if it collides with a bridge to another part of the map

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