Unity XR 手势射击控制脚本(适用于任何可手势识别的设备)

这个主要是玩了Meteoric VR  觉得操作蛮不错的,就想复刻一个类似的游戏出来,但是在手势发射物体时遇到了难题;
不能直接拿手心的方向,因为人的手在往前推的过程中手心方向非常不稳定,根本无法做到想打到什么就打到什么,人也很难控制自己每一次推动各个关节的角度都几乎一致;
在充分研究Meteoric VR的操作模式之后,发现他的指示方向(射击方向),跟手的旋转无关,只跟手、摄像机两个点的向量有关,这两个点形成的新的方向具有稳定的特性;
于是沿着这个思路,在官方FirstHand示例场景中将手势识别+推手检测发射物体的功能复现了出来;

Unity XR手势识别+推手发射物体(适用任何设备)

主要特点:

  1. 方向指示器的动态调整:根据手部到摄像机的距离和角度差,方向指示器会向左或向右偏移,增加了射击的准确性和动态反应性。
  2. 推动检测与发射控制:当检测到手部在短时间内快速移动超过设定的阈值时,将触发子弹的发射。这模仿了真实世界中的快速射击动作。
  3. 冷却时间控制:为防止过快连续射击,脚本中设置了一个冷却时间,确保每次射击后有一定的间隔,避免子弹的无限快速发射。
  4. 灵活的偏移调整:脚本允许根据玩家与摄像机之间的距离动态调整射击偏移,距离越近,偏移效果越明显,增强游戏的实际操作感和挑战性。

技术细节

  • 使用Unity引擎的Transform组件来计算和更新物体的位置和旋转。
  • 利用Vector3.Lerp进行方向的平滑过渡,使射击动作更加自然。
  • 根据摄像机的前向向量和手部位置动态调整射击方向,提供更为直观稳定的射击体验。

using UnityEngine;

public class GestureShooting : MonoBehaviour
{
    public GameObject directionIndicatorPrefab; // 方向指示器的预制体
    public GameObject projectilePrefab; // 子弹的预制体

    private GameObject directionIndicator; // 实例化的方向指示器对象
    private Transform cameraTransform; // 摄像机的Transform组件

    public float cameraInfluenceWeight = 0.1f; // 摄像机对计算方向的影响权重
    public float maxOffsetDistance = 1f; // 最大偏移距离
    public float effectDistance = 0.6f; // 影响偏移的距离阈值

    private Vector3 lastHandPosition; // 记录上一帧手的位置
    private float pushThreshold = 0.05f; // 手部推动的阈值
    private float pushCooldown = 0.2f; // 推动后的冷却时间
    private float lastPushTime; // 上次推动的时间
    private bool isRightHand; // 标记是否是右手

    private void Start()
    {
        cameraTransform = Camera.main.transform; // 获取摄像机的Transform
        lastHandPosition = transform.position; // 初始化手的位置
        CreateDirectionIndicator(); // 创建方向指示器
        DetermineHandSide();
        Debug.Log("Gesture shooting system initialized."); // 输出初始化完成的日志
    }

    private void DetermineHandSide()
    {
        Transform current = transform;
        for (int i = 0; i < 3; i++) // 检查3层父对象
        {
            if (current != null)
            {
                if (current.name.Contains("RightHand") || current.name.Contains("Hand_R"))
                {
                    isRightHand = true;
                    return;
                }
                if (current.name.Contains("LeftHand") || current.name.Contains("Hand_L"))
                {
                    isRightHand = false;
                    return;
                }
                current = current.parent;
            }
        }
    }
    private void Update()
    {
        Vector3 bulletDirection = CalculateDirection(); // 计算射击方向
        UpdateDirectionIndicator(bulletDirection); // 更新方向指示器
        CheckHandPush(bulletDirection); // 检查手部是否推动
        lastHandPosition = transform.position; // 更新手的位置
        lastPushTime -= Time.deltaTime; // 更新冷却时间
    }

    private void CreateDirectionIndicator()
    {
        if (directionIndicatorPrefab != null)
        {
            directionIndicator = Instantiate(directionIndicatorPrefab, transform.position, Quaternion.identity); // 实例化方向指示器
        }
        else
        {
            Debug.LogError("Direction indicator prefab is not set."); // 如果预制体未设置,输出错误日志
        }
    }

    private void UpdateDirectionIndicator(Vector3 direction)
    {
        if (directionIndicator != null)
        {
            directionIndicator.transform.position = transform.position; // 设置方向指示器的位置
            directionIndicator.transform.rotation = Quaternion.LookRotation(-direction); // 设置方向指示器的朝向
        }
        else
        {
            Debug.LogError("Direction indicator instance is missing."); // 如果实例丢失,输出错误日志
        }
    }

    private Vector3 CalculateDirection()
    {
        Vector3 handToCamera = cameraTransform.position - transform.position; // 计算从手到摄像机的向量
        Vector3 cameraForward = cameraTransform.forward; // 获取摄像机的前向向量
        Vector3 baseDirection = handToCamera.normalized; // 标准化手到摄像机的向量
        Vector3 adjustedDirection = Vector3.Lerp(baseDirection, cameraForward, cameraInfluenceWeight); // 根据权重插值计算调整后的方向

        float distance = handToCamera.magnitude; // 计算手到摄像机的距离
        float distanceFactor = Mathf.Clamp01(1 - distance / effectDistance); // 根据距离计算偏移影响的权重
       
        adjustedDirection += cameraTransform.right * (isRightHand ? maxOffsetDistance : -maxOffsetDistance) * distanceFactor; // 根据手的类型调整偏移方向

        return adjustedDirection.normalized; // 返回归一化的方向
    }

    private void CheckHandPush(Vector3 direction)
    {
        float moveDistance = Vector3.Distance(transform.position, lastHandPosition); // 计算手移动的距离
        if (moveDistance > pushThreshold && lastPushTime <= 0) // 如果移动距离大于阈值且冷却时间结束
        {
            LaunchProjectile(direction); // 发射子弹
            lastPushTime = pushCooldown; // 重置冷却时间
        }
    }

    private void LaunchProjectile(Vector3 direction)
    {
        if (projectilePrefab != null)
        {
            GameObject projectile = Instantiate(projectilePrefab, transform.position, Quaternion.LookRotation(-direction)); // 实例化子弹
            Rigidbody rb = projectile.GetComponent<Rigidbody>(); // 获取子弹的Rigidbody组件
            if (rb != null)
            {
                rb.AddForce(-direction * 20f, ForceMode.Impulse); // 给子弹添加力
                Debug.Log("Projectile launched."); // 输出子弹发射的日志
            }
            else
            {
                Debug.LogError("Projectile prefab does not have a Rigidbody component."); // 如果子弹预制体缺少Rigidbody组件,输出错误日志
            }
        }
        else
        {
            Debug.LogError("Projectile prefab is not set."); // 如果子弹预制体未设置,输出错误日志
        }
    }
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值