Edit: I am realizing now the pupil only goes out of bounds when the player is facing the cookie it is looking at from a certain angle, I still have not found a solution yet
What do you want to achieve?
I’m trying to keep the eye pupils of my character inside the bounds I’ve made for them to move around in. The character is a skinned mesh with only bones for the pupils to move.
What is the issue?
My issue is keeping the eye pupils of my custom starter character inside the bounds I have created. It seems to directly border the bounds on the front and back face instead while the other edges of the boundary work fine.
What solutions have you tried so far?
I have tried messing around with my script to no avail. No matter what I try does not fix my issue with keeping the eye pupil in bounds.
I’m fairly new to scripting so I’m sure there is an easy solution to this that I am not aware of
My script for the right eye which is in StarterCharacterScripts:
local function findCookie(pos)
local cookie = nil
local dist = 100
local child = workspace:children()
for i=1, #child do
if child[i].className == "Model" then
local c = child[i]:FindFirstChild("Cookie")
if c ~= nil then
local check = child[i]:FindFirstChild("Chips")
if check ~= nil then
if (check.Position - pos).magnitude < dist then
cookie = check
dist = (check.Position - pos).magnitude
end
end
end
end
end
return cookie
end
--eye stuff
game:GetService"RunService".Stepped:connect(function()
local eyebounds = script.Parent.RightEyeBoundary
local eyebone = script.Parent.RootPart.RightEye
local origpos = eyebone.WorldPosition
local part = script.Parent.RightPupil
local cookie = findCookie(origpos)
if cookie ~= nil then
eyebone.WorldCFrame = CFrame.new(cookie.Position.X, cookie.Position.Y, eyebone.WorldPosition.Z)
eyebone.WorldOrientation = Vector3.new(eyebounds.Orientation.X, eyebounds.Orientation.Y, eyebounds.Orientation.Z)
local PosX = math.clamp(eyebone.WorldPosition.X, eyebounds.Position.X - eyebounds.Size.X/2 + part.Size.X/2, eyebounds.Position.X + eyebounds.Size.X/2 - part.Size.X/2)
local PosY = math.clamp(eyebone.WorldPosition.Y, eyebounds.Position.Y - eyebounds.Size.Y/2 + part.Size.Y/2, eyebounds.Position.Y + eyebounds.Size.Y/2 - part.Size.Y/2)
local PosZ = math.clamp(eyebone.WorldPosition.Z, eyebounds.Position.Z - eyebounds.Size.Z/2 + part.Size.Z/2, eyebounds.Position.Z + eyebounds.Size.Z/2 - part.Size.Z/2)
eyebone.WorldPosition = Vector3.new(PosX, PosY, PosZ) -- Setting the position of the part to be in bounds
end
end)
These lines are problematic because you’re clamping the world-space position of the eyebones to a bounding box that is the size of the ‘eyebounds’ part, but has identity orientation in world space, rather than the eyebounds part’s actually orientation. In other words, you’re clamping eyebone.WorldPosition.X to always be within eyebounds.Position.X +/- eyebounds.Size.X/2 on the world space X-axis, but what you want is to clamp it to these distances on the eyebounds Part’s local X-axis. The way it is now, the clamping is only going to work when the character itself is aligned to the world axes.
I can’t see exactly how your eye bones are oriented relative to the eyebounds part, or what is parented to what, but assuming the eye bones are aligned with the bounding box parts, you’ll want to clamp Bone.Position instead of Bone.WorldPosition.
Thank you for the help, I’m somewhat understanding this now!
Edit:
I used WorldPosition and WorldOrientation for the bones because using Position and Orientation seemed to mess up everything I scripted, but now it works after doing what you suggested!
Changing any other WorldPosition to Position in the script currently does not keep the bone where it should be on the player, and keeps it at a weird distance away.
Unfortunately, I’m still having an issue with the eye positioning on the Y axis, which I will show in a video below
The script I have attempted now:
game:GetService"RunService".Stepped:connect(function()
local eyebounds = script.Parent.RightEyeBoundary
local eyebone = script.Parent.RootPart.RightEye
local origpos = eyebone.Position
local part = script.Parent.RightPupil
local boundsrel = eyebounds.CFrame:ToObjectSpace(eyebone.WorldCFrame)
local cookie = findCookie(origpos)
if cookie ~= nil then
eyebone.WorldCFrame = CFrame.new(cookie.Position.X, cookie.Position.Y, eyebone.Position.Z)
eyebone.WorldOrientation = Vector3.new(eyebounds.Orientation.X, eyebounds.Orientation.Y, eyebounds.Orientation.Z)
local PosX = math.clamp(eyebone.Position.X, boundsrel.Position.X - eyebounds.Size.X/2 + part.Size.X/2, boundsrel.Position.X + eyebounds.Size.X/2 - part.Size.X/2)
local PosY = math.clamp(eyebone.Position.Y, boundsrel.Position.Y - eyebounds.Size.Y/2 + part.Size.Y/2, boundsrel.Position.Y + eyebounds.Size.Y/2 - part.Size.Y/2)
local PosZ = math.clamp(eyebone.Position.Z, boundsrel.Position.Z - eyebounds.Size.Z/2 + part.Size.Z/2, boundsrel.Position.Z + eyebounds.Size.Z/2 - part.Size.Z/2)
eyebone.Position = Vector3.new(PosX, PosY, PosZ) -- Setting the position of the part to be in bounds
end
end)
I don’t think boundsrel.Position should still be a part of this either, since that’s also a world-space part position. Instead, you should probably either save each eye Bone’s initial Position (before you make changes), and offset from that. If the bounds Parts are not centered on the pupils, then you’d additionally have to capture the offset between the eye bone initial WorldPosition and the boundsrel.Position, and use that as the center to which you then add the +/- half box size to. But that offset would be static, not something you need to recompute every frame (assuming the bounds box doesn’t change size or offset at runtime)
Alternately, instead of saving the bounding box to bone initial position offset, you could just do a one-time calculation for the limits of the eye Bone travel in its own coordinate space. You’d then have to store just two Vector3, with the min and max values for x, y, z that the bone is allowed to move away from it’s starting location.
Worth noting, Bones also have a Transform property that can be used to animate them, which lets you set a relative offset and rotation each frame. If you use that though, you have to remember that it needs to be set on each client, because the Transform property doesn’t replicate to the server. This can be done easily now with a Script that has runcontext set to client. This is a bigger change from your current code though, and comes with it’s own new gotchas, but I thought I’d let you know that it’s there if you need it.