Unity拖拽物体到另外一个物体中的检测相关脚本(萝卜和坑)

来源:互联网 发布:数据恢复精灵如何使用 编辑:程序博客网 时间:2024/04/27 21:16

从前。。。有个兔子要把得来的几个萝卜埋到坑里面,现在就是检测萝卜埋坑里的方法,讲一下需求:
1.鼠标拖动萝卜可以放进不同的坑中。
2.当萝卜没有拖到坑中时就返回到原来位置
3.当萝卜拖到坑里时萝卜就放进坑里。
这里写图片描述
我是用TriggerEvent来检测的,顺便做了一些逻辑,语言描述不够好,还是上代码吧:

using UnityEngine;/// <summary>/// 拖拽接口/// </summary>public interface IDrag{    /// <summary>    /// 开始拖动    /// </summary>    void OnStartDrag();    /// <summary>    /// 拖动中    /// </summary>    void OnDrag();    /// <summary>    /// 放下    /// </summary>    void OnDrop();}/// <summary>/// 事件监听处理/// </summary>public interface IDispatch{    /// <summary>    /// 添加事件监听    /// </summary>    void AddEvent();    /// <summary>    /// 移除对事件的监听    /// </summary>    void RemoveEvent();}

上面是接口,用来描述我拖动中需要做的事情,下面是实现:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class DraggerImpler : IDrag{    private GameObjDrag host;    public DraggerImpler(GameObjDrag _host)    {        host = _host;    }    public void OnStartDrag()    {        host.IsDraging = true;    }    public void OnDrag()    {        host.IsDraging = true;    }    public void OnDrop()    {        host.IsDraging = false;        if (host.IsReturn)        {            host.targetTransform.position = host.startTransform.position;        }        else        {            host.targetTransform.position = host.currTransform.position;        }      }}

上面这个脚本实现了Idrag 接口,因为没想到开始拖和正在拖需要做什么就先不管他,当放下的时候我做了个判断就是这个:

public void OnDrop()    {        host.IsDraging = false;        if (host.IsReturn)        {            host.targetTransform.position = host.startTransform.position;        }        else        {            host.targetTransform.position = host.currTransform.position;        }      }

大概意思是如果需要返回了就把初始记录的位置给targetTransform,如果不需要返回就把currTransform的位置给targetTransform.
下面这个脚本是用来做碰撞检测后事件处理,萝卜进坑是和萝卜出坑时需要进行的逻辑写在这里。
这里有另外一个新需求:
当这个坑被萝卜占了,其他萝卜不能再进入这个坑
解决方案:所以给我在萝卜坑加了一个标记,当萝卜离开坑的时候就把这个标记移除。
出现问题:我怎么知道我移除的是自己的标记?
解决方案:给每个标记一个唯一的ID,根据ID来确定是不是自己加的标记
出现问题:当两个坑离得特别近的时候,从这个坑移动到另外一个坑的时候,会先触发一次进入下个坑的进入
事件,然后再触发这个坑的离开事件,导致我记录的ID被修改成新的坑的ID,导致我没办法移除我离开的那个坑的标记。(这个坑就没有萝卜,而且也不能再把萝卜放进去了)。
解决方案:把标记做一个列表List,当进入坑的时候往标记列表里加一个ID,当离开坑时就从列表里查是否有要离开这个坑的ID,有的话移除这个坑的标记,并且从列表里移除这个ID*

