Make squirrel face where its moving

local squirrel = script.Parent
local humanoid = squirrel:FindFirstChild(“Humanoid”)
local rootPart = squirrel:FindFirstChild(“HumanoidRootPart”) – Ensure the correct root part
local biome = “forestrange” – The folder name (e.g., “forestrange”)
local speedStat = squirrel:FindFirstChild(“stats”):FindFirstChild(“speed”)

– Create a small arrow part to represent the forward direction
local arrow = Instance.new(“Part”)
arrow.Size = Vector3.new(1, 1, 5) – Make it long and thin like an arrow
arrow.Color = Color3.fromRGB(255, 0, 0) – Red color to easily spot
arrow.Anchored = true
arrow.Position = rootPart.Position + rootPart.CFrame.LookVector * 5 – Position it in front of the root part
arrow.Parent = workspace
arrow.CanCollide = false

– Make the arrow always face the front of the model
game:GetService(“RunService”).Heartbeat:Connect(function()
arrow.CFrame = CFrame.new(rootPart.Position, rootPart.Position + rootPart.CFrame.LookVector)
end)

if not humanoid or not rootPart then
warn(“Squirrel is missing a Humanoid or HumanoidRootPart!”)
return
end

rootPart.Anchored = false

– Find the area part in workspace based on the folder name
local areaPart = workspace:FindFirstChild(biome)
if not areaPart or not areaPart:IsA(“Part”) then
warn("No valid part found for biome: " … biome)
return
end

local areaCenter = areaPart.Position – Set the center of the area based on the part’s position
local areaSize = areaPart.Size – Size of the area (using the part’s size)

– Ensure speed is available, otherwise default to a reasonable value
local speed = speedStat and speedStat.Value or 16 – Default speed is 16

– Function to get a random position within the area bounds
local function getRandomPosition()
– Generate random offsets within the bounds of the area
local offsetX = math.random(-areaSize.X / 2, areaSize.X / 2)
local offsetZ = math.random(-areaSize.Z / 2, areaSize.Z / 2)
local newPos = areaCenter + Vector3.new(offsetX, 0, offsetZ)

-- Clamp the position to stay within bounds (if needed)
local clampedX = math.clamp(newPos.X, areaCenter.X - areaSize.X / 2, areaCenter.X + areaSize.X / 2)
local clampedZ = math.clamp(newPos.Z, areaCenter.Z - areaSize.Z / 2, areaCenter.Z + areaSize.Z / 2)

-- Ensure the Y position stays at ground level (raycasting for ground height)
local rayStart = Vector3.new(clampedX, 1000, clampedZ)
local rayEnd = Vector3.new(clampedX, -1000, clampedZ)
local raycastResult = workspace:Raycast(rayStart, rayEnd - rayStart)

local newY = raycastResult and raycastResult.Position.Y or areaCenter.Y

return Vector3.new(clampedX, newY, clampedZ)

end

– Function to make the squirrel face the target position directly
– Function to make the squirrel face the target position directly with a 90-degree offset
local function faceTargetPosition(targetPosition)
– Calculate the direction vector to the target position
local direction = (targetPosition - rootPart.Position).unit – Get the normalized direction vector

-- Create a new CFrame that faces the target direction
local lookAtCFrame = CFrame.lookAt(rootPart.Position, targetPosition)

-- Apply a 90-degree rotation (in radians) to the CFrame on the Y-axis
local adjustedCFrame = lookAtCFrame * CFrame.Angles(0, math.rad(90), 0)  -- Rotate by 90 degrees on the Y-axis

-- Apply the new orientation to the root part
rootPart.CFrame = adjustedCFrame

end

– Function to move the squirrel to a random position
local function moveToRandomPosition()
local targetPosition = getRandomPosition()

-- First, face the target position directly
faceTargetPosition(targetPosition)

-- Move the squirrel to the target position
humanoid:MoveTo(targetPosition)

-- Set the humanoid's walk speed
humanoid.WalkSpeed = speed

-- Wait until the squirrel finishes moving
humanoid.MoveToFinished:Wait()
print("Reached position:", targetPosition)

end

– Main loop to keep the squirrel moving
while true do
moveToRandomPosition()
wait(math.random(2, 5)) – Wait before picking a new target
end
this is my script. I want to attempt to make the squirrel face where its moving, but it always seems to face 90 degrees off the direction its moving, aka the temporary arrow part, no matter how much I try to adjust it. ive tried everything my somewhat limited scripting knowledge can think of. My guess is the orientation with the mesh (which I didnt make) was wrong in blender or wherever it was made, but I highly doubt thats impossible to fix. I’d just appreciate some help.

1 Like

uh can u send all of it as one script?

I can try…

