로블록스 절차적 발 착지 IK: 2-Bone 역기구학 코사인 법칙, 골반 높이 보정 및 표면 법선 정렬

By DopaBrain 애니메이션 및 절차적 물리 엔지니어링 팀 Updated: 2026-10-01 12 min read

딥워큰(Deepwoken), 프론트라인(Frontlines), 데드 어헤드(Dead Ahead) 등 로블록스 최고의 오픈월드 및 액션 게임에서 캐릭터가 불규칙한 지형을 이동할 때 발이 허공에 뜨는 현상은 몰입감을 치명적으로 해칩니다. 기본 R15 및 R6 애니메이션은 수평의 평평한 바닥만을 가정하여 제작되었기 때문에 계단, 바위 지형, 가파른 언덕에서 한쪽 발이 공중에 뜨고 다른 쪽 발은 바닥에 파묻히게 됩니다.

사실적인 보행 표현을 위해 최상위 개발팀은 '절차적 발 착지 역기구학(Foot Placement IK)'을 구현합니다. 본 가이드에서는 Luau를 활용해 2-Bone 해석적 IK 솔버를 구축합니다. 제2코사인 법칙 기반 관절 회전각 유도, 골반 부착점 하향 레이캐스팅, 다리 과신전(Overextension) 방지를 위한 동적 골반 하강(Hip Drop), 지형 법선에 맞춘 발목 회전 정렬 기법을 체계적으로 다룹니다.

1. 왜 기본 캐릭터는 공중에 뜨는가: 절차적 발 IK의 필요성

기본 로블록스 애니메이션 블렌딩은 런타임 지형 굴곡을 인지하지 못합니다:

2. 수학적 기초: 해석적 2-Bone 제2코사인 법칙 솔버

2개의 뼈(허벅지 UpperLeg -> 종아리 LowerLeg -> 발 Foot)로 구성된 다리 역기구학은 반복 연산인 FABRIK 대신 해석적 제2코사인 법칙으로 즉각 계산됩니다:

3. 프로덕션 등급 절차적 발 착지 Luau 구현

다음은 RunService.RenderStepped에서 60fps 이상으로 부드럽게 구동되는 모듈형 발 IK 컨트롤러 전체 코드입니다:

FootPlacementIKController.luau (절차적 다리 IK 솔버)
--!strict
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")

local FootIK = {}
FootIK.__index = FootIK

export type LegRig = {
    HipMotor: Motor6D,
    KneeMotor: Motor6D,
    AnkleMotor: Motor6D,
    HipAttachment: Attachment,
    ThighLength: number,
    CalfLength: number,
    CurrentFootOffset: number,
}

export type CharacterIK = {
    Character: Model,
    RootJoint: Motor6D,
    LeftLeg: LegRig,
    RightLeg: LegRig,
    RayParams: RaycastParams,
    HipDropOffset: number,
}

local function SolveTwoBone(origin: CFrame, targetPos: Vector3, l1: number, l2: number): (CFrame, CFrame)
    local localized = origin:PointToObjectSpace(targetPos)
    local dist = localized.Magnitude
    dist = math.clamp(dist, 0.05, (l1 + l2) - 0.001)

    local cosAlpha = (l1 * l1 + dist * dist - l2 * l2) / (2 * l1 * dist)
    local alpha = math.acos(math.clamp(cosAlpha, -1, 1))

    local cosBeta = (l1 * l1 + l2 * l2 - dist * dist) / (2 * l1 * l2)
    local beta = math.acos(math.clamp(cosBeta, -1, 1))

    local planeRot = CFrame.lookAt(Vector3.zero, localized)
    local hipRot = planeRot * CFrame.Angles(alpha, 0, 0)
    local kneeRot = CFrame.Angles(-(math.pi - beta), 0, 0)

    return hipRot, kneeRot
end

