【Unity拖拽物体】实现对点中的3D物体进行拖拽的功能

news2025/2/23 23:03:56

场景结构,两个普通模型

第一种 脚本所挂载的物体才可以被拖拽 【PC鼠标版本】

using UnityEngine;

// 这个脚本实现了,本脚本所在的游戏物体能够被拖拽
public class DragObjectT : MonoBehaviour
{
    private Vector3 screenPoint; // 存储物体在屏幕上的位置
    private Vector3 offset; // 存储鼠标点击位置与物体实际位置的偏移量
    private bool isDragging = false; // 标志位,表示物体是否正在被拖拽

    void Update()
    {
        // 检测鼠标左键是否按下
        if (Input.GetMouseButtonDown(0))
        {
            RaycastHit hit;
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // 从摄像机发射一条射线到鼠标位置

            // 使用射线检测是否击中了某个物体
            if (Physics.Raycast(ray, out hit))
            {
                // 检查被击中的物体是否是本脚本所附加的物体
                if (hit.collider.gameObject == gameObject)
                {
                    screenPoint = Camera.main.WorldToScreenPoint(gameObject.transform.position); // 将物体的世界坐标转换为屏幕坐标
                    offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z)); // 计算偏移量
                    isDragging = true; // 设置拖拽标志位为真
                }
            }
        }

        // 检测鼠标左键是否持续按下并且物体正在被拖拽
        if (Input.GetMouseButton(0) && isDragging)
        {
            Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z); // 获取当前鼠标位置的屏幕坐标
            Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) + offset; // 将屏幕坐标转换为世界坐标并加上偏移量
            transform.position = curPosition; // 更新物体的位置
        }

        // 检测鼠标左键是否释放
        if (Input.GetMouseButtonUp(0))
        {
            isDragging = false; // 设置拖拽标志位为假
        }
    }
}

这个脚本通过检测鼠标输入来实现物体的拖拽功能。以下是详细的步骤解释:

  1. 变量声明

    • screenPoint:存储物体在屏幕上的位置。

    • offset:存储鼠标点击位置与物体实际位置的偏移量。

    • isDragging:标志位,表示物体是否正在被拖拽。

  2. Update方法

    • 检测鼠标左键是否按下(Input.GetMouseButtonDown(0))。

    • 如果按下,从摄像机发射一条射线到鼠标位置(Camera.main.ScreenPointToRay(Input.mousePosition))。

    • 使用射线检测是否击中了某个物体(Physics.Raycast(ray, out hit))。

    • 检查被击中的物体是否是本脚本所附加的物体(hit.collider.gameObject == gameObject)。

    • 如果是,将物体的世界坐标转换为屏幕坐标(Camera.main.WorldToScreenPoint(gameObject.transform.position)),并计算偏移量(offset)。

    • 设置拖拽标志位为真(isDragging = true)。

  3. 拖拽逻辑

    • 检测鼠标左键是否持续按下并且物体正在被拖拽(Input.GetMouseButton(0) && isDragging)。

    • 获取当前鼠标位置的屏幕坐标(curScreenPoint)。

    • 将屏幕坐标转换为世界坐标并加上偏移量(curPosition)。

    • 更新物体的位置(transform.position = curPosition)。

  4. 释放逻辑

    • 检测鼠标左键是否释放(Input.GetMouseButtonUp(0))。

    • 设置拖拽标志位为假(isDragging = false)。

 

以下是适用于移动端触摸屏的版本:

using UnityEngine;

// 这个脚本实现了,本脚本所在的游戏物体能够被触摸拖拽
public class DragObjectT : MonoBehaviour
{
    private Vector3 screenPoint; // 存储物体在屏幕上的位置
    private Vector3 offset; // 存储触摸点击位置与物体实际位置的偏移量
    private bool isDragging = false; // 标志位,表示物体是否正在被拖拽

    void Update()
    {
        // 检测是否有触摸输入
        if (Input.touchCount > 0)
        {
            Touch touch = Input.GetTouch(0); // 获取第一个触摸点

            // 检测触摸开始
            if (touch.phase == TouchPhase.Began)
            {
                RaycastHit hit;
                Ray ray = Camera.main.ScreenPointToRay(touch.position); // 从摄像机发射一条射线到触摸位置

                // 使用射线检测是否击中了某个物体
                if (Physics.Raycast(ray, out hit))
                {
                    // 检查被击中的物体是否是本脚本所附加的物体
                    if (hit.collider.gameObject == gameObject)
                    {
                        screenPoint = Camera.main.WorldToScreenPoint(gameObject.transform.position); // 将物体的世界坐标转换为屏幕坐标
                        offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, screenPoint.z)); // 计算偏移量
                        isDragging = true; // 设置拖拽标志位为真
                    }
                }
            }

