UICamera

UICamera是NGUI里很重要的一个脚本,它负责UI事件的通知,是NGUI里UI事件的生产者和投递者。它需要attach到一个绘制NGUI里UI控件的camera上,但它和UI控件的绘制没有任何关系——没有被UICamera attached的camera依然可以正常绘制UI,只是这些UI控件不会收到UI事件的通知。

UICamera支持的UI事件如下。

1

UICamera本质上是对从Unity Input中获取的输入数据进行了封装和分发。它根据输入设备的不同将输入模式分为了三类,即Mouse(鼠标)、Touch(触摸屏)和Controller(手柄等)。

2

不同模式下对UI事件的生成会有不同的处理,后面会详细讲到。

UICamera中将事件类型分为World和UI两类,它和eventReceiverMask、rangeDistance一起影响着如何通过raycasts找到输入位置的GameObject。

3

4

5

当EventType为World时UICamera采用GameObject的实际距离判定射线碰撞到的最近的对象,为UI时则采用UIWidget的raycastDepth来判断。eventReceiverMask指定了哪些层能发生和接收UI事件,rangeDistance则指定了能发生和接收UI事件的对象的距离范围。这三者一起决定了UICamera.Raycast方法的执行逻辑和结果。

6

UICamera中还有一些细节的配置项(成员变量),这里就不一一详述了,下面介绍下UICamera里的核心数据类型——MouseOrTouch。

7

MouseOrTouch是一个UI事件的抽象,用于保存输入数据中的位置、偏移量等重要信息。

pos:当前帧输入数据的位置信息。

lastPos:前一次输入数据的位置信息,不过好像没用到。

delta:当前帧与上一帧输入数据的位置偏移。

totalDelta:当前帧与事件最开始发生时输入数据的位置偏移,用于drag事件。

pressedCam:发生press事件时的camera。

last:前一次输入位置的对象。

current:当前帧输入位置的对象。

pressed:press的对象。

dragged:drag的对象。

clickTime:click事件分发出去的时间,用于判定double click事件的分发。

clickNotification:OnClick事件的发生条件,None为不发生,Always为总是发生,BasedOnDelta为根据位置移动的偏移量来决定是否发生。

touchBegan:用于Touch模式下标识一个touch是否为began阶段。

pressStarted:标识press事件是否开始。

dragStarted:表示drag事件是否开始。

有了MouseOrTouch,UICamera就通过它来保存不同输入设备对应的输入数据。

8

mMouse、mTouches、controller就分别对应了鼠标、触摸屏和手柄等的输入数据。鼠标分左键、右键和滚轮,因此由一个包含3个MouseOrTouch对象的数组组成。而触摸屏会有多点触控发生,因此是一个以fingerId为key的Dictionary。

UICamera的事件生成和分发会在每一帧Update时做检测,其主要逻辑如下:

1、处理触摸屏或鼠标事件;

2、通过OnCustomInput的delegate允许使用者自定义的输入处理;

3、处理选中状态的控件;

4、处理键盘和手柄事件;

这里选取触摸屏事件的处理做简单分析:

public void ProcessTouches ()

{

currentScheme = ControlScheme.Touch; // 设置输入模式为Touch

for (int i = 0; i < Input.touchCount; ++i) // 遍历输入数据中的每一个Touch

{

Touch touch = Input.GetTouch(i);

// 获取并设置当前的MouseOrTouch

currentTouchID = allowMultiTouch ? touch.fingerId : 1;

currentTouch = GetTouch(currentTouchID);

// 判断当前的touch状态

bool pressed = (touch.phase == TouchPhase.Began) || currentTouch.touchBegan;

bool unpressed = (touch.phase == TouchPhase.Canceled) || (touch.phase == TouchPhase.Ended);

currentTouch.touchBegan = false;

// 设置当前touch的位置信息

currentTouch.delta = pressed ? Vector2.zero : touch.position – currentTouch.pos;

currentTouch.pos = touch.position;

// 通过Raycast获取hoveredObject并更新touch相关信息

if (!Raycast(currentTouch.pos, out lastHit)) hoveredObject = fallThrough;

if (hoveredObject == null) hoveredObject = genericEventHandler;

currentTouch.last = currentTouch.current;

currentTouch.current = hoveredObject;

lastTouchPosition = currentTouch.pos;

if (pressed) currentTouch.pressedCam = currentCamera;

else if (currentTouch.pressed != null) currentCamera = currentTouch.pressedCam;

// 设置touch的clickTime,用于双击事件的判定

if (touch.tapCount > 1) currentTouch.clickTime = RealTime.time;

// 事件的分发

ProcessTouch(pressed, unpressed);

// 当前touch的事件分发完成后进行重置

if (unpressed) RemoveTouch(currentTouchID);

currentTouch.last = null;

currentTouch = null;

// 不支持多点触控则只处理第一个touch

if (!allowMultiTouch) break;

}

// 没有触控的输入数据时则考虑鼠标等输入设备的输入

if (Input.touchCount == 0)

{

if (useMouse) ProcessMouse();

#if UNITY_EDITOR

else ProcessFakeTouches();

#endif

}

}