function FootIK.Step(ik: CharacterIK, dt: number)
    local char = ik.Character
    local rootPart = char.PrimaryPart
    if not rootPart then return end

    local rayDown = Vector3.new(0, -5, 0)
    local leftOrigin = ik.LeftLeg.HipAttachment.WorldPosition
    local rightOrigin = ik.RightLeg.HipAttachment.WorldPosition

    local leftHit = Workspace:Raycast(leftOrigin, rayDown, ik.RayParams)
    local rightHit = Workspace:Raycast(rightOrigin, rayDown, ik.RayParams)

    local leftTarget = leftHit and leftHit.Position or (leftOrigin + Vector3.new(0, -3.2, 0))
    local rightTarget = rightHit and rightHit.Position or (rightOrigin + Vector3.new(0, -3.2, 0))

    -- 골반 하강(Hip Drop) 계산
    local leftDeficit = leftOrigin.Y - leftTarget.Y
    local rightDeficit = rightOrigin.Y - rightTarget.Y
    local maxDeficit = math.max(leftDeficit, rightDeficit)
    local targetHipDrop = math.clamp(maxDeficit - 3.2, 0, 1.8)

    ik.HipDropOffset = ik.HipDropOffset + (targetHipDrop - ik.HipDropOffset) * math.clamp(dt * 15, 0, 1)
    ik.RootJoint.Transform = CFrame.new(0, -ik.HipDropOffset, 0)

    -- 좌측 다리 연산
    local leftHipCF = ik.LeftLeg.HipMotor.Part0.CFrame * ik.LeftLeg.HipMotor.C0
    local lHipRot, lKneeRot = SolveTwoBone(leftHipCF, leftTarget, ik.LeftLeg.ThighLength, ik.LeftLeg.CalfLength)
    ik.LeftLeg.HipMotor.Transform = lHipRot
    ik.LeftLeg.KneeMotor.Transform = lKneeRot

    -- 우측 다리 연산
    local rightHipCF = ik.RightLeg.HipMotor.Part0.CFrame * ik.RightLeg.HipMotor.C0
    local rHipRot, rKneeRot = SolveTwoBone(rightHipCF, rightTarget, ik.RightLeg.ThighLength, ik.RightLeg.CalfLength)
    ik.RightLeg.HipMotor.Transform = rHipRot
    ik.RightLeg.KneeMotor.Transform = rKneeRot
end

return FootIK

4. 골반 하강(Hip Drop) 보정 및 발목 표면 법선 정렬

경사면에 선 캐릭터에서 다리 IK만 풀면 아래쪽 다리가 팽팽하게 펴져 로봇처럼 부자연스러워집니다:

5. 렌더링 최적화, 프러스텀 컬링 및 멀티플레이어 LOD

수십 명의 플레이어가 모인 서버에서 모든 캐릭터의 삼각법 연산을 수행할 때 CPU 병목을 방지하는 아키텍처:

Frequently Asked Questions

로블록스 내장 IKControl 대신 자체 2-Bone 삼각법을 구현하는 이유는 무엇인가요?

내장 IKControl에 비해 자체 삼각법 솔버는 골반 하강(Hip Drop)의 미세 감쇠 조절, 바닥 법선에 맞춘 발목 회전, 커스텀 충돌 마스크 및 카메라 프러스텀 거리별 LOD를 100% 코드로 제어할 수 있기 때문입니다.

계단을 오를 때 발이 미세하게 떨리는 지터(Jitter)는 어떻게 해결하나요?

계단 디딤판과 수직면 사이에서 레이캐스트 히트 좌표가 급변할 때 발생하므로, 발 착지 목표 위치에 지수 이동 평균(EMA) 필터를 적용하면 떨림이 완벽히 사라집니다.

골반을 낮추면 캐릭터 충돌체(HumanoidRootPart) 높이도 낮아져 점프에 영향을 주나요?

아닙니다. 물리적 충돌체인 HumanoidRootPart의 위치는 그대로 유지되며, 시각적 렌더링을 담당하는 RootJoint Motor6D의 Transform 오프셋만 조절되므로 물리 충돌 및 점프 성능은 전혀 영향을 받지 않습니다.

Explore More Interactive Tests & Guides

Discover personalized cognitive assessments, stress evaluations, and game psychology tools on DopaBrain.

Go to Portal Hub