Root Motion을 써야하는 상황이 왔는데 이상하게 뭘 만져도 아무런 변화가 없었다.

구글링중 알게 된 것이 모델의 스켈레톤에서 루트본이 Bip001같은 기본 바이패드 본 이름이 아니라 제일 최상단에 Root 라는 본이 있어야만 작동한다.

3DS MAX 에서 Root Bone을 재설정 해주는 스크립트를 만들어 주신 분이 계셔서 저장해 둬야겠다.

출처 : https://lifeisforu.tistory.com/305 (정말 감사합니다 ㅠㅠ)

-------------------------------------------------------------------------------
-- Function : BuildRootMotion
-------------------------------------------------------------------------------
   
function BuildRootMotion removeX removeY removeZ bipRootBoneName bipBottomBoneName =
(
    ----------------------------------------------------------------------------
    -- get biped root
    ----------------------------------------------------------------------------
   
    local bipedRoot = getNodeByName bipRootBoneName
   
    if bipedRoot == undefined do
    (
        messagebox ( "Biped 루트가 존재하지 않거나 이름이 틀렸습니다" )
        return false
    )
      
    ----------------------------------------------------------------------------
    -- check if root bone exists
    ----------------------------------------------------------------------------
      
    local bipedBottom = getNodeByName bipBottomBoneName
      
    if bipedBottom == undefined do
    (
        messagebox( "Biped 바닥 본의 이름이 틀리므로 무시됩니다" )
    )
      
    ----------------------------------------------------------------------------
    -- check if root bone exists
    ----------------------------------------------------------------------------
   
    local rootBone = getNodeByName "Root"
   
    if rootBone != undefined do
    (
        messagebox ( "Root 본이 이미 존재합니다" )
        return false
    )
   
    ----------------------------------------------------------------------------
    -- create rootBone
    ----------------------------------------------------------------------------
   
    local rootBone = BoneSys.createBone [ 0, 0, 0 ] [ 20, 0, 0 ] [ 0, 0, 1 ]
   
    rootBone.name = "Root"
   
    clearSelection()
   
    select rootBone
   
    ----------------------------------------------------------------------------
    -- create rootBone position controller
    ----------------------------------------------------------------------------
   
    rootBone.position.controller = Position_XYZ()
   
    for t = animationRange.start to animationRange.end do
    (
        addNewKey rootBone.position.controller t
    )
   
    ----------------------------------------------------------------------------
    -- create position constraint controller for root
    ----------------------------------------------------------------------------
   
    local tempRootController = Position_Constraint()
   
    tempRootController.constraints.appendTarget bipedRoot 1.0
      
    ----------------------------------------------------------------------------
    -- create position constraint controller for bottom
    ----------------------------------------------------------------------------
      
    local tempBottomController = Position_Constraint()
      
    if bipedBottom != undefined do
    (
        tempBottomController.constraints.appendTarget bipedBottom 1.0
    )
       
    ----------------------------------------------------------------------------
    -- fill rootBone position controller
    ----------------------------------------------------------------------------
   
    animate on
    (
        for t = animationRange.start to animationRange.end do
        (
            at time t
            (
                if true == removeX then
                    rootBone.position.controller.value.x = 0.0
                else
                    rootBone.position.controller.value.x = tempRootController.value.x
                   
                if true == removeY then
                    rootBone.position.controller.value.y = 0.0
                else
                    rootBone.position.controller.value.y = tempRootController.value.y
                   
                if true == removeZ then
                (
                    rootBone.position.controller.value.z = 0.0
                )
                else
                (
                    local zValue = 0.0
                      
                    if bipedBottom != undefined then
                        zValue = tempBottomController.value.z
                    else
                        zValue = tempRootController.value.z
                      
                    rootBone.position.controller.value.z = zValue
                )
            )
        )
    )
       
    ----------------------------------------------------------------------------
    -- create biped layer
    ----------------------------------------------------------------------------
       
    rootMotionLayerId = biped.numLayers bipedRoot.controller + 1
       
    biped.createLayer bipedRoot.controller rootMotionLayerId "RootMotionLayer"
       
    biped.setCurrentLayer bipedRoot.controller rootMotionLayerId
       
    clearSelection()
       
    select bipedRoot
       
    horzController = bipedRoot.controller.horizontal.controller
       
    for t = animationRange.start to animationRange.end do
    (
        biped.addNewKey horzController t
    )
       
    animate on
    (
        for t = animationRange.start to animationRange.end do
        (  
            at time t
            (              
                newPos = horzController.value[ 4 ]
                   
                if false == removeX do
                    newPos.x = 0.0
                   
                if false == removeY do
                    newPos.y = 0.0
                   
                if false == removeZ do
                (
                    if bipedBottom != undefined then
                        newPos.z = newPos.z - tempBottomController.value.z
                    else
                        newPos.z = 0.0
                )
                   
                biped.setTransform bipedRoot #pos newPos false
            )
        )
    )
       
    ----------------------------------------------------------------------------
    -- make rootBone as root
    ----------------------------------------------------------------------------
       
    bipedRoot.parent = rootBone
       
    return true
)
   
-------------------------------------------------------------------------------
-- UI
-------------------------------------------------------------------------------
   
rollout RootMotionUI "루트모션 본 생성"
(
    checkbox checkRemoveX "Remove X" checked:true
    checkbox checkRemoveY "Remove Y"
    checkbox checkRemoveZ "Remove Z" checked:true
      
    edittext bipedRootName "Biped Root : " text:"Bip01"
    edittext bipedBottomName "Biped Bottom : " text:"Bip01 L Toe0Nub" readOnly:true
      
    button buttonOK "빌드"
       
    on buttonOK pressed do
    (
        BuildRootMotion checkRemoveX.checked checkRemoveY.checked checkRemoveZ.checked bipedRootName.text bipedBottomName.text
        DestroyDialog RootMotionUI
    )
      
    on checkRemoveZ changed theState do
    (
        bipedBottomName.readOnly = checkRemoveZ.checked
    )
)
   
-------------------------------------------------------------------------------
-- open UI
-------------------------------------------------------------------------------
   
CreateDialog RootMotionUI 200 150

사용법

  1. 제거할 축 입력받음.
  2. “Root” 본 생성.
  3. “Bip01” 본 획득.
  4. Root 본에 Position_XYZ 컨트롤러 바인딩하고 프레임 개수만큼 키를 생성.
  5. Bip01 을 따라 가는 Position_Constraint 컨트롤러 “tempController” 생성.
  6. 전체 프레임을 돌면서 “tempController” 값을 Root 본에 복사함( Bip01 을 복사하는 것과 동일함 ).
  7. Biped 에 “RootMotionLayer” 레이어를 생성하고 현재 레이어로 설정함.
  8. 새로운 레이어의 position 컨트롤러를 획득해서 전체 프레임 개수만큼 키를 생성.
  9. 새로운 레이어의 키의 position 에 “tempController” 컨트롤러에서 특정 축값을 제거한 값을 삽입.
  10. Root 본을 Bip01 의 부모로 설정.FBX 로 익스포트( 수동 ).
  11. 익스포트 후에 “RootMotionLayer” 레이어 및 “Root” 본 제거( 수동 ).