            // 检测触摸移动并且物体正在被拖拽
            if (touch.phase == TouchPhase.Moved && isDragging)
            {
                Vector3 curScreenPoint = new Vector3(touch.position.x, touch.position.y, screenPoint.z); // 获取当前触摸位置的屏幕坐标
                Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) + offset; // 将屏幕坐标转换为世界坐标并加上偏移量
                transform.position = curPosition; // 更新物体的位置
            }

            // 检测触摸结束
            if (touch.phase == TouchPhase.Ended)
            {
                isDragging = false; // 设置拖拽标志位为假
            }
        }
    }
}

这个脚本通过检测触摸输入来实现物体的拖拽功能。以下是详细的步骤解释:

  1. 变量声明

    • screenPoint:存储物体在屏幕上的位置。

    • offset:存储触摸点击位置与物体实际位置的偏移量。

    • isDragging:标志位,表示物体是否正在被拖拽。

  2. Update方法

    • 检测是否有触摸输入(Input.touchCount > 0)。

    • 获取第一个触摸点(Touch touch = Input.GetTouch(0))。

    • 检测触摸开始(touch.phase == TouchPhase.Began)。

    • 如果触摸开始,从摄像机发射一条射线到触摸位置(Camera.main.ScreenPointToRay(touch.position))。

    • 使用射线检测是否击中了某个物体(Physics.Raycast(ray, out hit))。

    • 检查被击中的物体是否是本脚本所附加的物体(hit.collider.gameObject == gameObject)。

    • 如果是,将物体的世界坐标转换为屏幕坐标(Camera.main.WorldToScreenPoint(gameObject.transform.position)),并计算偏移量(offset)。

    • 设置拖拽标志位为真(isDragging = true)。

  3. 拖拽逻辑

    • 检测触摸移动并且物体正在被拖拽(touch.phase == TouchPhase.Moved && isDragging)。

    • 获取当前触摸位置的屏幕坐标(curScreenPoint)。

    • 将屏幕坐标转换为世界坐标并加上偏移量(curPosition)。

    • 更新物体的位置(transform.position = curPosition)。

  4. 释放逻辑

    • 检测触摸结束(touch.phase == TouchPhase.Ended)。

    • 设置拖拽标志位为假(isDragging = false)。

第二种 全局脚本,所有有碰撞器的物体都可以被拖拽

using UnityEngine;

public class GlobalDragManager : MonoBehaviour
{
    private GameObject draggedObject;
    private Vector3 screenPoint;
    private Vector3 offset;

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            RaycastHit hit;
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            if (Physics.Raycast(ray, out hit))
            {
                draggedObject = hit.collider.gameObject;
                screenPoint = Camera.main.WorldToScreenPoint(draggedObject.transform.position);
                offset = draggedObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
            }
        }

        if (Input.GetMouseButton(0) && draggedObject != null)
        {
            Vector3 curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
            Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) + offset;
            draggedObject.transform.position = curPosition;
        }

        if (Input.GetMouseButtonUp(0))
        {
            draggedObject = null;
        }
    }
}

 

这个脚本的工作原理如下:

  1. Update方法

    • 在每一帧中,检查是否按下了鼠标左键(Input.GetMouseButtonDown(0))。

    • 如果按下了鼠标左键,发射一条从主摄像机到鼠标位置的射线(Physics.Raycast(ray, out hit))。

    • 如果射线击中了某个物体(hit.collider.gameObject),则将该物体的引用存储在draggedObject变量中,并记录物体的屏幕位置和偏移量。

  2. 拖动逻辑

    • 如果鼠标左键保持按下并且draggedObject不为空,根据当前鼠标位置计算物体的新位置(curPosition),并将其应用到物体的变换位置上(draggedObject.transform.position = curPosition)。

  3. 释放物体

    • 当鼠标左键释放时(Input.GetMouseButtonUp(0)),将draggedObject设置为null,表示没有物体正在被拖动。

通过这种方式,你只需要挂载一个脚本到场景中的某个空游戏对象上,就可以实现对所有带有碰撞器的物体的拖拽功能,而不需要为每个物体单独挂载脚本。

适用于移动端触摸屏交互的版本

using UnityEngine;

public class GlobalDragManager : MonoBehaviour
{
    private GameObject draggedObject;
    private Vector3 screenPoint;
    private Vector3 offset;

    void Update()
    {
        if (Input.touchCount > 0)
        {
            Touch touch = Input.GetTouch(0);

            if (touch.phase == TouchPhase.Began)
            {
                RaycastHit hit;
                Ray ray = Camera.main.ScreenPointToRay(touch.position);

                if (Physics.Raycast(ray, out hit))
                {
                    draggedObject = hit.collider.gameObject;
                    screenPoint = Camera.main.WorldToScreenPoint(draggedObject.transform.position);
                    offset = draggedObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, screenPoint.z));
                }
            }

