1 Unity原生

1.1 GUI

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

1.1 Input

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

  • fingerId 手指索引
    The unique index for a touch. 触摸的唯一索引。
  • position 位置
    The screen position of the touch. 触摸屏幕的位置。
  • deltaPosition 增量位置
    The screen position change since the last frame.
    自最后一帧改变的屏幕位置。
  • deltaTime 增量时间
    Amount of time that has passed since the last state change.
    从最后状态改变到现在经过的时间
  • tapCount 点击数
    The iPhone/iPad screen is able to distinguish quick finger taps by the user. This counter will let you know how many times the user has tapped the screen without moving a finger to the sides. Android devices do not count number of taps, this field is always 1.
    iPhone/iPad屏幕能够识别用过的快速点击, 这个计数器让你知道用户点击屏幕多少次,而不是移动手指。android设备不对点击计数,这个方法总是返回1
  • phase 相位(状态)
    Describes so called "phase" or the state of the touch. It can help you determine if the touch just began, if user moved the finger or if he just lifted the finger.
    描述触摸的状态,可以帮助开发者确定用户是否刚开始触摸,是否用户移动手指,是否用户刚刚抬起手指。

Phase can be one of the following:

相位(状态)可能是下列之一:

    • Began 开始
      A finger just touched the screen. 手指刚刚触摸屏幕
    • Moved 移动
      A finger moved on the screen. 手指在屏幕上移动
    • Stationary 静止
      A finger is touching the screen but hasn't moved since the last frame.
      手指触摸屏幕,但是自最后一帧没有移动
    • Ended 结束
      A finger was lifted from the screen. This is the final phase of a touch.
      手指从屏幕上抬起,这是触控的最后状态。
    • Canceled 取消
      The system cancelled tracking for the touch, as when (for example) the user puts the device to her face or more than five touches happened simultaneously. This is the final phase of a touch.
      系统取消了触控跟踪,如,用户将设备放在了脸上或超过同时超过5个触摸点。这是触控的最后状态。
  • 下面的例子,只要用户在屏幕上点击,将射出一条光线:

var particle : GameObject;
function Update () {
for (var touch : Touch in Input.touches) {
if (touch.phase == TouchPhase.Began) {
// Construct a ray from the current touch coordinates
//从当前的触摸坐标建一条光线
var ray = Camera.main.ScreenPointToRay (touch.position);
if (Physics.Raycast (ray)) {
// Create a particle if hit
//如果触摸就创建一个例子
Instantiate (particle, transform.position, transform.rotation);
}
}
}
}