using System.Collections.Generic;using UnityEngine;public class TriggerImpler : IDispatch{    /// <summary>    /// 监听到后对Obj进行操作    /// </summary>    GameObjDrag _obj;    /// <summary>    /// 触发器的事件监听    /// </summary>    TriggerEvent _te;    /// <summary>    /// 标记ID列表    /// </summary>    List<int> _markIDs;    public TriggerImpler(GameObjDrag obj)    {        _te = TriggerEvent.AddComponentToGameObject(obj.gameobject);        _obj = obj;        _markIDs = new List<int>();    }    public void AddEvent()    {        _te.TriggerEnter += OnEnter;        _te.TriggerExit += OnExit;    }    public void RemoveEvent()    {        _te.TriggerEnter -= OnEnter;        _te.TriggerExit -= OnExit;    }    public void OnExit(GameObject obj)    {        //如果这个标记是自己加的才可以移除,不是自己加的不移除        Mark ma = obj.GetComponent<Mark>();        if (ma != null)        {            if (_markIDs.Contains(ma.GetInstanceID()))            {                UnityEngine.GameObject.Destroy(ma);                _markIDs.Remove(ma.GetInstanceID());            }        }        //如果退出的obj不是当前记录的obj就不返回        if (_obj.currTransform.position == obj.transform.position)        {            _obj.IsReturn = true;            _obj.currTransform.position = Vector3.zero;        }    }    public void OnEnter(GameObject obj)    {        //如果进入的这个Obj被标记了,那么不赋值,如果没被标记,那么标记Obj,并且赋值        Mark ma = obj.GetComponent<Mark>();        if (ma == null)        {            ma = obj.AddComponent<Mark>();            //_markID = ma.GetInstanceID();            _markIDs.Add(ma.GetInstanceID());            _obj.IsReturn = false;            _obj.currTransform.position = obj.transform.position;                  }    }}
using System;using UnityEngine;public class TriggerEvent : MonoBehaviour {    public Action<GameObject> TriggerEnter;    public Action<GameObject> TriggerExit;    /// <summary>    /// 往一个物体上添加这个事件监听类    /// </summary>    /// <param name="obj"></param>    /// <returns></returns>    public static TriggerEvent AddComponentToGameObject(GameObject obj)    {        TriggerEvent com = obj.GetComponent<TriggerEvent>();        if (com == null)        {            com = obj.AddComponent<TriggerEvent>();        }        return com;    }    public void OnTriggerEnter(Collider other)    {        //Debug.Log("TriggerEnter" + other.gameObject.name);        TriggerEnter(other.gameObject);    }    public void OnTriggerExit(Collider other)    {        //Debug.Log("TriggerExit" + other.gameObject.name);        TriggerExit(other.gameObject);    }}

上面这个脚本时用来做碰撞检测的,注意要给萝卜加刚体(rigidbody)和碰撞器(collider),坑只加碰撞器(collider)就好了,把碰撞器的isTrigger勾选。
做标记的脚本:

using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// 用来做标记的一个脚本/// </summary>public class Mark : MonoBehaviour {    /// <summary>    /// 不允许用户手动修改,测试时看ID    /// </summary>    public int ID;    // Use this for initialization    void Start () {        ID = GetInstanceID();        //Debug.Log(gameObject.name + "  这个物体已被标记,ID是  " + ID);    }    // Update is called once per frame    void Update () {    }    public void OnDestroy()    {        //Debug.Log(gameObject.name + "  标记已经移除");    }}

恩恩下面是重点:

using UnityEngine;public class GameObjDrag{    public Position startTransform;    public Position currTransform;    public Position targetTransform;    private bool _isReturn;    /// <summary>    /// 是否返回原位,true是返回,false是不返回    /// </summary>    public bool IsReturn    {        get { return _isReturn; }        set { _isReturn = value; }    }    private bool isDraging;    /// <summary>    /// 是否正在拖拽    /// </summary>    public bool IsDraging    {        get { return isDraging; }        set { isDraging = value; }    }    /// <summary>    /// 拖拽的接口    /// </summary>    public IDrag _dragInterface;    /// <summary>    /// 事件接口    /// </summary>    public IDispatch _dispatchInterface;    /// <summary>    /// 被拖拽的物体    /// </summary>    public GameObject gameobject;    public GameObjDrag(GameObject obj)    {        gameobject = obj;        startTransform = new Position(obj.transform.position,obj.transform.eulerAngles,obj.transform.localScale);        currTransform = new Position();        targetTransform = new Position();        _isReturn = true;        _dragInterface = new DraggerImpler(this);        _dispatchInterface = new TriggerImpler(this);        _dispatchInterface.AddEvent();    }    public void SetDraggerImp(IDrag dragger)    {        _dragInterface = dragger;    }    public void SetTriggerImp(IDispatch trigger)    {        _dispatchInterface = trigger;    }    public void OnStartDrag()    {        _dragInterface.OnStartDrag();    }    public void OnDrag()    {        _dragInterface.OnDrag();    }    public void OnDrop()    {        _dragInterface.OnDrop();    }}/// <summary>/// 位置/// </summary>public class Position{    /// <summary>    /// 位置    /// </summary>    public Vector3 position;    /// <summary>    /// 旋转    /// </summary>    public Vector3 rotation;    /// <summary>    /// 大小    /// </summary>    public Vector3 scale;    public Position(Vector3 pos, Vector3 rot, Vector3 sca)    {        position = pos;        rotation = rot;        scale = sca;    }    public Position()    {        position = Vector3.zero;        rotation = Vector3.zero;        scale = Vector3.zero;    }}
using System.Collections;using System.Collections.Generic;using UnityEngine;public class EPGameObj : MonoBehaviour{    GameObjDrag ep;    void Start()    {        ep = new GameObjDrag(this.gameObject);       // ep.SetDragImp(new DraggerImpler(ep));    }    IEnumerator OnMouseDown()    {        ep.OnStartDrag();        //将物体由世界坐标系转换为屏幕坐标系        Vector3 screenSpace = Camera.main.WorldToScreenPoint(transform.position);        //完成了两个步骤,1由于鼠标的坐标系是2维的,需要转化成3维的世界坐标系        Vector3 mouseScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenSpace.z);        //2只有三维的情况下才能来计算鼠标位置与物体的距离,offset即是距离          Vector3 offset = transform.position - Camera.main.ScreenToWorldPoint(mouseScreenSpace);        while (Input.GetMouseButton(0))//鼠标左键被持续按下。        {            ep.OnDrag();            //得到现在鼠标的2维坐标系位置             Vector3 curScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenSpace.z);            //将当前鼠标的2维位置转换成3维位置            Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenSpace) + offset;            //curPosition就是物体应该的移动向量赋给transform的position属性            transform.position = curPosition;            yield return new WaitForFixedUpdate();        }    }    public void OnMouseUp()    {        ep.OnDrop();        transform.position = ep.targetTransform.position;     }    void Dispose()    {        //记得remove拖拽碰撞检测事件        ep._dispatchInterface.RemoveEvent();    }}

上面这脚本就是我们要拖的萝卜上面需要挂的脚本,主要控制萝卜的移动和,松开鼠标时把重点位置修改一下。。。