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

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

using UnityEngine;
/// 
/// 拖拽接口
/// 
public interface IDrag
{
    /// 
    /// 开始拖动
    /// 
    void OnStartDrag();
    /// 
    /// 拖动中
    /// 
    void OnDrag();
    /// 
    /// 放下
    /// 
    void OnDrop();
}
/// 
/// 事件监听处理
/// 
public interface IDispatch
{
    /// 
    /// 添加事件监听
    /// 
    void AddEvent();
    /// 
    /// 移除对事件的监听
    /// 
    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
{
    /// 
    /// 监听到后对Obj进行操作
    /// 
    GameObjDrag _obj;
    /// 
    /// 触发器的事件监听
    /// 
    TriggerEvent _te;
    /// 
    /// 标记ID列表
    /// 
    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();
        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();
        if (ma == null)
        {
            ma = obj.AddComponent();
            //_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 TriggerEnter;
    public Action TriggerExit;
    /// 
    /// 往一个物体上添加这个事件监听类
    /// 
    /// 
    /// 
    public static TriggerEvent AddComponentToGameObject(GameObject obj)
    {
        TriggerEvent com = obj.GetComponent();
        if (com == null)
        {
            com = obj.AddComponent();
        }
        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;
/// 
/// 用来做标记的一个脚本
/// 
public class Mark : MonoBehaviour {
    /// 
    /// 不允许用户手动修改,测试时看ID
    /// 
    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;
    /// 
    /// 是否返回原位,true是返回,false是不返回
    /// 
    public bool IsReturn
    {
        get { return _isReturn; }
        set { _isReturn = value; }
    }
    private bool isDraging;
    /// 
    /// 是否正在拖拽
    /// 
    public bool IsDraging
    {
        get { return isDraging; }
        set { isDraging = value; }
    }
    /// 
    /// 拖拽的接口
    /// 
    public IDrag _dragInterface;
    /// 
    /// 事件接口
    /// 
    public IDispatch _dispatchInterface;
    /// 
    /// 被拖拽的物体
    /// 
    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();
    }
}
/// 
/// 位置
/// 
public class Position
{
    /// 
    /// 位置
    /// 
    public Vector3 position;
    /// 
    /// 旋转
    /// 
    public Vector3 rotation;
    /// 
    /// 大小
    /// 
    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();
    }
}

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

你可能感兴趣的:(Unity3D)