从ProcessTouches的处理可以看出真正的事件分发在ProcessTouch里,不仅是这里的触控事件分发,其它如鼠标的事件分发等最终都会调用到它。这里就不再列出全部源码,仅截取一个小片段来分析。

9

ProcessTouch里当前事件处于pressed状态时,先通过第一个Nofity通知原来处于pressed状态的对象响应OnPress(false)消息,再通过第二个Notify通知当前处于pressed状态的对象响应OnPress(true)消息。而Notify又是如何做到通知对象响应消息的呢?答案是Unity的SendMessage。

10

这样UICamera就完成了从Unity Input中获取输入数据,再封装成事件,最终又通过Unity SendMessage分发消息的整个闭环流程。

Beginning iOS 7 Development Exploring the iOS SDK 1-4

iOS编程的一些区别:
1、任何时候只有一个应用处于active状态并显示在屏幕上;
2、每个应用都只有一个window;
3、每个应用都只能访问自己的sandbox目录;
4、每个应用都要在限定的时间内做出响应(用户按下home键,应用保存数据等操作的时长超过5s就会被系统kill掉);
5、有限大小的屏幕;
6、有限的系统资源。

Xcode navigator从左到右依次为:
Project navigator
Symbol navigator
Find navigator
Issue navigator
Test navigator
Debug navigator
Breakpoint navigator
Log navigator
快捷键依次为command + 1/2/3/4/5/6/7/8

Xcode快捷键:
shift+command+O: Open Quickly

Interface Builder支持.nib、.xib和.storyboard文件。
.nib:最早的二进制格式
.xib:XML格式,.nib和.xib通称nib file
.storyboard:meta-nib file
nib file和storyboard还有一个区别:nib file通常一个文件只包含一个single view及其里面的控件,当这个view需要显示时只需加载这个nib节约内存;而storyboard可以包含所有界面但只加载其中特定的部分。

按住Option键移动鼠标到类名上可以查看类的描述
选中Image View使用快捷键Command+=或者使用菜单Editor->Size to Fit Content可以将Image View调整为设置的图片大小
为UI添加约束:Editor->Resolve Auto Layout Issues->Add Missing Constraints

Mesh MeshFilter MeshRenderer

Mesh是计算机图形学中一个很重要的概念,也是Unity中用于渲染的数据模型。
Unity中Mesh继承自Object而非Component,因为它只是一个为MeshFilter、MeshRenderer渲染提供数据的模型。它的主要数据成员如下:
Vector3[] vertices:Mesh的顶点数组。如果resize该数组时其它的顶点属性数组(normals, colors, tangents, UVs)都会自动resize以保持一致。
int[] triangles:Mesh的三角形数组(包括Mesh的sub meshes的三角形)。其中的三角形使用vertices中的索引来表示,数组大小必须是3的倍数(3个顶点索引组成一个三角形)。
Vector2[] uv:顶点贴图的UV数组,大小保持和顶点数组一致。
Color[] colors:顶点的颜色数组,大小保持和顶点数组一致。
Vector3[] normals:顶点的法线数组,大小保持和顶点数组一致。
Vector4[] tangents:顶点的切线数组,大小保持和顶点数组一致。

Unity中渲染Mesh除了需要Mesh本身的数据模型外,还需要MeshFilter和MeshRenderer配合才能完成。
MeshFilter会引用一个Mesh,并将它传递给MeshRenderer完成渲染。它的主要数据成员:
sharedMesh:GameObject关联的Mesh对象。Unity建议通过它只能读取Mesh的数据而不要进行改写,因为改写会同时影响其它使用了该Mesh的GameObject,改写单个GameObject的Mesh可以使用mesh。
mesh:如果MeshFilter没有设置Mesh的话它会生成一个新的Mesh对象;有设置、如果是第一次访问mesh属性的话它会复制生成一个新的Mesh对象,这之后再访问mesh属性都将返回复制出来的Mesh对象。如果访问了mesh属性后原始的sharedMesh丢失了,那么shareMesh返回的将也是mesh。使用mesh属性可以改变单个GameObject的Mesh,但当GameObject被销毁的时候我们需要负责销毁复制生成的mesh。

MeshRenderer继承自Renderer,它从MeshFilter中获取几何信息并在GameObject Transform指定的位置进行渲染。渲染涉及多个方面,这里简要介绍MeshRenderer与材质有关的成员变量:
sharedMaterial:和sharedMesh类似,GameObject关联的Material对象,改写的话会影响到所有使用了该Material的GameObject。当GameObject拥有多个Material时它返回的是sharedMaterials数组里的第一个元素。
sharedMaterials:Unity支持一个GameObject使用多个Material,这种情况下sharedMaterials数组包含了这些Material。
material:和mesh类似,自动生成,GameObject被销毁的时候需要我们负责销毁它。
materials:同material,用于支持多Material。

