My Custom PoseHandler half-working; Offsets limbs from blank CFrame instead of RootPart's CFrame

Good evening programmers! I’ve made this topic to ask how could I make this “pose” module a little more consistent?

I’m making a Pose module for a game that I’m working on that’ll require you to make poses to do certain things. (almost like shapeshifting) And, I want to create a default pose upon character respawning, so that they can default to that pose if they want to restart or something.

The current problem I come across is how can I make it so that it doesn’t just randomly offsets the limbs from a blank CFrame position, and instead offset the limbs from the root’s CFrame position.

Images to show what I mean

image
image

I do believe it’s because it’s executing from a script that’s parented to the StarterGui, but I suspect waiting until the character is added before running the function.

The current code of the script that's creating the default pose
-- Service
local ContextActionService = game:GetService("ContextActionService")
local TestService = game:GetService("TestService") -- Just debugss
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")

-- Libraries
local Tools = script:WaitForChild('Tools')
local Util = script:WaitForChild("Util")

local CM_Enum = require(Util:WaitForChild("CM_Enum"))
local CurrentSettings = require(script:WaitForChild("Settings"))
local Pose = require(Util:WaitForChild("Pose"))
local Types = require(Util:WaitForChild("Types"))
local UIManager = require(Util:WaitForChild("UIManager"))

-- Variables
local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAppearanceLoaded:Wait()
-- skipped lines 20 to 29

-- Functions
-- skipped lines 32 to 120

-- Main Script
Pose.init(Character) -- "init" is just creating the default pose
The current code of the function that creates the default pose
function PoseHandler.init(character)
	assert(typeof(character) == "Instance", "character: expected Instance, got " .. typeof(character))
	assert(character:IsA("Model"), "character: expected Model, got " .. character.ClassName)

	if Character ~= character then -- doing this so it doesn't overwrite Character if it's set already
		Character = character
	end

	Humanoid = Character:WaitForChild("Humanoid") :: Humanoid
	RootPart = Character:WaitForChild("HumanoidRootPart") :: Part
	Torso = Character:WaitForChild("Torso") :: Part

	for _, child in Character:GetChildren() do
		local limb = Humanoid:GetLimb(child)

		if limb ~= Enum.Limb.Unknown then
			Limbs[limb.Name] = child
		end
	end

	if (RootPart.CFrame.Position - Vector3.zero).Magnitude < 1 then
		repeat
			game:GetService("RunService").Heartbeat:Wait()
		until (RootPart.CFrame.Position - Vector3.zero).Magnitude > 1
		-- i thought this was it, but it didn't do anything at all
	end

	PoseHandler:AddPose("_default")
	print("Initiated Default Pose")
end

Edit @ 2023-08-30T04:00:00Z

I forgot to leave some details about how it’s loaded. It gets loaded after pressing a button in the local script, like so:

local function ToggleTools(actionName: string, userState: Enum.UserInputState, input: InputObject)
	if userState == Enum.UserInputState.Begin then
		CurrentSettings.Enabled = not CurrentSettings.Enabled
		
		if CurrentSettings.Enabled then
			freezeCharacter()
			game:GetService("RunService").Heartbeat:Wait()
			Pose:LoadPose("_default")
			
			local tool = _Tools[CurrentSettings.LastMode]
			if tool then
				tool:Disable()
			end
			
			UIManager:ShowUI()
		else
			unfreezeCharacter()
			hideTools()
			
			UIManager:HideUI()
		end
	end
end

The function that loads the pose:

function PoseHandler:LoadPose(poseName)
	assert(typeof(poseName) == "string", "poseName: expected string, got " .. typeof(poseName))

	if not Poses[poseName] then
		return CM_Enum.LogType.Caution, "No pose named " .. poseName
	end
	
	local Pose = Poses[poseName]
	local Root = Pose.Root
	local LimbData = Pose.LimbData
	
	local currRootCFrame = Root.CFrame
	
	for limb, data in LimbData do
		if limb:FindFirstAncestorOfClass("Model") == Character then
			for property, value in pairs(data) do
				limb[property] = value
			end
			
			print(Root.CFrame:ToWorldSpace(data.CFrame).Position)
			
			limb.CFrame = Root.CFrame:ToWorldSpace(data.CFrame)
		end
	end
	
	game:GetService("RunService").Heartbeat:Wait()
	
	Root.CFrame = currRootCFrame
end
1 Like

After some tinkering, I managed to find the solution.

Apparently, the Root part’s CFrame changes whenever the other limb’s CFrame is changed. (Which is what the load pose would do) And because the Root’s CFrame can change whenever the limb’s CFrame does, I should’ve been putting the original Root’s CFrame in a variabe and using that. So, now everything works properly!

image

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.