Unity鼠标带动物体运动的三种方法

Posted 浔其丹华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity鼠标带动物体运动的三种方法相关的知识,希望对你有一定的参考价值。

目录

第一种:物体跟随鼠标移动。

第二种:鼠标拖动物体运动。

第三种:鼠标点到哪,物体运动到哪。


第一种:物体跟随鼠标移动。

描述:鼠标左键点击物体,将物体拾起,松开鼠标左键,物体跟随鼠标光标移动。再次点击鼠标左键,物体不在跟随鼠标,处于静止状态。

实现方法:射线。

1657678042844

写在前面:什么是射线?

简单来说就是当鼠标点击某个物体时,通过摄像机camera发射射线,射线与物体碰撞在一个点上。我们通过这个点找到该点对应的物体,先判断该物体是否是我们想要移动的(是否为可以移动的)。

如果是,那我们就拿到要操作的对象了,接下来就是让其坐标位置和鼠标位置保持一致即可。

具体解释和详细使用方法读者可自行查阅相关文献。

由此我们可以得出,我们要先声明一个射线的变量,还有碰撞点,由于有碰撞点,我们就必需为物体添加刚体组件。为了判断是不是我们要的物体,就定义一个布尔值。此外,为了区分鼠标点击这一动作是拾起还是放下物体,我们要声明一个标志量flag。为了得到该碰撞点对应的物体,我们要声明一个GameObject组件来得到它。

    private Ray ra;//声明射线
    private RaycastHit hit;//声明碰撞点
    private bool is_element =false;//判断是否是我们要的物体
    private int flag = 0;//标志量
    private GameObject Element;//控件

接下来就是干正事儿了。

判断鼠标是否按下:

if (Input.GetMouseButtonDown(0))
        
            
        

鼠标按下后,摄像机往按下位置发射射线。

    ra = Camera.main.ScreenPointToRay(Input.mousePosition);

判断射线是否跟物体发生碰撞得到碰撞点hit,并且同时判断该点对应的物体是不是我们要的。

判断物体是不是我们要的,这里我使用的是判断其标签是不是'element',因此应该给先给物体添加一个标签。

以上检测完后将布尔值设为true,并将碰撞点对应的物体给Element。

if (Physics.Raycast(ra, out hit)&&hit.collider.tag=="element")
            
                is_element = true;
                Element = hit.collider.gameObject;
            

判断鼠标按下是拾起还是放下。

这里我规定,鼠标拾起物体时,flag=1,再次点击flag=0。

总的判断部分代码为:

if (Input.GetMouseButtonDown(0))
        
            ra = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ra, out hit)&&hit.collider.tag=="element")
            
                is_element = true;
                Element = hit.collider.gameObject;
                Debug.Log(Element.transform.position);
                if (flag == 0)
                
                    flag = 1;
                
                else
                
                    flag = 0;
                
            
        

改变物体位置和鼠标位置一致。

我们要先得到鼠标的位置。

鼠标只有x和y轴,为了和物体位置一致,这里将鼠标的z轴设置为物体的坐标。

Vector3 mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y,
                Element.transform.position.z);

由于鼠标位置是屏幕坐标,而物体坐标是世界坐标,那我们就要把鼠标的屏幕坐标换成世界坐标。

Vector3 mouseSToW = Camera.main.ScreenToWorldPoint(mousePos);

细心的人可以发现,这里有个“陷阱”。

我们在把鼠标的二位坐标用三位坐标表示时,其z轴就是物体的世界坐标的位置,然后我们之后又把它当作屏幕坐标转化成世界坐标,那么最后的坐标肯定和以前的不一样了。

所以我们要先将物体的世界坐标转化成成屏幕坐标,在把屏幕坐标的z轴坐标给鼠标的屏幕坐标,然后一同转化成世界坐标。然后将得到的世界坐标给物体。

Vector3 targetScreenPos = Camera.main.WorldToScreenPoint(Element.transform.position);
Vector3 mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y,targetScreenPos.z);
Element.transform.position = Camera.main.ScreenToWorldPoint(mousePos);

当flag=1时,我们是拾起物体的,因此在flag变为0之前,我们的物体都是跟着鼠标移动的。

因此以上操作都是在flag==1以及is_element=true的条件下进行的。

if (flag == 1&&is_element)
        
            Vector3 targetScreenPos = Camera.main.WorldToScreenPoint(Element.transform.position);
            Vector3 mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y,targetScreenPos.z);
            Element.transform.position = Camera.main.ScreenToWorldPoint(mousePos);
        

代码汇总得:

using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using Unity.VisualScripting;
using UnityEngine;
 
