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

场景结构,两个普通模型

第一种 脚本所挂载的物体才可以被拖拽 【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.mzph.cn/bicheng/29876.shtml

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

相关文章

python GUI开发: tkinter菜单创建,记事本和画图软件综合项目的实战演练

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

【0287】Postgres内核在pg_internal.init不存在情况下的relcache cache初始化实现

上一篇: 【0264】深入分析relcache(relation descriptor cache)缓存初始化第2阶段(2) 0. 前言 在用户未CREATE DATABASE 情况(即initdb创建了Postgres数据库集簇,或有过psql登录数据库动作)下,PGDATA/global目录下是还不存在pg_internal.init文件的,因此,启动Post…

elasticsearch安装(centos7)

先给出网址 elasticsearch:Download Elasticsearch | Elastic elasticKibana:Download Kibana Free | Get Started Now | Elastic Logstash:Download Logstash Free | Get Started Now | Elastic ik分词:Releases infinilabs/…

百度智能云推出智能运维工具,云助手让云服务器运维更简单

为了提升云服务器执行命令的效率,百度智能云发布了 SmartTerm 远程连接终端。不止于此,为了更加极致地提升运维效率,我们又推出了「云助手」这款轻量快捷的运维工具。 ​ 只有做过云服务器运维的人才知道管理上万台云服务器有多崩溃。在海量…

Ruby 数据库访问 - DBI 教程

Ruby 数据库访问 - DBI 教程 本文将详细介绍如何使用 Ruby 的 DBI(Database Interface)库来访问和操作数据库。DBI 是 Ruby 语言中一个常用的数据库接口库,它提供了一套统一的接口来访问不同的数据库系统,如 MySQL、PostgreSQL、SQLite 等。通过本文的学习,您将掌握如何使…

了解振弦采集仪在建筑物安全监测中的应用与研究

了解振弦采集仪在建筑物安全监测中的应用与研究 摘要:河北稳控科技振弦采集仪是一种常用的结构物安全监测设备,广泛应用于建筑物、桥梁、塔楼等工程结构的监测。本文将从振弦采集仪的原理、应用案例和研究进展等方面进行详细介绍,以便更好地…

解决python从TD数据库取50w以上大量数据慢的问题

1.问题背景描述 python项目中的时序数据都存放在TD数据库中,数据是秒级存入的,当查询一周数据时将超过50w数据量,这是一次性获取全量数据到python程序很慢,全流程10秒以上,希望进行优化加速 2.排查 首先&#xff0c…

vue中使用发布订阅的方式进行vue组件之间的通信

全部实现代码如下&#xff1a; header.vue组件的相关代码 Search GitHub Users <input type“button” value“Search” class“btn btn-primary” placeholder“请输入 github 用户名” click“search”> main组件的相关代码 请输入搜索用户的名称 loading {{error…

springboot3 基础特性(1)

文章目录 一、SpringApplication三种方式1.1 基础方式1.2.自定义 SpringApplication1.3、FluentBuilder API 二、自定义Banner三、Profiles3.1 什么是 Profiles &#xff1f;3.2 声明Profiles3.3 激活配置文件3.3.1 分组3.3.2 环境包含3.3.3 激活方式3.3.4 配置优先级 一、Spri…

常用告警规则

Node_exporter告警规则 NodeCPUUsageHigh: yaml 复制代码 alert: NodeCPUUsageHigh expr: (100 - (avg by (instance) (rate(node_cpu_seconds_total{mode“idle”}[5m])) * 100)) > 80 for: 5m labels: severity: critical annotations: summary: “High CPU usage detec…

STM32单片机USART串口详解

文章目录 1. 通信接口概述 2. 串口通信 3. 硬件电路 4. 电平标准 5. 串口参数及时序 5.1 数据帧的组成 5.2 起始位 5.3 数据位 5.4 校验位 5.5 停止位 5.6 波特率 5.7 数据帧传输过程示例 6. 串口时序 7. USART概述 8. USART框图 9. USART基本结构 10. 数据帧…

力扣793. 阶乘函数后 K 个零

Problem: 793. 阶乘函数后 K 个零 文章目录 题目描述思路即解法复杂度Code 题目描述 思路即解法 1.根据题意可知即是要求取满足条件的n最小是多少&#xff0c;最大是多少&#xff0c;最大值和最小值一减&#xff0c;就可以算出来有多少个n满足条件了。 2.由于题目中的阶乘存在单…

乡村养老服务管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;医疗人员管理&#xff0c;乡村志愿者管理&#xff0c;文娱活动管理&#xff0c;活动报名管理&#xff0c;医疗保健管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;文娱活…

确保数据一致性

目录 事务 ACID 属性 事务的作用 示例 隔离级别 事务管理的重要性 检查点 概述 检查点的好处 检查点的执行策略 检查点操作示例 并发控制 概述 并发控制技术 优点 缺点 适用场景 在数据库管理系统中&#xff0c;保持数据一致性至关重要。即使在系统故障或并发…

简单记录一下命名规则

简单记录一下命名规则 1. 记录一下 在编程中&#xff0c;命名规则&#xff08;也称为命名约定&#xff09;是非常重要的&#xff0c;它可以帮助我们编写出更易于理解和维护的代码。一直记不住到底有哪些&#xff0c;稍微记一下&#xff01; 以下是一些常见的命名规则&#xf…

软件推荐 caj2pdf

## 推荐内容 用 zotero 管理文献时&#xff0c;不能处理知网 caj 格时&#xff0c;有大佬做了相应的工作并开源了。 今天推荐这两个 GitHub 项目 https://github.com/caj2pdf/caj2pdfhttps://github.com/ElonH/caj2pdf_gui 一是 python 做的 caj 2 pdf 源码&#xff0c;二是结…

005-OSPF基本配置

OSPF基本配置 OSPF (Open Shortest Path First) 是一种链路状态路由协议&#xff0c;它属于内部网关协议&#xff08;IGP&#xff09;类别&#xff0c;用于在自治系统&#xff08;AS&#xff09;内部路由 IP 数据包。OSPF 通过使用 Dijkstra 算法计算最短路径树来确定到达每个…

Linux之旅: 基础知识点的终极指南

文章目录 1、Linux的目录结构2、ls命令3、管理文件和目录4、linux命令使用细节和技巧5、权限管理基本命令6、搜索命令7、管道符与重定向8、压缩和解压命令9、用户及vim编辑器10、用户和用户组管理一、Linux系统用户账号的基本管理二、Linux系统用户组的管理 1、Linux的目录结构…

【CSS in Depth2精译】1.1.2 行内样式~1.1.3 选择器的优先级

文章目录 1.1.2 行内样式1.1.3 选择器的优先级1.1.3.1 优先级的写法1.1.3.2 关于优先级的思考 1.1.2 行内样式 如果无法通过样式表来源规则解决样式冲突&#xff0c;浏览器则会考察它们是否通过 行内样式 作用于该元素。当使用 HTML 的 style 属性声明样式时&#xff0c;该样式…

js-promise、async/await

promise&#xff0c;简单就就是回调的一种简化了回调地狱&#xff08;如果多个调用是异步并且有结果依赖&#xff0c;那么就需要写成回调&#xff09;。 async/await&#xff0c;需要成对使用&#xff0c;是对promise的更高级的抽象&#xff0c; 比如 runAsync1() .then(fun…