You can write your topic however you want, but you need to answer these questions:
- What do you want to achieve? Keep it simple and clear!
My goal is to be able to have a bone instance of a model correctly rotate to the position of an instance in the workspace be it a part or a model similar to the CFrame.lookAt() method.
- What is the issue? Include screenshots / videos if possible!
Because the bones need a specific axis for animations, their axes are derived by default, so when you try to rotate something on the Y axis hoping it would go left to right, it’ll, due to the new axis, rotate at an angle. Keep in mind this axis cannot be changed due to animations of the model.
- What solutions have you tried so far? Did you look for solutions on the Creator Hub?
I’ve tried a custom CFrame.lookAt() implementation using atan2 to translate the direction of the part to be tracked relative to the bones position. This should give me more flexibility in how I want to rotate the part be it CFrames, WorldCFrames, CFrame.fromAxis, manipulations of the bone CFrame in relation to the models space. However, none have worked. The closest I got was manipulating the WorldCFrame, however when orienting the model at a different Y angle, it all breaks
Here’s the entire code that runs the tracking implementation with WorldCFrames, you will most likely see the issue right away, but the solution is unbearably hard and I’ve yet to find it.
assume IKConfig.Yaw is just math.rad() angles, same for IKConfig.Pitch and IKConfig.TurnSpeeds returns a number
local replicatedStorage = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")
local playerService = game:GetService("Players")
local IKConfig = require(replicatedStorage.Configs.IKConfig)
local signal = runService.Heartbeat
local tracking = replicatedStorage.States.Tracking
local bones = {}
local originalCFrames = {}
local currentYaws = {}
local currentPitchs = {}
local NUM_OF_ACTIVES = 5 - 1
local function GetBones()
local model = workspace:WaitForChild("A_Running").Dinosaur
bones["FirstSpine"] = model.DeformationSystem.root.Root_M.Spine1_M.Spine2_M.Chest_M
bones["SecondSpine"] = model.DeformationSystem.root.Root_M.Spine1_M.Spine2_M
bones["ThirdSpine"] = model.DeformationSystem.root.Root_M.Spine1_M
bones["Neck"] = model.DeformationSystem.root.Root_M.Spine1_M.Spine2_M.Chest_M.Neck0_M
bones["Head"] = model.DeformationSystem.root.Root_M.Spine1_M.Spine2_M.Chest_M.Neck0_M.Neck1_M.Neck5_M.Head_M
end
local function OrientBone(direction, boneInstance, boneName, deltaTime)
if not originalCFrames[boneName] then
originalCFrames[boneName] = boneInstance.CFrame
currentYaws[boneName] = 0
currentPitchs[boneName] = 0
end
local directionFlat = direction * Vector3.new(1, 0, 1)
local targetYaw = math.atan2(directionFlat.X, directionFlat.Z)
targetYaw = math.clamp(targetYaw, -IKConfig.Yaws[boneName], IKConfig.Yaws[boneName])
local horizontalDistance = directionFlat.Magnitude
local targetPitch = math.atan2(direction.Y, horizontalDistance)
targetPitch = math.clamp(targetPitch, -IKConfig.Pitches[boneName], IKConfig.Pitches[boneName])
currentYaws[boneName] = currentYaws[boneName] + (targetYaw - currentYaws[boneName]) * math.min(deltaTime * IKConfig.TurnSpeeds[boneName], 1)
currentPitchs[boneName] = currentPitchs[boneName] + (targetPitch - currentPitchs[boneName]) * math.min(deltaTime * IKConfig.TurnSpeeds[boneName], 1)
boneInstance.CFrame = originalCFrames[boneName]
local pivot = boneInstance.WorldCFrame.Position
boneInstance.WorldCFrame = CFrame.new(pivot) * CFrame.Angles(-currentPitchs[boneName] / NUM_OF_ACTIVES, currentYaws[boneName] / NUM_OF_ACTIVES, 0) * (boneInstance.WorldCFrame - pivot)
end
local function Update(deltaTime)
local player = playerService:GetPlayerByUserId(tracking.Value)
if not player then return end
local character = player.Character
if not character then return end
local targetPosition = character:GetPivot().Position
for boneName, bone in pairs(bones) do
OrientBone( ( bone.WorldCFrame.Position - targetPosition ), bone, boneName, deltaTime )
end
end
local function Setup(data)
GetBones()
signal:Connect(Update)
end
shared.Controllers.LoadController.register_loaded("TrackingSetup", Setup)
return nil
If you need more context or information on the dinosaurs structure, or a clearer explanation of my goal don’t hesitate to ask. Thank you in advance for any help