local squirrel = script.Parent
local humanoid = squirrel:FindFirstChild("Humanoid")
local rootPart = squirrel:FindFirstChild("HumanoidRootPart") -- Ensure the correct root part
local biome = "forestrange" -- The folder name (e.g., "forestrange")
local speedStat = squirrel:FindFirstChild("stats"):FindFirstChild("speed")

if not humanoid or not rootPart then
	warn("Squirrel is missing a Humanoid or HumanoidRootPart!")
	return
end

rootPart.Anchored = false

-- Find the area part in workspace based on the folder name
local areaPart = workspace:FindFirstChild(biome)
if not areaPart or not areaPart:IsA("Part") then
	warn("No valid part found for biome: " .. biome)
	return
end

local areaCenter = areaPart.Position -- Set the center of the area based on the part's position
local areaSize = areaPart.Size -- Size of the area (using the part's size)

-- Ensure speed is available, otherwise default to a reasonable value
local speed = speedStat and speedStat.Value or 16 -- Default speed is 16

-- Function to get a random position within the area bounds
local function getRandomPosition()
	-- Generate random offsets within the bounds of the area
	local offsetX = math.random(-areaSize.X / 2, areaSize.X / 2)
	local offsetZ = math.random(-areaSize.Z / 2, areaSize.Z / 2)
	local newPos = areaCenter + Vector3.new(offsetX, 0, offsetZ)

	-- Clamp the position to stay within bounds (if needed)
	local clampedX = math.clamp(newPos.X, areaCenter.X - areaSize.X / 2, areaCenter.X + areaSize.X / 2)
	local clampedZ = math.clamp(newPos.Z, areaCenter.Z - areaSize.Z / 2, areaCenter.Z + areaSize.Z / 2)

	-- Ensure the Y position stays at ground level (raycasting for ground height)
	local rayStart = Vector3.new(clampedX, 1000, clampedZ)
	local rayEnd = Vector3.new(clampedX, -1000, clampedZ)
	local raycastResult = workspace:Raycast(rayStart, rayEnd - rayStart)

	local newY = raycastResult and raycastResult.Position.Y or areaCenter.Y

	return Vector3.new(clampedX, newY, clampedZ)
end

-- Function to make the squirrel face the target position
local function faceTargetPosition(targetPosition)
	-- Calculate the direction on the XZ plane
	local direction = (targetPosition - rootPart.Position)
	local flatDirection = Vector3.new(direction.X, 0, direction.Z).Unit -- Ignore Y-axis

	-- Update the squirrel's orientation
	rootPart.CFrame = CFrame.lookAt(rootPart.Position, rootPart.Position + flatDirection)
end

-- Function to move the squirrel to a random position
local function moveToRandomPosition()
	local targetPosition = getRandomPosition()

	-- Orient the squirrel before moving
	faceTargetPosition(targetPosition)

	-- Set the humanoid speed
	humanoid.WalkSpeed = speed

	-- Move the squirrel to the target position
	print("Moving to:", targetPosition)
	humanoid:MoveTo(targetPosition)

	-- Wait until the squirrel finishes moving
	humanoid.MoveToFinished:Wait()
	print("Reached position:", targetPosition)
end

-- Main loop to keep the squirrel moving
while true do
	moveToRandomPosition()
	wait(math.random(2, 5)) -- Wait before picking a new target
end

Hard to help this kind as we don’t have your objects. If you know where your problem is make a short script easy to replicate with that chunk of code…

my untested guess.

local function faceTargetPosition(targetPosition)
	local direction = targetPosition - rootPart.Position
	if direction.Magnitude > 0 then
		rootPart.CFrame = CFrame.lookAt(rootPart.Position, 
			rootPart.Position + Vector3.new(direction.X, 0, direction.Z).Unit)
	end
end

tried it. see, I THINK the issue is that the squirrel model itself has its “front” 90 degrees off its actual orientation. here, ill send a video. before taking this video I swiped the faceTargetPosition function with yours. it did basically the same thing as it did before.

Ok… well im trying to send a video but it keeps giving me an error.

Can’t go off untested code. Consider it a blind guess. I was trying to account for not moveing at all.
Let me look around at my programs for some type of a example facing chunk.

local currentLocation = --> current location
local nextLocation = --> next location

local cf = CFrame.lookAt(currentLocation, nextLocation)

Easiest way I know of. Also you may be able to use the Top Menu/Model/Edit Pivot.

im realatively new. you’ll need to explain partially how to implement this correctly.

LookAt returns a CFrame positioned on the first argument and rotated towards the second argument. This may not work in your case … Are you telling me it is working but it is facing 90 degrees off constantly?

Trying to force the looking direction…

local npc = script.Parent
local targetPosition = Vector3.new(10, 0, 10) -- replace with the spot you're moving to.

local direction = (targetPosition - npc.Position).unit
npc.CFrame = CFrame.new(npc.Position, npc.Position + direction)

yes I am pretty sure the model is facingg io degrees off constantly.