            if (touch.phase == TouchPhase.Moved && draggedObject != null)
            {
                Vector3 curScreenPoint = new Vector3(touch.position.x, touch.position.y, screenPoint.z);
                Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) + offset;
                draggedObject.transform.position = curPosition;
            }

            if (touch.phase == TouchPhase.Ended)
            {
                draggedObject = null;
            }
        }
    }
}

这个脚本的工作原理如下:

  1. Update方法

    • 在每一帧中,检查是否有触摸输入(Input.touchCount > 0)。

    • 如果有触摸输入,获取第一个触摸点(Touch touch = Input.GetTouch(0))。

  2. 触摸开始

    • 如果触摸开始(touch.phase == TouchPhase.Began),发射一条从主摄像机到触摸位置的射线(Physics.Raycast(ray, out hit))。

    • 如果射线击中了某个物体(hit.collider.gameObject),则将该物体的引用存储在draggedObject变量中,并记录物体的屏幕位置和偏移量。

  3. 触摸移动

    • 如果触摸移动(touch.phase == TouchPhase.Moved)并且draggedObject不为空,根据当前触摸位置计算物体的新位置(curPosition),并将其应用到物体的变换位置上(draggedObject.transform.position = curPosition)。

  4. 触摸结束

    • 当触摸结束(touch.phase == TouchPhase.Ended),将draggedObject设置为null,表示没有物体正在被拖动。

通过这种方式,你可以在移动端触摸屏上实现对所有带有碰撞器的物体的拖拽功能,而不需要为每个物体单独挂载脚本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1837254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多行文本的文字展示全部和收起功能

组件代码&#xff1a; <template><!-- 外层容器&#xff0c;使用相对定位 --><div class"relative"><!-- 文本容器&#xff0c;根据 expanded 状态决定是否应用 line-clamp-4 类 --><div :class"{ line-clamp-4: !expanded }"…

没有二十年功力,写不出这一行代码!

这篇文章要从一个奇怪的注释说起&#xff0c;就是下面这张图&#xff1a; 我们可以不用管具体的代码逻辑&#xff0c;只是单单看这个 for 循环。 在循环里面&#xff0c;专门有个变量 j&#xff0c;来记录当前循环次数。 第一次循环以及往后每 1000 次循环之后&#xff0c;进…

阻力支撑相对强度(RSRS)选股系列报告之三

https://download.csdn.net/download/SuiZuoZhuLiu/89447699?spm1001.2014.3001.5503https://download.csdn.net/download/SuiZuoZhuLiu/89447699?spm1001.2014.3001.5503

电脑密码忘记了怎么办?3步教你找回密码!

在日常使用电脑的过程中&#xff0c;忘记密码是一件令人头痛的事情。如果您不慎忘记了电脑的登录密码&#xff0c;无法进入系统进行工作和娱乐&#xff0c;这时需要找到合适的解决方案来恢复对电脑的访问权限。那么电脑密码忘记了怎么办呢&#xff1f;本文将介绍三种解决方法&a…

Uncaught TypeError: Cannot read properties of null (reading ‘isCE‘)

问题描述 使用 view-ui-plus 加 vue3 开发项目&#xff0c;本地启动项目正常&#xff0c;但其他人将代码拉下来&#xff0c;启动项目时报错 Uncaught TypeError: Cannot read properties of null (reading isCE)&#xff1a; 原因分析&#xff1a; 尝试将 mode_nodules 文件删…

C++实时检测耳机的插入与拔出(附源码)

目录 1、实现继承于IMMNotificationClient接口类的CMMNotificationClient类,实时感知音频设备变化的通知事件 2、在CMMNotificationClient的构造函数中初始化多媒体设备COM接口,设置回调类指针 3、通过获取音频设备接口下外设的KSJACK_DESCRIPTION 信息判断耳机的连接状态…

ArcGIS arcpy代码工具——关于工具使用的软件环境说明

系列文章目录 ArcGIS arcpy代码工具——批量对MXD文件的页面布局设置修改 ArcGIS arcpy代码工具——数据驱动工具批量导出MXD文档并同步导出图片 ArcGIS arcpy代码工具——将要素属性表字段及要素截图插入word模板 ArcGIS arcpy代码工具——定制属性表字段输出表格 ArcGIS arc…

鸿蒙实现自定义Tabbar样式,显示数字红点提示

前言&#xff1a; DevEco Studio版本&#xff1a;4.0.0.600 Tabs的链接参考&#xff1a;OpenHarmony Tabs TabContent的链接参考&#xff1a;OpenHarmony TabContent 通过查看链接参考我们知道可以通过TabContent的tabBar来实现自定义TabBar样式&#xff08;CustomBuilder&…

