Qt Quick 3D Physics - 복합 도형 예제

복합 충돌 도형을 사용하는 데모입니다.

이 예제에서는 둘 이상의 콜리전 셰이프를 사용하여 콜리전 감지를 위한 복잡한 오브젝트를 만드는 방법을 보여줍니다. 장면은 녹색 정적 평면과 서로 연결된 일련의 링크로 구성됩니다. 처음에는 시뮬레이션이 비활성화되어 있습니다. 잠시 후 또는 사용자가 스페이스 키를 누르면 시뮬레이션이 시작됩니다. 가장 왼쪽과 가장 오른쪽의 링크가 수평으로 앞뒤로 움직이는 애니메이션이 시작됩니다.

설정

평소와 같이 PhysicsWorld 을 추가해야 합니다:

PhysicsWorld {
    id: physicsWorld
    enableCCD: true
    maximumTimestep: 20
    scene: viewport.scene
}

환경, 카메라 및 조명이 있는 일반적인 설정을 수행합니다:

environment: SceneEnvironment {
    antialiasingMode: SceneEnvironment.MSAA
    backgroundMode: SceneEnvironment.Color
    clearColor: "lightblue"
}

focus: true

PerspectiveCamera {
    id: camera
    position: Qt.vector3d(0, 900, 1500)
    eulerRotation: Qt.vector3d(-10, 0, 0)
    clipFar: 15500
    clipNear: 1
}

DirectionalLight {
    eulerRotation.x: -45
    eulerRotation.y: 45
    castsShadow: true
    brightness: 1.5
    shadowMapQuality: Light.ShadowMapQualityHigh
}

물리적 오브젝트

일반적인 정적 평면이 있습니다:

StaticRigidBody {
    position: Qt.vector3d(0, -100, 0)
    eulerRotation: Qt.vector3d(-90, 0, 0)
    collisionShapes: PlaneShape {}
    Model {
        source: "#Rectangle"
        scale: Qt.vector3d(500, 500, 1)
        materials: DefaultMaterial {
            diffuseColor: "green"
        }
        castsShadows: false
        receivesShadows: true
    }
}

그런 다음 링크의 인스턴스를 만듭니다.

MeshLink {
    id: leftLink
    isKinematic: true
    property vector3d startPos: Qt.vector3d(-6 * viewport.ringDistance,
                                            viewport.ringY,
                                            0)
    property vector3d startRot: Qt.vector3d(90, 0, 0)
    kinematicPosition: startPos
    position: startPos
    kinematicEulerRotation: startRot
    eulerRotation: startRot
    color: "red"
}

