Unity拖拽物体到另外一个物体中的检测相关脚本(萝卜和坑)
2017-07-11 10:34
633 查看
从前。。。有个兔子要把得来的几个萝卜埋到坑里面,现在就是检测萝卜埋坑里的方法,讲一下需求:
1.鼠标拖动萝卜可以放进不同的坑中。
2.当萝卜没有拖到坑中时就返回到原来位置
3.当萝卜拖到坑里时萝卜就放进坑里。
我是用TriggerEvent来检测的,顺便做了一些逻辑,语言描述不够好,还是上代码吧:
上面是接口,用来描述我拖动中需要做的事情,下面是实现:
上面这个脚本实现了Idrag 接口,因为没想到开始拖和正在拖需要做什么就先不管他,当放下的时候我做了个判断就是这个:
大概意思是如果需要返回了就把初始记录的位置给targetTransform,如果不需要返回就把currTransform的位置给targetTransform.
下面这个脚本是用来做碰撞检测后事件处理,萝卜进坑是和萝卜出坑时需要进行的逻辑写在这里。
这里有另外一个新需求:
当这个坑被萝卜占了,其他萝卜不能再进入这个坑
解决方案:所以给我在萝卜坑加了一个标记,当萝卜离开坑的时候就把这个标记移除。
出现问题:我怎么知道我移除的是自己的标记?
解决方案:给每个标记一个唯一的ID,根据ID来确定是不是自己加的标记
出现问题:当两个坑离得特别近的时候,从这个坑移动到另外一个坑的时候,会先触发一次进入下个坑的进入
事件,然后再触发这个坑的离开事件,导致我记录的ID被修改成新的坑的ID,导致我没办法移除我离开的那个坑的标记。(这个坑就没有萝卜,而且也不能再把萝卜放进去了)。
解决方案:把标记做一个列表List,当进入坑的时候往标记列表里加一个ID,当离开坑时就从列表里查是否有要离开这个坑的ID,有的话移除这个坑的标记,并且从列表里移除这个ID*
上面这个脚本时用来做碰撞检测的,注意要给萝卜加刚体(rigidbody)和碰撞器(collider),坑只加碰撞器(collider)就好了,把碰撞器的isTrigger勾选。
做标记的脚本:
恩恩下面是重点:
上面这脚本就是我们要拖的萝卜上面需要挂的脚本,主要控制萝卜的移动和,松开鼠标时把重点位置修改一下。。。
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(); } }
上面这脚本就是我们要拖的萝卜上面需要挂的脚本,主要控制萝卜的移动和,松开鼠标时把重点位置修改一下。。。
相关文章推荐
- unity中同一个物体上挂载多个脚本的执行顺序
- Unity扩展Editor菜单:提供一个统一的接口,来让策划调节prefab里相关脚本的数值
- 【Unity】动态生成物体,设置一个物体为另外一个物体的父类
- Unity 拖拽物体的脚本
- unity如何在一个场景中使用另外一个参加烘培好的物体
- Unity学习笔记——利用脚本实现对一个物体的第三人称观察
- [Unity&NGUI&对象]怎么把按钮自身 的 对象 传递给另外一个脚本对象
- Unity中同一个物体上加载的脚本的执行顺序以及点击Button时触发事件的顺序
- 【Unity】(探讨)一个预制体对象GO存放入一个脚本A中,再使用另外一个脚本B来调用A脚本的预制体对象GO。
- Unity通过脚本实现给一个物体添加子物体
- Unity属性——AddComponentMenu 字面理解:添加 组件选项菜单 分析:可能是添加一个脚本或者组件到一个物体上 验证: 新建一个脚本:AttributeTest 提示:添
- unity之RotateAround 一个物体围绕另外一个物体旋转
- 把一个用户的相关权限赋予另外一个用户
- 3008.脚本作业―l201.6.0编写一个脚本用于检测IP地址(递进版6)
- 3005.脚本作业―l201.3.0编写一个脚本用于检测IP地址(递进版3)
- 3010.脚本作业―l201.8.0编写一个脚本用于检测IP地址(递进版8)
- 3009.脚本作业―l201.7.0编写一个脚本用于检测IP地址(递进版7)
- 轻松接触一个检测MySQL状态的脚本
- 3003.脚本作业―l201.1.0编写一个脚本用于检测IP地址
- 一个检测垃圾样式的好插件(JS脚本)