2 NGUI事件机制

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

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

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

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上面。这种方法不够灵活,不推荐。

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 观察者模式和事件/委托机制

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

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

  • 抽象主题角色(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处理触屏事件。

ProcessTouches函数

public void ProcessTouches ()
{
currentScheme = ControlScheme.Touch; for (int i = ; i < Input.touchCount; ++i)
{
Touch touch = Input.GetTouch(i); currentTouchID = allowMultiTouch ? touch.fingerId : ;
//根据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 > ) 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 == )
{
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 = ; 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 > )
{
//碰到多个colider
for (int b = ; 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 = ; 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 == )
{
GameObject go = hits[].collider.gameObject;
UIWidget w = go.GetComponent<UIWidget>(); if (w != null)
{
if (!w.isVisible) continue;
if (w.hitCheck != null && !w.hitCheck(hits[].point)) continue;
}
else
{
UIRect rect = NGUITools.FindInParents<UIRect>(go);
if (rect != null && rect.finalAlpha < 0.001f) continue;
} if (IsVisible(hits[].point, hits[].collider.gameObject))
{
lastHit = hits[];
lastWorldPosition = hits[].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 > )
{
for (int b = ; 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 = ; 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 == )
{
GameObject go = hits[].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

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

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

http://wetest.qq.com

Unity事件处理机制与NGUI事件机制的更多相关文章

  1. Atitit  数据库的事件机制--触发器与定时任务attilax总结

    Atitit  数据库的事件机制--触发器与定时任务attilax总结 1.1. 事件机制的图谱1 2. 触发器的类型2 3. 实现原理 After触发器 Vs Instead Of触发器2 3.1. ...

  2. PHP event 事件机制

    PHP event 事件机制   <?php /* * PHP 事件机制 */ class baseClass{ private $_e; public function __set($name ...

  3. Tomcat与Spring中的事件机制详解

    最近在看tomcat源码,源码中出现了大量事件消息,可以说整个tomcat的启动流程都可以通过事件派发机制串起来,研究透了tomcat的各种事件消息,基本上对tomcat的启动流程也就有了一个整体的认 ...

  4. java事件处理机制(自定义事件)

    java中的事件机制的参与者有3种角色: 1.event object:事件状态对象,用于listener的相应的方法之中,作为参数,一般存在与listerner的方法之中 2.event sourc ...

  5. 【移动端兼容问题研究】javascript事件机制详解(涉及移动兼容)

    前言 这篇博客有点长,如果你是高手请您读一读,能对其中的一些误点提出来,以免我误人子弟,并且帮助我提高 如果你是javascript菜鸟,建议您好好读一读,真的理解下来会有不一样的收获 在下才疏学浅, ...

  6. tkinter事件机制

    一.tkinter.Event tkinter的事件机制跟js是一样的,也是只有一个Event类,这个类包罗万象,集成了键盘事件,鼠标事件,包含各种参数. 不像java swing那种强类型事件,sw ...

  7. 深入理解DOM事件机制系列第四篇——事件模拟

    × 目录 [1]引入 [2]模拟机制 [3]自定义事件 前面的话 事件是网页中某个特别的瞬间,经常由用户操作或通过其他浏览器功能来触发.但实际上,也可以使用javascript在任意时刻来触发特定的事 ...

  8. 总结JavaScript事件机制

    JavaScript事件模型 在各种浏览器中存在三种事件模型: 原始事件模型 , DOM2事件模型 , IE事件模型. 其中原始的事件模型被所有浏览器所支持,而DOM2中所定义的事件模型目前被除了IE ...

  9. javascript 中的事件机制

    1.javascript中的事件. 事件流 javascript中的事件是以一种流的形式存在的. 一个事件会也有多个元素同时响应. 有时候这不是我们想要的效果, 我们只是需要某个特定的元素相应我们的绑 ...

随机推荐

  1. NetBpm 组织架构(4)

    大牛的杰作,赞一个 转自:NetBPM工作流的架构设计及实现浅析 读前的话:由于本文涉及内容颇多,若有地方读来不很明白,建议先跳过,整体上有个认识后,再回过头来理解.作者认识有限,若有错误,欢迎斧正: ...

  2. Nginx 访问日志

    配置访问日志: [root@localhost ~]$ cat /usr/local/nginx/conf/nginx.conf http { log_format main '$remote_add ...

  3. [C/E] 等差数列求和

    题目:要求给定一个整数 N,求从 0 到 N 之间所有整数相加之和. 解1:使用 for 循环依次递加. #include <stdio.h> int main(void){ int x; ...

  4. 项目适配iOS9遇到的一些问题及解决办法(更新两个小问题)

    本文转载至 http://www.bubuko.com/infodetail-1110714.html http://www.jianshu.com/p/631bd7f12a38 1.网络请求报错.升 ...

  5. iOS 静态库和动态库(库详解)

    什么是库 ? 库就是程序代码的集合,将N个文件组织起来,是共享程序代码的一种方式.库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行. 库的分类 开源库:源代码是公开的,可以看到每个实现 ...

  6. Git学习之Git恢复进度

    ================================================ 继续暂存区未完成的实践 ======================================= ...

  7. enum hack

    关于占用内存的大小,enum类型本身是不占内存的,编译器直接替换.但是enum类型的变量肯定是占内存的. class A{ public: //enum类型本身不占内存 enumEnumTest{ a ...

  8. package.json字段全解(转)

    Name 必须字段. 小提示: 不要在name中包含js, node字样: 这个名字最终会是URL的一部分,命令行的参数,目录名,所以不能以点号或下划线开头: 这个名字可能在require()方法中被 ...

  9. CopyTo 方法详解

    如果你就想复制一个字符串到另一个字符串,可以使用string的静态方法Copy 例如: string a = "hello"; string b = "world&quo ...

  10. RAC迁移至单机考虑几大因素

    数据库迁移几大因素 1. 停机时间 2. 源端,目标端 操作系统平台,版本,对应的数据库版本 3. 数据量 4. 外界因素,存储空间,网络等