CapsuleLink {
    position: Qt.vector3d(-5 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    position: Qt.vector3d(-4 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    position: Qt.vector3d(-3 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(0, 90, 0)
}

MeshLink {
    position: Qt.vector3d(-2 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    position: Qt.vector3d(-1 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(0, 90, 0)
}

CapsuleLink {
    position: Qt.vector3d(0, viewport.ringY, 0)
}

MeshLink {
    position: Qt.vector3d(1 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(0, 90, 0)
}

MeshLink {
    position: Qt.vector3d(2 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    position: Qt.vector3d(3 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(0, 90, 0)
}

MeshLink {
    position: Qt.vector3d(4 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

CapsuleLink {
    position: Qt.vector3d(5 * viewport.ringDistance, viewport.ringY, 0)
    eulerRotation: Qt.vector3d(90, 0, 0)
}

MeshLink {
    id: rightLink
    isKinematic: true
    property vector3d startPos: Qt.vector3d(6 * viewport.ringDistance,
                                            viewport.ringY,
                                            0)
    property vector3d startRot: Qt.vector3d(90, 0, 0)
    kinematicPosition: startPos
    position: startPos
    kinematicEulerRotation: startRot
    eulerRotation: startRot
    color: "red"
}

왼쪽에 있는 첫 번째 링크는 isKinematic 속성을 true 으로 설정하여 애니메이션을 통해 제어할 수 있도록 합니다. 키네마틱 객체이므로 kinematicPositionkinematicRotation 프로퍼티를 설정해야 합니다. kinematicPosition 프로퍼티를 애니메이션화하여 애니메이션을 적용합니다. 다른 링크는 링크 사이에 약간의 간격을 두고 인스턴스화합니다.

물리적 시뮬레이션을 정확히 따르는 부드러운 애니메이션을 얻기 위해 AnimationController 을 사용하여 PhysicsWorldonFrameDone 신호에 연결합니다. 이렇게 하면 시뮬레이션 속도가 느려지는 프레임 드롭이 발생하면 애니메이션이 그에 따라 느려집니다. 가장 왼쪽과 오른쪽 링을 앞뒤로 움직이기 위해 NumberAnimation 4개가 있는 SequentialAnimation 을 사용합니다. 이것이 애니메이션의 QML 코드입니다:

Connections {
    target: physicsWorld
    property real totalAnimationTime: 12000
    function onFrameDone(timeStep) {
        let progressStep = timeStep / totalAnimationTime
        animationController.progress += progressStep
        if (animationController.progress >= 1) {
            animationController.completeToEnd()
            animationController.reload()
            animationController.progress = 0
        }
    }
}

AnimationController {
    id: animationController
    animation: SequentialAnimation {
        NumberAnimation {
            target: leftLink
            property: "kinematicPosition.x"
            to: 3 * viewport.ringDistance
            from: -6 * viewport.ringDistance
            easing.type: Easing.InOutCubic
            duration: 1000
        }
        NumberAnimation {
            target: leftLink
            property: "kinematicPosition.x"
            from: 3 * viewport.ringDistance
            to: -6 * viewport.ringDistance
            easing.type: Easing.InOutCubic
            duration: 1000
        }
        NumberAnimation {
            target: rightLink
            property: "kinematicPosition.x"
            to: -3 * viewport.ringDistance
            from: 6 * viewport.ringDistance
            easing.type: Easing.InOutCubic
            duration: 1000
        }
        NumberAnimation {
            target: rightLink
            property: "kinematicPosition.x"
            from: -3 * viewport.ringDistance
            to: 6 * viewport.ringDistance
            easing.type: Easing.InOutCubic
            duration: 1000
        }
    }
}

흥미로운 부분은 메시 및 캡슐 링크 파일 내부에서 일어나는 일입니다. 각각을 살펴보겠습니다.

DynamicRigidBody {
    scale: Qt.vector3d(100, 100, 100)
    property color color: "white"
    PrincipledMaterial {
        id: _material
        baseColor: color
        metalness: 1.0
        roughness: 0.5
    }

    Model {
        source: "meshes/ring.mesh"
        materials: [_material]
    }

    collisionShapes: [
        ConvexMeshShape {
            source: "meshes/segmentedRing_001.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_002.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_003.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_004.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_005.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_006.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_007.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_008.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_009.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_010.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_011.mesh"
        },
        ConvexMeshShape {
            source: "meshes/segmentedRing_012.mesh"
        }
    ]
}

메시 링크는 모델과 머티리얼이 있는 다이내믹 리지드 바디입니다. 모델은 메시 파일에서 메시를 로드합니다. 또한 함께 결합되어 충돌 감지를 위한 복합 모양을 형성하는 충돌 모양 목록이 있습니다. 각 셰이프는 소스 파일에서 메시를 로드하는 컨벡스 메시 셰이프입니다. 볼록 셰이프는 기본적으로 셰이프 내의 두 점 사이의 선이 항상 셰이프 내부에 있는 셰이프입니다.

디버그 모드가 활성화되었을 때 자세히 살펴보면, 콜리전 셰이프가 복합 콜리전 셰이프를 형성하는 방식입니다:

DynamicRigidBody {
    property real len: 170
    property real w: 17
    PrincipledMaterial {
        id: material3
        baseColor: "yellow"
        metalness: 1.0
        roughness: 0.5
    }
    Node {
        opacity: 1
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(w / 100, len / 100, w / 100)
            eulerRotation.z: 90
            y: -len / 2
        }
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(w / 100, len / 100, w / 100)
            eulerRotation.z: 90
            y: len / 2
        }
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(w / 100, len / 100, w / 100)
            x: len / 2
        }
        Model {
            materials: material3
            source: "#Cylinder"
            scale: Qt.vector3d(w / 100, len / 100, w / 100)
            x: -len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(w / 100, w / 100, w / 100)
            x: -len / 2
            y: -len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(w / 100, w / 100, w / 100)
            x: -len / 2
            y: len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(w / 100, w / 100, w / 100)
            x: len / 2
            y: -len / 2
        }
        Model {
            materials: material3
            source: "#Sphere"
            scale: Qt.vector3d(w / 100, w / 100, w / 100)
            x: len / 2
            y: len / 2
        }
    }
    collisionShapes: [
        CapsuleShape {
            y: -len / 2
            height: len
            diameter: w
        },
        CapsuleShape {
            y: len / 2
            height: len
            diameter: w
        },
        CapsuleShape {
            x: -len / 2
            eulerRotation.z: 90
            height: len
            diameter: w
        },
        CapsuleShape {
            x: len / 2
            eulerRotation.z: 90
            height: len
            diameter: w
        }
    ]
}

캡슐 링크는 동일한 재질을 공유하는 일부 모델의 동적 리지드 바디입니다. 이 링크는 여러 개의 원통과 구체로 구성됩니다. 메시 링크와 마찬가지로 콜리전 셰이프 목록이 있습니다. 이번에는 각 모양이 캡슐 모양입니다.

디버그 모드가 활성화되었을 때 자세히 살펴보면 콜리전 셰이프가 복합 콜리전 셰이프를 형성하는 방식이 다음과 같습니다.

파일:

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.