با استفاده از ARCore برای Jetpack XR با دست کار کنید

دستگاه‌های XR قابل اجرا
این راهنما به شما کمک می‌کند تا برای این نوع دستگاه‌های XR تجربه ایجاد کنید.
هدست‌های XR
عینک‌های XR سیمی

ARCore برای Jetpack XR می‌تواند اطلاعاتی در مورد دست‌های شناسایی‌شده‌ی کاربر ارائه دهد و اطلاعات مربوط به حالت دست‌ها و مفاصل مرتبط با آنها را ارائه دهد. این داده‌های دست می‌توانند برای اتصال موجودیت‌ها و مدل‌ها به دست‌های کاربر، به عنوان مثال، یک منوی ابزار، استفاده شوند:

دسترسی به یک جلسه

از طریق یک Session زمان اجرای Jetpack XR که برنامه شما می‌تواند ایجاد کند ، به اطلاعات دستی دسترسی پیدا کنید.

پیکربندی جلسه

ردیابی دست به طور پیش‌فرض در جلسات 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)

هنگام توسعه تشخیص سفارشی برای حرکات دست، نکات زیر را در نظر داشته باشید:

  • کاربران ممکن است تفسیر متفاوتی از هر حرکت داده شده داشته باشند. برای مثال، برخی ممکن است حرکت "ایست" را به باز کردن انگشتان نسبت دهند، در حالی که برخی دیگر ممکن است نزدیک کردن انگشتان را شهودی‌تر بدانند.
  • ممکن است حفظ برخی از حرکات ناراحت کننده باشد. از حرکات شهودی استفاده کنید که به دست کاربر فشار نمی‌آورند.

دست دوم کاربر را تعیین کنید

سیستم اندروید، ناوبری سیستم را بر اساس دست اصلی کاربر، همانطور که توسط کاربر در تنظیمات سیستم مشخص شده است، قرار می‌دهد. برای جلوگیری از تداخل با حرکات ناوبری سیستم، از دست دوم برای حرکات سفارشی خود استفاده کنید:

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 علائم تجاری متعلق به گروه Khronos هستند و به عنوان یک علامت تجاری در چین، اتحادیه اروپا، ژاپن و بریتانیا ثبت شده‌اند.