“运动过量”?想多了,普通骑友没那能力和意志力,好好骑车吧

最近听到“运动过量”这个词挺多的&#xff0c;身为骑行爱好者的校长&#xff0c;感觉又好笑又无奈&#xff0c;所以想写点东西&#xff0c;这篇文通过分析普通骑友的运动习惯、能力和意志力&#xff0c;探讨了“运动过量”这一概念在骑行领域中的适用性。文章指出&#xff0c;…

基于PHP的民宿管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的民宿管理系统 一 介绍 此民宿管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端jquery.js和echarts.js。系统角色分为用户和管理员。用户可以在线浏览和预订民宿&#xff0c;管理员登录后台进行相关管理等。(在系统…

精度丢失引起的支付失败问题

问题描述 在提交订单时候&#xff0c;输入充值金额和优惠码&#xff0c;后台会返回具体的订单信息&#xff0c;如下图&#xff0c;支付金额应该是1 * (1 - 0.09) 0.91&#xff08;这个是理想状态&#xff09;&#xff0c;但是表单显示的是0.90999997&#xff0c; 然后点击确…

大模型系列:提示词管理

既然大模型应用的编程范式是面向提示词的编程&#xff0c;需要建立一个全面且结构化的提示词库&#xff0c; 对提示词进行持续优化也是必不可少的&#xff0c;那么如何在大模型应用中更好的管理提示词呢&#xff1f; 1. 提示词回顾 提示词在本质上是向大型语言模型&#xff08;…

算法设计与分析 实验3 回溯法求地图填色问题

目录 一、实验目的 二、背景知识 三、实验内容 四、算法思想 未优化的回溯算法 节点选择-最小剩余值准则&#xff08;MRV&#xff09; 节点选择-最多约束准则&#xff08;DH&#xff09; 颜色选择-最少约束选择 数据结构的选择 向前探查 颜色轮换&#xff08;贪心置…

构建专属Web SSH客户端:从零到一打造你的在线运维利器

随着云服务和远程工作的普及&#xff0c;能够随时随地访问服务器变得越来越重要。虽然市面上已有不少成熟的SSH客户端&#xff0c;但打造一个属于自己的Web版SSH工具&#xff0c;不仅能根据个人需求定制功能&#xff0c;还能享受灵活访问的便利。 本文将带你一步步实践&#xf…

linux——ansible实验

要求 0.进入servera进行准备工作&#xff0c;做一些清理 1&#xff09;停止httpd服务&#xff0c;清除httpd软件包、配置文件、主页文件 2&#xff09;清理/etc/hosts文件中的内容&#xff0c;只保留最上面默认的两行 &#xff08;127.0.0.1和::1这两行&#xff09; 1.根据之前…

004-配置交换机ssh远程登录

配置交换机ssh远程登录 注意事项 要远程的本机电脑必须与该交换机在同一个网段&#xff0c;以下实验在172.16.12段下模拟&#xff0c;本地ip设置为172.16.12.10&#xff0c;交换机的ip设置为172.16.12.254 将密码设置为明文&#xff08;simple&#xff09;是不安全的&#x…

分布式技术导论 — 探索分析从起源到现今的巅峰之旅(消息队列)

探索分析从起源到现今的巅峰之旅 分布式队列 - Kafka架构特性可扩展性磁盘优化与顺序访问大容量存储与历史数据利用高效数据封装与压缩智能内存管理与OS缓存利用 Kafka发布/订阅模型Kafka架构分析Producer和Consumer接口交互Producer通过Topic发送数据Consumer通过Topic消费数据…

【Ubuntu】--- 创建用户 删除用户 及其他用户操作大全 持续更新中

在编程的艺术世界里&#xff0c;代码和灵感需要寻找到最佳的交融点&#xff0c;才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里&#xff0c;我们将共同追寻这种完美结合&#xff0c;为未来的世界留下属于我们的独特印记。 【Ubuntu】--- 创建用户 删除用户 及其他…

React常用方法汇总【更新中】

文章目录 前言创建项目启动命令列表渲染useEffect 异步函数使用方法useEffect 异步函数清除方法控制组件显示隐藏axios 安装使用 前言 运行 react 需要先安装 node.js&#xff0c;具体安装步骤可以参考这篇文章 https://blog.csdn.net/weixin_43721000/article/details/134284…

C++ 57 之 静态联编和动态联编

#include <iostream> #include <string> using namespace std;// 动态多态产生条件: // 1.要有继承关系 // 2.父类中有虚函数、子类要重写父类的虚函数 // 3.父类的指针或引用指向子类的对象class Animal{ public:virtual void speak(){ // 虚函数 父类中的vir…