Jetpack XR için ARCore'u kullanarak ellerinizle çalışma

Jetpack XR için ARCore, kullanıcının algılanan elleri hakkında bilgi sağlayabilir ve eller ile ilişkili eklemler için duruş bilgisi verir. Bu el verileri, kullanıcının ellerine öğe ve modeller (ör. bir araç menüsü) eklemek için kullanılabilir:

Oturum edinme

Android XR Session aracılığıyla el bilgilerine erişin. Session almak için Oturum yaşam döngüsünü anlama başlıklı makaleyi inceleyin.

Oturumu yapılandırma

El takibi, XR oturumlarında varsayılan olarak etkin değildir. El verilerini almak için oturumu yapılandırın:

val newConfig = session.config.copy(
    handTracking = Config.HandTrackingMode.Enabled
)
when (val result = session.configure(newConfig)) {
    is SessionConfigureConfigurationNotSupported ->
        TODO(/* Some combinations of configurations are not valid. Handle this failure case. */)
    is SessionConfigurePermissionsNotGranted ->
        TODO(/* The required permissions in result.permissions have not been granted. */)
    is SessionConfigureSuccess -> TODO(/* Success! */)
}

El verilerini alma

El verileri sol ve sağ el için ayrı olarak kullanılabilir. Her eklemin poz konumlarına erişmek için her elin state simgesini kullanın:

Hand.left(session)?.state?.collect { handState -> // or Hand.right(session)
    // Hand state has been updated.
    // Use the state of hand joints to update an entity's position.
    renderPlanetAtHandPalm(handState)
}

Eller aşağıdaki özelliklere sahiptir:

  • isActive: Elin izlenip izlenmediği.
  • handJoints: El eklemlerinin pozlarla eşleştirildiği bir harita. El eklemi pozları OpenXR standartları ile belirtilir.

Uygulamanızda el verilerini kullanma

Kullanıcının el eklemlerinin konumları, 3D nesneleri kullanıcının ellerine sabitlemek için kullanılabilir. Örneğin, bir modeli sol avuç içine eklemek için:

val palmPose = leftHandState.handJoints[HandJointType.PALM] ?: return

// the down direction points in the same direction as the palm
val angle = Vector3.angleBetween(palmPose.rotation * Vector3.Down, Vector3.Up)
palmEntity.setHidden(angle > Math.toRadians(40.0))

val transformedPose =
    session.scene.perceptionSpace.transformPoseTo(
        palmPose,
        session.scene.activitySpace,
    )
val newPosition = transformedPose.translation + transformedPose.down * 0.05f
palmEntity.setPose(Pose(newPosition, transformedPose.rotation))

Dilerseniz sağ elinizin işaret parmağının ucuna da model ekleyebilirsiniz:

val tipPose = rightHandState.handJoints[HandJointType.INDEX_TIP] ?: return

// the forward direction points towards the finger tip.
val angle = Vector3.angleBetween(tipPose.rotation * Vector3.Forward, Vector3.Up)
indexFingerEntity.setHidden(angle > Math.toRadians(40.0))

val transformedPose =
    session.scene.perceptionSpace.transformPoseTo(
        tipPose,
        session.scene.activitySpace,
    )
val position = transformedPose.translation + transformedPose.forward * 0.03f
val rotation = Quaternion.fromLookTowards(transformedPose.up, Vector3.Up)
indexFingerEntity.setPose(Pose(position, rotation))

Temel el hareketlerini algılama

Temel el hareketlerini algılamak için eldeki eklemlerin duruşlarını kullanın. Eklemlerin belirli bir poz olarak kaydedilmesi için hangi poz aralığında olması gerektiğini belirlemek üzere El eklemleriyle ilgili kurallar başlıklı makaleyi inceleyin.

Örneğin, başparmak ve işaret parmağıyla yapılan sıkıştırma hareketini algılamak için iki parmak ucu eklemi arasındaki mesafeyi kullanın:

val thumbTip = handState.handJoints[HandJointType.THUMB_TIP] ?: return false
val thumbTipPose = session.scene.perceptionSpace.transformPoseTo(thumbTip, session.scene.activitySpace)
val indexTip = handState.handJoints[HandJointType.INDEX_TIP] ?: return false
val indexTipPose = session.scene.perceptionSpace.transformPoseTo(indexTip, session.scene.activitySpace)
return Vector3.distance(thumbTipPose.translation, indexTipPose.translation) < 0.05

"Dur" hareketi, daha karmaşık bir harekete örnek olarak verilebilir. Bu harekette her parmak düz tutulmalıdır. Yani her parmağın her eklemi kabaca aynı yönü göstermelidir:

val threshold = toRadians(angleInDegrees = 30f)
fun pointingInSameDirection(joint1: HandJointType, joint2: HandJointType): Boolean {
    val forward1 = handState.handJoints[joint1]?.forward ?: return false
    val forward2 = handState.handJoints[joint2]?.forward ?: return false
    return Vector3.angleBetween(forward1, forward2) < threshold
}
return pointingInSameDirection(HandJointType.INDEX_PROXIMAL, HandJointType.INDEX_TIP) &&
    pointingInSameDirection(HandJointType.MIDDLE_PROXIMAL, HandJointType.MIDDLE_TIP) &&
    pointingInSameDirection(HandJointType.RING_PROXIMAL, HandJointType.RING_TIP)

El hareketleri için özel algılama geliştirirken aşağıdaki noktaları göz önünde bulundurun:

  • Kullanıcılar belirli bir hareketi farklı şekilde yorumlayabilir. Örneğin, bazı kullanıcılar "dur" hareketi için parmakların açık olmasını tercih ederken bazıları parmakların birbirine yakın olmasını daha sezgisel bulabilir.
  • Bazı hareketleri uzun süre boyunca yapmak rahatsız edici olabilir. Kullanıcıların ellerini yormayan sezgisel hareketler kullanın.

Kullanıcının ikincil elini belirleme

Android sistemi, sistem gezinmesini kullanıcının sistem tercihlerinde belirttiği gibi kullanıcının birincil eline yerleştirir. Sistem gezinme hareketleriyle çakışmayı önlemek için özel hareketlerinizde ikincil eli kullanın:

val handedness = Hand.getHandedness(activity.contentResolver)
val secondaryHand = if (handedness == Hand.Handedness.LEFT) Hand.right(session) else Hand.left(session)
val handState = secondaryHand?.state ?: return
detectGesture(handState)