العمل باستخدام اليدين باستخدام ARCore لـ Jetpack XR

أجهزة XR المعنيّة
تساعدك هذه الإرشادات في إنشاء تجارب لهذه الأنواع من أجهزة الواقع الممتد.
سماعات رأس بنظام الواقع الممتد
نظارات الواقع الممتد السلكية

يمكن أن يوفّر ARCore for Jetpack XR معلومات عن الأيدي التي تم رصدها للمستخدم، ويقدّم معلومات عن وضعيات الأيدي والمفاصل المرتبطة بها. يمكن استخدام بيانات اليد هذه لربط الكيانات والنماذج بأيدي المستخدم، مثل قائمة الأدوات:

الوصول إلى جلسة

يمكنك الوصول إلى معلومات اليد من خلال Session Jetpack XR Runtime، الذي يمكن لتطبيقك إنشاؤه.

ضبط الجلسة

لا يتم تفعيل ميزة "تتبُّع اليد" تلقائيًا في جلسات XR. لتلقّي بيانات اليد، اضبط الجلسة واضبط وضع HandTrackingMode.BOTH:

val newConfig = session.config.copy(
    handTracking = Config.HandTrackingMode.BOTH
)
when (val result = session.configure(newConfig)) {
    is SessionConfigureSuccess -> TODO(/* Success! */)
    else ->
        TODO(/* The session could not be configured. See SessionConfigureResult for possible causes. */)
}

استرداد بيانات اليد

تتوفّر بيانات اليدين بشكل منفصل لكل من اليد اليسرى واليمنى. استخدِم state في كل يد للوصول إلى مواضع الوضعيات لكل مفصل:

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)
}

تتضمّن اليدان السمات التالية:

  • trackingState: ما إذا كان يتم تتبُّع اليد أم لا
  • handJoints: خريطة لمفاصل اليد إلى أوضاع. يتم تحديد وضعيات مفاصل اليد وفقًا لمعايير OpenXR.

استخدام بيانات اليد في تطبيقك

يمكن استخدام مواضع مفاصل يد المستخدم لتثبيت الكائنات الثلاثية الأبعاد على يدي المستخدم، مثلاً لربط نموذج براحة اليد اليسرى:

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

// the down direction points in the same direction as the palm
val angle = Vector3.angleBetween(palmPose.rotation * Vector3.Down, Vector3.Up)
palmEntity.setEnabled(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))

أو لربط نموذج بطرف إصبع السبابة في يدك اليمنى:

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

// the forward direction points towards the finger tip.
val angle = Vector3.angleBetween(tipPose.rotation * Vector3.Forward, Vector3.Up)
indexFingerEntity.setEnabled(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))

رصد إيماءات اليد الأساسية

استخدام أوضاع المفاصل في اليد لرصد إيماءات اليد الأساسية راجِع اتفاقيات مفاصل اليد لتحديد نطاق الوضعيات التي يجب أن تكون فيها المفاصل ليتم تسجيلها كوضعية معيّنة.

على سبيل المثال، لرصد عملية الضغط بالإبهام والسبابة، استخدِم المسافة بين مفصلَي الإصبعَين:

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

من الأمثلة على الإيماءات الأكثر تعقيدًا إيماءة "التوقّف". في هذه الإشارة، يجب أن تكون كل الأصابع ممدودة، أي يجب أن تشير كل مفاصل كل إصبع إلى الاتجاه نفسه تقريبًا:

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.HAND_JOINT_TYPE_INDEX_PROXIMAL, HandJointType.HAND_JOINT_TYPE_INDEX_TIP) &&
    pointingInSameDirection(HandJointType.HAND_JOINT_TYPE_MIDDLE_PROXIMAL, HandJointType.HAND_JOINT_TYPE_MIDDLE_TIP) &&
    pointingInSameDirection(HandJointType.HAND_JOINT_TYPE_RING_PROXIMAL, HandJointType.HAND_JOINT_TYPE_RING_TIP)

يُرجى مراعاة النقاط التالية عند تطوير عملية رصد مخصّصة لإيماءات اليد:

  • قد يفسّر المستخدمون أي إيماءة معيّنة بشكل مختلف. على سبيل المثال، قد يرى البعض أنّ إيماءة "التوقف" تتطلّب فرد الأصابع، بينما قد يجد البعض الآخر أنّ ضم الأصابع معًا أكثر سهولة.
  • قد يكون من الصعب الحفاظ على بعض الإيماءات. استخدِم إيماءات سهلة لا تُجهد يدي المستخدم.

تحديد اليد الثانوية للمستخدم

يضع نظام التشغيل Android عناصر التنقّل في النظام في يد المستخدم الأساسية، وذلك وفقًا لما يحدّده المستخدم في الإعدادات المفضّلة للنظام. استخدِم اليد الثانوية لتنفيذ الإيماءات المخصّصة لتجنُّب التعارض مع إيماءات التنقّل في النظام:

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


‫OpenXR™ وشعار OpenXR هما علامتان تجاريتان مملوكتان لشركة The Khronos Group Inc. ومسجّلتان كعلامة تجارية في الصين والاتحاد الأوروبي واليابان والمملكة المتحدة.