According to the title, I am attempting to replicate the “strafing” system of that in Elden Ring.
In Elden Ring, typically, enemies with large shields will strafe around you in a circle-like pattern until attacking.
I’m absolutely HORRID at math, so the best I could do is this.
run_service = game:GetService("RunService")
--^ services
ai = script.Parent
target = workspace.FakePlayer
face_point = ai.HumanoidRootPart.FacePoint -- align orientation
--^ essentials
start_point = nil
last_strafe = "-x"
--^ other
strafe_pause = 0
radius = 10
--^ settings
run_service.Heartbeat:Connect(function(delta_time)
face_point.CFrame = CFrame.lookAt(
ai.PrimaryPart.Position, Vector3.new(
target.PrimaryPart.Position.X,
ai.PrimaryPart.Position.Y,
target.PrimaryPart.Position.Z
)
)
if (target.PrimaryPart.Velocity.Magnitude) < 1 and strafe_pause <= 0 then -- target is standing still, wait for opening
start_point = target.PrimaryPart.CFrame * CFrame.new(0, 0, -10)
local strafe_distance
strafe_pause = 1
--^ strafe data
if last_strafe == "-x" then
last_strafe = "x"
strafe_distance = math.random(-10, -8)
else
last_strafe = "-x"
strafe_distance = math.random(8, 10)
end
local end_pos = start_point * CFrame.new(strafe_distance, 0, 0)
end_pos = end_pos.Position
--^ strafe randomizer to avoid repetition
ai.Humanoid:MoveTo(
end_pos
)
ai.Humanoid.MoveToFinished:Wait()
--^ strafing
strafe_pause = 0
end
end)
As you can see, it strafes left to right based off of the direction that the player is facing. This is obviously very flawed. What I want to achieve, is, to imagine a “circle” around the player, pick the nearest point, then start to follow that imaginary circle (slowly).
This sounds too complicated for my small brain. Is anybody able to assist?
---> Imports
local values = require(script.Parent.values)
local visualize = require(script.visualize_vertex)
---> Config
local data = {
["vertex_count"] = 50;
["visualize_path"] = true;
}
local blacklist = {}
---> Strafe function
local function strafe(humanoid: Humanoid, target: Model, ai: Model)
values.strafe = true -- Start strafe
local connection, reached
local npc_position = humanoid.Parent.HumanoidRootPart.Position
local nearest_distance = math.huge
local nearest_vertex = 1
local last_target_position = target.HumanoidRootPart.Position
local initial_distance = (npc_position - target.HumanoidRootPart.Position).Magnitude
-- Find nearest vertex
for i = 1, data.vertex_count do
local angle = math.pi * 2 * (i / data.vertex_count)
local direction = Vector3.new(math.cos(angle), 0, math.sin(angle))
local pos = target.HumanoidRootPart.Position + (direction * initial_distance)
local distance = (npc_position - pos).Magnitude
if table.find(blacklist, i) then continue end
if distance < nearest_distance then
nearest_distance = distance
nearest_vertex = i
end
end
-- Move between vertices
if values.strafe == nil then return end
local current_vertex = nearest_vertex
if table.find(blacklist, current_vertex) then return end
local current_distance = (humanoid.Parent.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude
local angle = math.pi * 2 * current_vertex / data.vertex_count
local direction = Vector3.new(math.cos(angle), 0, math.sin(angle))
local pos = target.HumanoidRootPart.Position + (direction * current_distance)
if data.visualize_path then visualize(pos) end -- Visualize path
humanoid:MoveTo(pos)
connection = humanoid.MoveToFinished:Connect(function()
reached = true
connection:Disconnect()
end)
local random = math.random(math.floor(data.vertex_count/5), data.vertex_count)
table.insert(blacklist, current_vertex)
if #blacklist >= random then
table.clear(blacklist)
end
while reached ~= true do
task.wait()
if values.strafe == nil then return end
end
values.strafe = nil -- End strafe
end
return strafe
Just like in theory, It creates an imaginary circle around a stationary player, (resetting it each time they move), and follows the closest vertexes of that circle, increases the index by 1, and the next time the function is called, it repeats. Hope this helps for anyone else.