您的位置:首页 > 移动开发 > Unity3D

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

2017-07-11 10:34 633 查看
从前。。。有个兔子要把得来的几个萝卜埋到坑里面,现在就是检测萝卜埋坑里的方法,讲一下需求:

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();
}
}


上面这脚本就是我们要拖的萝卜上面需要挂的脚本,主要控制萝卜的移动和,松开鼠标时把重点位置修改一下。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