public class camera : MonoBehaviour 
    //public Camera ca;
    private Ray ra;
    private RaycastHit hit;
    private bool is_element =false;
    private int flag = 0;
    private GameObject Element;

    // Use this for initialization
    void Start () 
        
    
	
    // Update is called once per frame
    void Update () 
        if (Input.GetMouseButtonDown(0))
        
            ra = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ra, out hit)&&hit.collider.tag=="element")
            
                is_element = true;
                Element = hit.collider.gameObject;
                if (flag == 0)
                
                    flag = 1;
                
                else
                
                    flag = 0;
                
            
        

        if (flag == 1&&is_element)
        
            Vector3 targetScreenPos = Camera.main.WorldToScreenPoint(Element.transform.position);
            Vector3 mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y,
                targetScreenPos.z);
            Element.transform.position = Camera.main.ScreenToWorldPoint(mousePos);
        
    

第二种:鼠标拖动物体运动。

描述:长按鼠标左键,物体跟随鼠标移动,松开鼠标左键,物体停止运动。

实现方法:协程或者射线。

有了前面的坐标转换基础,再加上对协程的基本了解,这个实现是非常容易的,我就直接上代码吧。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Cooperation : MonoBehaviour

    
    IEnumerator OnMouseDown()    //使用协程
    
        Vector3 targetScreenPos = Camera.main.WorldToScreenPoint(transform.position);//三维物体坐标转屏幕坐标
        //将鼠标屏幕坐标转为三维坐标,再计算物体位置与鼠标之间的距离
        var offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, targetScreenPos.z));
        
        while (Input.GetMouseButton(0))
        
            //将鼠标位置二维坐标转为三维坐标
            Vector3 mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, targetScreenPos.z);
            //将鼠标转换的三维坐标再转换成世界坐标+物体与鼠标位置的偏移量
            var targetPos = Camera.main.ScreenToWorldPoint(mousePos) + offset;
            transform.position = targetPos;
            yield return new WaitForFixedUpdate();//循环执行
        
    
    
    

使用射线的方法与之类似,这里就不在赘述。

第三种:鼠标点到哪,物体运动到哪。

描述:类似于英雄联盟的移动方式,鼠标点击平面任意位置,物体立即朝该方向移动。

1657681859641

首先,对移动物体和地面进行bake,操作如下:

选中Cube,点击window—>AI—>Navigation

然后将Navigation Static勾选上

 之后选择bake,地面和移动物体(Cube)都要勾选上。

 代码我就直接给了,比较容易理解。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class Nvgt : MonoBehaviour

    private NavMeshAgent navMeshAgent;
    void Start()
    
        navMeshAgent = gameObject.GetComponent<NavMeshAgent>();//初始化navMeshAgent
    
    void Update()
    
        if (Input.GetMouseButtonDown(0))//鼠标左键点下  
        
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//住摄像机向鼠标位置发射射线 
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))//射线检验 
            
                if (hit.collider.gameObject.tag == "Plane")
                
                    navMeshAgent.SetDestination(hit.point);//mHit.point就是射线和plane的相交点,实为碰撞点
                
            
        
    

Unity play模式下选中物体的三种方式

射线检测(需要碰撞器)

// 创建一条点击位置为光标位置的射线
Ray rays = Camera.main.ScreenPointToRay(Input.mousePosition);
//将射线以黄色的表示出来
Debug.DrawRay(rays.origin, rays.direction * 100, Color.yellow);
//创建一个RayCast变量用于存储返回信息
RaycastHit hit;
//将创建的射线投射出去并将反馈信息存储到hit中
if (Physics.Raycast(rays, out hit))

//获取被射线碰到的对象transfrom变量
target = hit.transform.position;

target就是被选中的物体的坐标

屏幕坐标对比(不需要碰撞器)

	//设置鼠标点击的精度
    WHrate = (float)Screen.width / (float)Screen.height;
    xdelta = Screen.width * delta / WHrate;
    ydelta = Screen.height * delta;
    Vector3 screenPos;
    Vector3 mousePos;
    foreach (Vector3 v3 in positions)
    
        //把包围盒中心点转化为屏幕坐标
        screenPos = Camera.main.WorldToScreenPoint(v3);
        mousePos = Input.mousePosition;

        //如果鼠标点击的位置在包围盒中心点屏幕坐标的一定范围内
        if (mousePos.x >= screenPos.x - xdelta && mousePos.x <= screenPos.x + xdelta
            && mousePos.y >= screenPos.y - ydelta && mousePos.y <= screenPos.y + ydelta)
        
            target = v3;
            break;
        

    

包围盒(不需要碰撞器)

//判断模型的包围盒是否与射线交互
if (meshes[i].bounds.IntersectRay(rays))

target = meshes[i].bounds.center;
break;

以上是关于Unity鼠标带动物体运动的三种方法的主要内容,如果未能解决你的问题,请参考以下文章

Unity中获取物体的尺寸(size)的三种方法

Unity play模式下选中物体的三种方式

Unity300个技巧检测物体在地面的三种方式

Unity常用的三种拖拽方法(内置方法 + 接口 + Event Trigger组件)

Unity3D中三种调用其他脚本函数的方法

unity中怎么让物体随着鼠标移动?