从前。。。有个兔子要把得来的几个萝卜埋到坑里面,现在就是检测萝卜埋坑里的方法,讲一下需求:
1.鼠标拖动萝卜可以放进不同的坑中。
2.当萝卜没有拖到坑中时就返回到原来位置
3.当萝卜拖到坑里时萝卜就放进坑里。
我是用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();
}
}
上面这脚本就是我们要拖的萝卜上面需要挂的脚本,主要控制萝卜的移动和,松开鼠标时把重点位置修改一下。。。