基准电压是指传感器置于0℃的温场(冰水混合物),在通以工作电流(100μA)的条件下,传感器上的电压值。实际上就是0点电压。其表示符号为V(0),该值出厂时标定,由于传感器的温度系数S相同,则只要知道基准电压值V(0),即可求知任何温度点上的传感器电压值,而不必对传感器进行分度。其计算公式为:
V(T)=V(0)+S×T (其特性曲线如下图)
特性曲线
示例:如基准电压V(0)=700mV;温度系数S=-2mV/℃,则在50℃时,传感器的输出电压V(50)=700—2×50=600(mV)。这一点正是线性温度传感器优于其它温度传感器的可贵之处。
电压基准与系统有关。在要求绝对测量的应用场合,其准确度受使用基准值的准确度的限制。但是在许多系统中稳定性和重复性比绝对精度更重要;而在有些数据采集系统中电压基准的长期准确度几乎完全不重要,但是如果从有噪声的系统电源中派生基准就会引起误差。单片隐埋齐纳基准(如AD588和AD688)在10 V时具有1 mV初始准确度(0.01 %或100 ppm), 温度系数为1.5 ppm/°C。这种基准用于未调整的12位系统中有足够的准确度(1 LSB=244 ppm),但还不能用于14或16位系统。如果初始误差调整到零,在限定的温度范围内可用于14位和16位系统(AD588或AD688限定40℃温度变化范围,1 LSB=61 ppm)。
对于要求更高的绝对精度,基准的温度需要用一个恒温箱来稳定,并对照标准校准。在许多系统中,12位绝对精度是不需要这样做的,只有高于12位分辨率才可能需要。对于准确度较低(价格也会降低)的应用,可以使用带隙基准。
退出线程和计数器
想要做的事儿:停止线程或计数器的运行,或者防止再次触发。
:对于计时器,使用 NSTimer 的实例方法 invalidate。而对于线程,使用 cancel 方法。在
线程中避免使用 exit 方法,因为当调用了 exit 之后,线程就没有机会做清理工作,当你的应
用程序结束时,会发生资源泄漏。
NSThread *thread = /* Get the reference to your thread here */; [thread cancel]; NSTimer *timer = /* Get the reference to your timer here */; [timer invalidate];
讨论讨论:退出一个计时器非常简单;只需要简单的调用计时器的 invalidate 实例方法即可。调用
了 invalidate方法之后,计时器不会再对它的目标对象触发任何时间。
然而,线程的退出有点复杂。当线程处于休眠状态时,调用了它的 cancel 方法,线程的
循环在退出之前仍然会执行完它的全部任务。
下面我给你演示一下:
- (void) threadEntryPoint{ @autoreleasepool { NSLog(@"Thread Entry Point"); while ([[NSThread currentThread] isCancelled] == NO){ [NSThread sleepForTimeInterval:4]; NSLog(@"Thread Loop"); } NSLog(@"Thread Finished"); } }
- (void) stopThread{ NSLog(@"Cancelling the Thread"); [self.myThread cancel]; NSLog(@"Releasing the thread"); self.myThread = nil; }
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.myThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEntryPoint) object:nil]; [self performSelector:@selector(stopThread) withObject:nil afterDelay:3.0f]; [self.myThread start]; self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; }
这段代码创建了一个 NSThread 实例,并立即启动。我们的线程在执行它的任务之前,
每次循环都会休眠 4 秒钟。然而,在线程开始之前,我们调用了 stopThread 方法,该方法会
在 3 秒钟后执行,stopThread 方法调用了当前线程的 cancel 方法,试图让线程退出它的循
环。现在让我们允许程序,可以看到控制台窗口打印出如下信息:
...
Thread Entry Point
Cancelling the Thread
Releasing the thread
Thread Loop
Thread Finished
你可以清晰的看到,即使在循环中间 cancel 请求已经触发了,但是我们的线程在退出之
前,仍会完成当前的循环。这是一个非常普遍的陷阱,在执行任务之前,检查线程是否被
cancel 了,可以简单的避免外部影响内部线程的循环。我们通过重写上面的代码,在执行任
务前,先检查外部影响,确定线程是否被cancel了。如下代码:
- (void) threadEntryPoint{ @autoreleasepool { NSLog(@"Thread Entry Point"); while ([[NSThread currentThread] isCancelled] == NO){ [NSThread sleepForTimeInterval:4]; if ([[NSThread currentThread] isCancelled] == NO){ NSLog(@"Thread Loop"); } } NSLog(@"Thread Finished"); } }
- (void) stopThread{ NSLog(@"Cancelling the Thread"); [self.myThread cancel]; NSLog(@"Releasing the thread"); self.myThread = nil; }
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.myThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEntryPoint) object:nil]; [self performSelector:@selector(stopThread) withObject:nil afterDelay:3.0f]; [self.myThread start]; self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES; }
Unity3d中提供了场景Scene的概念,Scene就是一组相关联的游戏对象的一个集合,通常每个集合就是一个场景,但是也有可能只是一个场景的一部分!
场景中的游戏对象是任意的,可以是HUD的UI组件,场景地图,模型等等
Unity3d提供了一些切换场景的规则和方法(例如在切换场景时不销毁某些GameObject,同步,异步加载场景API),但是并没有提供一个通用的场景管理的模块(想要做到“通用”是很难的)
在实际开发中,有些开发者摒弃了Scene模块,即整个游戏只有一个Scene,然后自己实现一套“窗口”对象以及“窗口”管理模块,以达到场景管理和通信的目的,这样的好处在于更灵活的控制场景对象;同样,坏处也很明显,即工作量会很大!
我在开发中也做了一套简单的场景管理模块,其主要功能包括:
1.使用一个栈来保存玩家在游戏中场景的载入先后关系(方便Back功能实现以及记录当前场景ID)
2.提供切换场景,压栈场景,出栈场景方法
3.提供异步加载场景,并提供加载进度(用以显示Loading条)
Unity3d将组件设计模式发挥的淋漓尽致,很多开发者都可以方便灵活的制作各种插件,如果足够抽象,便可以为其它项目很方便的使用!Scene Manager就是其中一个,官网地址
Scene Manager提供了2个场景的概念:Screen和Level
Screen:即相互之间没有关联的场景模块(例如登陆场景,主菜单场景,游戏场景之间的关系),其之间并没有严格的先后关系,更接近于Unity3d中Scene的概念
Level:即游戏场景中的关卡模块,有一定的先后关系,并且逻辑相同,Scene Manager为Level提供了一些关卡关系的方法,包括当前关卡,上一个关卡,关卡状态,参考 SMLevelProgress 类
这2个场景的概念在Unity3d看来都是Scene的意义,之所以这样区分是为了将Scene的概念更细化!
其提供了下图的编辑界面,我们只需要创建一个SceneConfiguration来编辑游戏中所有Scene的类别和关系
一旦Scene Configuration创建完成之后,即可以在第一个“Screen场景”中创建出单例类SMGameEnvironment实例,其
其构造方法中完成对SMSceneManager与SMLevelProgress实例的创建:
(注意一定要在Screen场景中实例化SMGameEnvironment,如果是Level场景,则有可能对各个Level之间的关系有错误)
SMSceneManager提供切换场景的接口(包括加载场景,加载关卡,加载第一个关卡)
SMLevelProgress用以保存Level之间的关系(包括当前Level,上一Level,当前Level状态)
SMTransition及其子类,提供了很多方便的切换场景(包括Screen和Level)动画效果,包括 淡入淡出,闪烁,卡通等等
(这些动画效果都作为Prefab保存在SceneManager/Resources/Transitions/下)
SMTransition作为基类,提供了是否异步加载场景,实际调用Unity3d API切换场景方法,但主要提供了一个动画的模板方法 DoTransition(),代码如下:
protected virtual IEnumerator DoTransition() {
// 第一部分:之前场景退出动画 state = SMTransitionState.Out; Prepare(); float time = 0; while(Process(time)) { time += Time.deltaTime; // wait for the next frame yield return 0; } // wait another frame... yield return 0;
// 第二部分:保证SMTransition对象不被销毁(完成后续动画) state = SMTransitionState.Hold; DontDestroyOnLoad(gameObject); // wait another frame... yield return 0; IEnumerator loadLevel = DoLoadLevel(); while (loadLevel.MoveNext()) { yield return loadLevel.Current; } // wait another frame... yield return 0;
// 第三部分:新场景载入动画 state = SMTransitionState.In; Prepare(); time = 0; while(Process(time)) { time += Time.deltaTime; // wait for the next frame yield return 0; } // wait another frame... yield return 0; Destroy(gameObject); }
在SMTransition的子类中,分别实现Prepare()虚方法和Process(float elapsedTime)抽象方法
例如 SMFadeTransition 类中,通过传入参数elapsedTime与配置淡入淡出参数duration计算得到当前进度,正交化进度,得到当前遮盖的alpha值,并在OnGUI绘制,代码如下:
protected override bool Process(float elapsedTime) { float effectTime = elapsedTime; // invert direction if necessary if (state == SMTransitionState.In) { effectTime = duration - effectTime; } progress = SMTransitionUtils.SmoothProgress(0, duration, effectTime); return elapsedTime < duration; }
public void OnGUI() { GUI.depth = 0; Color c = GUI.color; GUI.color = new Color(1, 1, 1, progress); GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), overlayTexture); GUI.color = c; }
其它SMTransition子类也通过Process(float elapsedTime)实现切换动画效果!
PS: 在异步加载场景中,Scene Manager中并没有提供一个获取当前加载进度的接口,需要自己实现,在SMTransition类中
protected virtual YieldInstruction LoadLevel() { if (loadAsync) { AsyncOperation ao = Application.LoadLevelAsync(screenId); Debug.Log("Progress: " + ao.progress); return ao; //return Application.LoadLevelAsync(screenId); } else { Application.LoadLevel(screenId); return null; } }