切换Prefab的保存模式

Unity中prefab的默认保存模式是Mixed的,用文本编辑器打开会出现乱码和部分文本,可以通过Edit—>Project Settings—>Editor将Asset Serialization Mode改为Force Text再保存就能显示全文本了。

git

git add不仅用于新增文件,改动的文件也需要使用它加入commit队列

git commit -m “这里是提交注释”

git push

Resources.Load和Asset

Resources.Load 加载Resources文件夹里指定路径的asset
AssetBundle 支持通过WWW类来使用流式的assets
Resources.Load从一个默认打进游戏包里的AssetBundle加载资源,而AssetBundle的方式是自己创建、管理AssetBundle文件并从中加载资源,两者本质上没有区别。

AssetBundle的加载:
CreateFromFile从硬盘加载一个AssetBundle文件
CreateFromMemory异步从内存数据创建一个AssetBundle
CreateFromMemoryImmediate同步从内存数据创建一个AssetBundle
AssetBundle加载完毕后只是在内存里创建了AssetBundle的数据结构,此时还没有assets的概念。

Assets的加载:
AssetBundle.Load等加载asset的方法才会从AssetBundle中创建一个asset,这里面可能包括了GameObject、Transform、Texture、Mesh、Material、Shader等各种资源。
当使用asset(prefab) Instantiate出一个对象时,是对这个asset进行复制(clone)加引用的过程:
GameObject、Transform是复制出来的;
Texture、TerrainData是引用原asset的;
Mesh、Material是复制和引用同时存在的。
因此这里需要注意:当Instantiate出的对象未Destory前不能卸载它引用的asset,而当对象Destory后需要通过Resources.UnloadUnusedAssets来释放不再引用的asset。

AssetBundle的卸载:
public void Unload(bool unloadAllLoadedObjects);
unloadAllLoadedObjects为true时将卸载AsserBundle的同时会卸载从AssetBundle中加载出来的assets,如果此时还有对象引用着这些assets时会不安全。
unloadAllLoadedObjects为false时只会卸载AssetBundle,要卸载从AssetBundle中加载出来的assets时需要在合适的时机调用Resources.UnloadUnusedAssets。

http://game.ceeger.com/forum/read.php?tid=4394

Objective-C

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html

Protocol的定义形式:
@protocol ProtocolName
// list of methods and properties
@end

通过属性引用一个实现了Protocol的对象:
@property (weak) id <XYZPieChartViewDataSource> dataSource;

Protocol中可以定义必须实现的方法和可选实现的方法,默认是必须实现的。使用关键字@optional和@required可以声明它们以下的方法是可选或必须实现的:
@protocol XYZPieChartViewDataSource
– (NSUInteger)numberOfSegments;
– (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
– (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
– (BOOL)shouldExplodeSegmentAtIndex:(NSUInteger)segmentIndex;
@required
– (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex;
@end

调用Protocol中的可选方法时需要通过respondsToSelector:和@selector()先检查对象是否实现了该方法:
NSString *thisSegmentTitle;
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
}

Protocol可以继承:
@protocol MyProtocol <NSObject>

@end

类实现多个协议:
@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol>

@end
注意:编译器不会合成协议中声明的属性。

Block也是OC对象,其定义形式为:
^{
NSLog(@”This is a block”);
}
^ double (double firstValue, double secondValue) {
return firstValue * secondValue;
}

同C语言里函数指针可以指向一个函数一样,OC里可以使用Block Pointer:
void (^simpleBlock)(void);

Block能抓住闭包范围内的值,但它不能改变值。如果Block要改变一个被Block抓住的变量的值,需要对该变量使用__block修饰符。
注意:Block若抓住了self时,可能引起强引用循环引用的问题。
解决循环引用的问题可以在Block内部使用__weak的self:
– (void)configureBlock {
XYZBlockKeeper * __weak weakSelf = self;
self.block = ^{
[weakSelf doSomething]; // capture the weak reference
// to avoid the reference cycle
}
}

Class中使用属性引用Block时要使用copy属性修饰符:
@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end

Operation Queues是Cocoa进行任务调度的一个途径。可以创建一个NSOperation实例将一个任务及其需要的数据封装起来,然后将它加入一个NSOperationQueue来执行。
使用NSBlockOperation创建一个使用Block的Operation:
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

}];

// schedule task on main queue:
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperation:operation];

// schedule task on background queue:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];

Block也可以使用Grand Central Dispatch (GCD)控制的Dispatch Queues来执行。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{
NSLog(@”Block for asynchronous execution”);
});