你的位置:首页 > 软件开发 > ASP.net > Unity事件处理机制与NGUI事件机制

Unity事件处理机制与NGUI事件机制

发布时间:2015-08-09 16:00:06
1 Unity原生1.1 GUIvoid OnGUI(){if(GUI.Button(Rect position, string text)){ //点击后立即执行}1.1 Input每个手指触控是通过Input.touches数据结构描述的:fingerId ...

1 Unity原生

1.1 GUI

void OnGUI(){if(GUI.Button(Rect position, get='_blank'>string text)){  //点击后立即执行}

1.1 Input

每个手指触控是通过Input.touches数据结构描述的:

  • fingerId 手指索引

    2 NGUI事件机制

           UICamera主要负责监听,分发所有此Camera渲染的GameObject。

           事件源:鼠标,触屏,键盘和手柄

    事件包括:悬停,按下/抬起,选中/取消选中,点击,双击,拖拽,释放,文本输入,tips显示,滚轮滑动,键盘输入。

     Unity事件处理机制与NGUI事件机制

    EventType:包括3D UI,3D world,2D UI,3D World用于区分UICamera处理UI事件的对象是UI空间还是3D物体。

    EventMask:可以过滤到一些不需要接受UI事件的对象。

    EventSource:只处理指定事件源,如touch

    Thresholds:是指事件误差的范围。

    2.1 NGUI事件注册

    2.1.1 直接监听事件

    直接将MonoBehaviour的脚本上的方法绑定至Notifiy上面。这种方法不够灵活,不推荐。

     Unity事件处理机制与NGUI事件机制

    Unity事件处理机制与NGUI事件机制

    2.1.2 使用SendMessage

    过期方式,不建议使用。

    2.1.3 UIListener注册

         在需要接收事件的gameobject上附加Event Listener。添加component->NGUI->Internal->Event Listener。

    在任何一个脚本或者类中即可得到按钮的点击事件、把如下代码放在任意类中或者脚本中。

    void Awake ()   {          //获取需要监听的按钮对象  GameObject button = GameObject.Find("UI Root (2D)/Camera/Anchor/Panel/LoadUI/MainCommon/Button");        //设置这个按钮的监听,指向本类的ButtonClick方法中。  UIEventListener.Get(button).onClick = ButtonClick;  }     //计算按钮的点击事件  void ButtonClick(GameObject button)  {    Debug.Log("GameObject " + button.name);  }

    3 NGUI事件相关源码分析

    3.1 事件注册

    UIEventListener.Get(gameObject).onClick = ButtonClick;//1、获取GameObject对应的UIEventListener的组件static public UIEventListener Get (GameObject go)  {    UIEventListener listener = go.GetComponent<UIEventListener>();    if (listener == null) listener = go.AddComponent<UIEventListener>();    return listener;  }//2、注册事件委托public class UIEventListener : MonoBehaviour{  public delegate void VoidDelegate (GameObject go);    public VoidDelegate onClick;    void OnClick (){ if (onClick != null) onClick(gameObject); }}

    3.1.1 观察者模式和事件/委托机制

    观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有的观察者对象,使他们能够执行自己的相关行为。

     Unity事件处理机制与NGUI事件机制

    可以看出,在观察者模式的结构图有以下角色:

    • 抽象主题角色(Subject):抽象主题把所有观察者对象的引用保存在一个列表中,并提供增加和删除观察者对象的操作,抽象主题角色又叫做抽象被观察者角色,一般由抽象类或接口实现。
    • 抽象观察者角色(Observer):为所有具体观察者定义一个接口,在得到主题通知时更新自己,一般由抽象类或接口实现。
    • 具体主题角色(ConcreteSubject):实现抽象主题接口,具体主题角色又叫做具体被观察者角色。
    • 具体观察者角色(ConcreteObserver):实现抽象观察者角色所要求的接口,以便使自身状态与主题的状态相协调。

    3.1.1.1            委托

    1、委托定义

    在C#中定义一个委托非常简单,只要包含关键词delegate,其他的与普通方法一致。

    public delegate void GreetingDelegate(string name);

    委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。(与Java中的接口非常相似)。

     2、方法绑定委托

    上面的绑定onclick即是一个方法绑定。

    UIEventListener.Get(gameObject).onClick = ButtonClick;

    委托方法可以绑定多个方法,调用时会一次调用绑定的方法。

    UIEventListener.Get(gameObject).onClick += ButtonClick2;

      委托还可以通过-=的方式解除绑定。

    注意:第一次绑定必须用赋值=,如果使用+=将会发生编译错误。

    3.2 UICamera事件分发

    3.2.1 初始化设置

    设置fallThrough为摄像机节点的UI Root节点或者摄像机节点或者当前本身gameobject。

    3.2.2 update获取事件源

    Unity提供了Input接口,能够从触屏中获取各类输入事件。Update的时候查看事件输入情况,并通过ProcessTouches处理触屏事件。

     Unity事件处理机制与NGUI事件机制

    ProcessTouches函数

    public void ProcessTouches ()  {    currentScheme = ControlScheme.Touch;    for (int i = 0; i < Input.touchCount; ++i)    {      Touch touch = Input.GetTouch(i);            currentTouchID = allowMultiTouch ? touch.fingerId : 1;      //根据TouchID获取touch事件,如果是begin的则创建新对象加入缓存      currentTouch = GetTouch(currentTouchID);      //设置当前输入的相位      bool pressed = (touch.phase == TouchPhase.Began) || currentTouch.touchBegan;      bool unpressed = (touch.phase == TouchPhase.Canceled) || (touch.phase == TouchPhase.Ended);      currentTouch.touchBegan = false;      // Although input.deltaPosition can be used, calculating it manually is safer (just in case)      //如果不是开始按下,就需要计算与上一次的偏移情况      currentTouch.delta = pressed ? Vector2.zero : touch.position - currentTouch.pos;      currentTouch.pos = touch.position;      // Raycast into the screen      //通过射线获取可以点击的gameobject      if (!Raycast(currentTouch.pos)) hoveredObject = fallThrough;      if (hoveredObject == null) hoveredObject = mGenericHandler;      currentTouch.last = currentTouch.current;      currentTouch.current = hoveredObject;      lastTouchPosition = currentTouch.pos;      // We don't want to update the last camera while there is a touch happening      if (pressed) currentTouch.pressedCam = currentCamera;      else if (currentTouch.pressed != null) currentCamera = currentTouch.pressedCam;      // Double-tap support      //ios的多次点击,则需要设置时间戳      if (touch.tapCount > 1) currentTouch.clickTime = RealTime.time;      // Process the events from this touch      ProcessTouch(pressed, unpressed);      // If the touch has ended, remove it from the list      if (unpressed) RemoveTouch(currentTouchID);      currentTouch.last = null;      currentTouch = null;      // Don't consider other touches      if (!allowMultiTouch) break;    }    if (Input.touchCount == 0)    {      if (useMouse) ProcessMouse();#if UNITY_EDITOR      else ProcessFakeTouches();#endif    }  }

    这里需要重点介绍Raycast(Vector3 inpos)和ProcessTouch()。

    3.2.2.1            Raycast

    list中缓存这当前场景中,所有的摄像机,并按照深度来排序。

    主要处理World_3D、UI_3D、World_2D和UI_2D4种不同类型的。

    static public bool Raycast (Vector3 inPos)  {    for (int i = 0; i < list.size; ++i)    {      UICamera cam = list.buffer[i];            // Skip inactive scripts      //没有激活的,全部跳过      if (!cam.enabled || !NGUITools.GetActive(cam.gameObject)) continue;      // Convert to view space      currentCamera = cam.cachedCamera;      Vector3 pos = currentCamera.ScreenToViewportPoint(inPos);//找到屏幕坐标      if (float.IsNaN(pos.x) || float.IsNaN(pos.y)) continue;      // If it's outside the camera's viewport, do nothing      if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f) continue;      // Cast a ray into the screen      Ray ray = currentCamera.ScreenPointToRay(inPos);      // Raycast into the screen      //UI层和事件层都OK的      int mask = currentCamera.cullingMask & (int)cam.eventReceiverMask;      float dist = (cam.rangeDistance > 0f) ? cam.rangeDistance : currentCamera.farClipPlane - currentCamera.nearClipPlane;      if (cam.eventType == EventType.World_3D)      {        if (Physics.Raycast(ray, out lastHit, dist, mask))        {          lastWorldPosition = lastHit.point;          hoveredObject = lastHit.collider.gameObject;          //如果父节点上有刚体,则返回          Rigidbody rb = FindRootRigidbody(hoveredObject.transform);          if (rb != null) hoveredObject = rb.gameObject;          return true;        }        continue;      }      else if (cam.eventType == EventType.UI_3D)      {                RaycastHit[] hits = Physics.RaycastAll(ray, dist, mask);        if (hits.Length > 1)        {          //碰到多个colider          for (int b = 0; b < hits.Length; ++b)          {            GameObject go = hits[b].collider.gameObject;            UIWidget w = go.GetComponent<UIWidget>();            //根据UIWidget或者UIRect来判断,落点是不是在节点里面            if (w != null)            {              if (!w.isVisible) continue;              if (w.hitCheck != null && !w.hitCheck(hits[b].point)) continue;            }            else            {              UIRect rect = NGUITools.FindInParents<UIRect>(go);              if (rect != null && rect.finalAlpha < 0.001f) continue;            }            //计算射线的深度            mHit.depth = NGUITools.CalculateRaycastDepth(go);            if (mHit.depth != int.MaxValue)            {              mHit.hit = hits[b];              mHit.point = hits[b].point;              mHit.go = hits[b].collider.gameObject;              mHits.Add(mHit);            }          }          //根据深度排序          mHits.Sort(delegate(DepthEntry r1, DepthEntry r2) { return r2.depth.CompareTo(r1.depth); });          for (int b = 0; b < mHits.size; ++b)          {#if UNITY_FLASH            if (IsVisible(mHits.buffer[b]))#else            //最上层且可见的,为返回的节点            if (IsVisible(ref mHits.buffer[b]))#endif            {              lastHit = mHits[b].hit;              hoveredObject = mHits[b].go;              lastWorldPosition = mHits[b].point;              mHits.Clear();              return true;            }          }          mHits.Clear();        }        else if (hits.Length == 1)        {          GameObject go = hits[0].collider.gameObject;          UIWidget w = go.GetComponent<UIWidget>();          if (w != null)          {            if (!w.isVisible) continue;            if (w.hitCheck != null && !w.hitCheck(hits[0].point)) continue;          }          else          {            UIRect rect = NGUITools.FindInParents<UIRect>(go);            if (rect != null && rect.finalAlpha < 0.001f) continue;          }          if (IsVisible(hits[0].point, hits[0].collider.gameObject))          {            lastHit = hits[0];            lastWorldPosition = hits[0].point;            hoveredObject = lastHit.collider.gameObject;            return true;          }        }        continue;      }      else if (cam.eventType == EventType.World_2D)      {        //用Plane射线获取        if (m2DPlane.Raycast(ray, out dist))        {          Vector3 point = ray.GetPoint(dist);          Collider2D c2d = Physics2D.OverlapPoint(point, mask);          if (c2d)          {            lastWorldPosition = point;            hoveredObject = c2d.gameObject;            Rigidbody2D rb = FindRootRigidbody2D(hoveredObject.transform);            if (rb != null) hoveredObject = rb.gameObject;            return true;          }        }        continue;      }      else if (cam.eventType == EventType.UI_2D)      {        if (m2DPlane.Raycast(ray, out dist))        {          lastWorldPosition = ray.GetPoint(dist);          Collider2D[] hits = Physics2D.OverlapPointAll(lastWorldPosition, mask);          if (hits.Length > 1)          {            for (int b = 0; b < hits.Length; ++b)            {              GameObject go = hits[b].gameObject;              UIWidget w = go.GetComponent<UIWidget>();              if (w != null)              {                if (!w.isVisible) continue;                if (w.hitCheck != null && !w.hitCheck(lastWorldPosition)) continue;              }              else              {                UIRect rect = NGUITools.FindInParents<UIRect>(go);                if (rect != null && rect.finalAlpha < 0.001f) continue;              }              mHit.depth = NGUITools.CalculateRaycastDepth(go);              if (mHit.depth != int.MaxValue)              {                mHit.go = go;                mHit.point = lastWorldPosition;                mHits.Add(mHit);              }            }            mHits.Sort(delegate(DepthEntry r1, DepthEntry r2) { return r2.depth.CompareTo(r1.depth); });            for (int b = 0; b < mHits.size; ++b)            {#if UNITY_FLASH              if (IsVisible(mHits.buffer[b]))#else              if (IsVisible(ref mHits.buffer[b]))#endif              {                hoveredObject = mHits[b].go;                mHits.Clear();                return true;              }            }            mHits.Clear();          }          else if (hits.Length == 1)          {            GameObject go = hits[0].gameObject;            UIWidget w = go.GetComponent<UIWidget>();            if (w != null)            {              if (!w.isVisible) continue;              if (w.hitCheck != null && !w.hitCheck(lastWorldPosition)) continue;            }            else            {              UIRect rect = NGUITools.FindInParents<UIRect>(go);              if (rect != null && rect.finalAlpha < 0.001f) continue;            }            if (IsVisible(lastWorldPosition, go))            {              hoveredObject = go;              return true;            }          }        }        continue;      }    }    return false;  }

    3.2.2.2            ProcessTouch

    ProcessTouch根据阀值,判断需要通知的事件类型。直接调用RasyCast返回的GameObject上UIEventListen注册的事件。

    通过直接执行事件委托(onClick),或者SendMessage给返回的GameObject上的相关事件委托。

    4 Unity知识

    4.1 Camera

    游戏界面中所显示的内容是摄像机照射到得场景部分。摄像机可以设置自身的位置、照射方向、照射的面积(类似于显示中照射广度)和照射的图层(只看到自己想看到的层次)。

     Unity事件处理机制与NGUI事件机制

    Clear Flags:背景内容,默认为Skybox

    Background:Clear Flag没设置,将显示纯色

    Culling Mask:用于选择是否显示某些层,默认为“EveryThing”

    Projection:摄像机类型,透视和正交。正交适合2D,没有距离感。

    Clipping Planes:开始摄像的最近点和最远点

    Viewport Rect:设置显示区域。多摄像机可以设置不同的区域,来进行分屏显示。

    Depth:深度大的会,画在小的上面。

    Rendering Path:渲染方式。

    4.2 Colider

    Colider是所有碰撞器的基类,包括BoxColider,SphereColider,CapsuleColider,MeshColider,PhysicMaterial,Rigidbody。

    4.2.1 RigidBody刚体

    刚体可以附加物理属性如物体质量、摩擦力和碰撞系数。

    Mass:质量

    Drag:阻力

    Angular Drag:旋转阻力,越大旋转减慢越快

    Use Gravity:是否用重力

    Is Kinematic:是否受物理的影响

    Collision Detection:碰撞检测

    Constraints:冻结,某个方向上的物理效果

    4.2.1.1            碰撞

    通过sendMessage调用改方法

    OnCollisionEnter(Collision collision):开始时,立刻调用

    OnCollisionStay(Collision collision):碰撞中,每帧调用

    OnCollisionExit(Collision collision):结束时调用

    4.2.2 碰撞器

    BoxColider(盒子碰撞器),SphereColider(球体碰撞器),CapsuleColider(胶囊碰撞器),MeshColider(自定义模型自身网格决定),WheelMaterial(车轮碰撞器)。

    碰撞器之间可以添加物理材质,用于设定物理碰撞后的效果。如反弹。

    4.2.3 射线

    射线是3D世界中一个点向一个方向发射的一条无终点的线。在发射的轨迹中,一旦与其他模型发生碰撞,它将停止发射(也可以是All)。

    创建一个射线首先要知道射线的起点和终点在3D世界中的坐标。Ray即为起点和终点的封装。ScreenPointToRay,是由摄像机当前位置向鼠标位置发射的射线。

    5 参考文献

    http://game.ceeger.com/Manual/Input.html

    http://blog.csdn.net/onerain88/article/details/18963539


    原标题:Unity事件处理机制与NGUI事件机制

    关键词:

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

可能感兴趣文章

我的浏览记录