2、让时钟动起来
现在为止,我们已经有了一个时钟。但是这个时钟只是静静的躺在那里,并不会动。那么 Unity 是不是有现成的组件可以让时钟动起来呢?遗憾的是也没有,我们必须自己写。
所有的组件都是由 scripts(脚本)定义的,所有我们开始这个阶段的工作,先创建一个名叫 Clock 的脚本吧。和创建其他资源一样,脚本也可以通过 Assets → Create → C# Script 的方式创建。
当选中了脚本之后,inspector 就会显示它的内容,还有一个按钮可以让你用代码编辑器打开它。你也可以通过双击来调用 Unity 的默认的代码编辑器。每一个 Unity 创建的脚本都包含 Unity 脚本组件的默认代码,如下:
代码的语言是 C#,Unity 支持的脚本语言,为了搞清楚代码是如何工作的,我们先把它删除,然后从 0 写起。
Q&A:JaveScript 能不能用?
Unity 其实是支持 JaveScript 的,虽然名字叫 JaveScript,但其实它是 UnityScript,只是语法和 JaveScript 很像而已。截止 2017.1.0,Unity 仍然可以通过菜单创建 JavaScript 脚本,但是 2017.2.0 将会移除创建的入口,并且在未来会逐步停止支持。
2.1 定义组件类型
一个空的文件并不是一个有效的脚本。它其实需要包含我们想要的 clock 的组件才行。我们再用代码去实现组件的时候,并不是定一个组件的示例,而是定义一种类型或者类,比如 Clock,一旦这个类或者类型确定了之后,我们就可以通过 Unity 创建很多个组件出来,就像 Unity 其他的内置组件一样。
在 C# 语言里,我们定义一个 Clock 的类型是通过最前面的 class,然后紧跟这个类的名字。在后面的代码展示中,我们会把新加的代码用黄色的底标识,因为是从一个空文件开始写的,所以下面的 classClock 会用黄色的底标识。
在我们定了类之后,还没有给类加上权限约束,所以我们还要在前面加上 public 表示这是一个公开的类,大家都可以使用。
到这里之后,我们还没符合一个有效的 C# 语法。一个完整的类,内容必须被一对大括号包裹 {},类可以是允许是空的,所以这里我们先加上大括号,定一个 Clock 的空类。
现在我们的代码已经可以用了,保存这个文件,然后切回到 Unity,Unity 的编辑器将会在后台对代码进行编译,完成之后,选中这个脚本,inspector 窗口会提示我们尚未包含 MonoBehaviour 脚本。
为了将 Clock 类转换为 MonoBehaviour 类的子类,我们需要对刚才的类申明做一些改造,加上冒号:和要继承的类名,内容如下:
public class Clock :UnityEngine.MonoBehaviour{}
也可以改成:
到现在为止,我们的 Clock 已经可以像其他组件一样通过拖拽或者 Add Component 按钮进行添加了。
2.2 拿到指针
如果要旋转指针,那么 Clock 得知道它们在哪。我们就从时针开始,就像其他的游戏对象一样,它可以通过修改 transform 组件的 rotation 值来完成。那么我们首先就要知道时针的 transform 在哪里。这很好办,在 Clock 的类里添加一个属性,命名为 hoursTransform。当然你可以可以命名为任何你想要的名字,只要你自己知道什么干什么的就可以,代码如下:
这里还有问题,我们已经在代码里命名了,但是代码的编译器并不知道这个名字代表什么意思,所以你还需要给这个名字加一个类型的限定,比如我们想要改变的是 Transfrom,那么就把 Transfrom 加载它的前面,变成这样:
OK,到这里之后代码的编译器已经能明白能要什么了,不会报错了,但是,记得刚才的访问权限吗?如果不加的话,表示这个数据或者属性只能类内部使用,如果想要外部使用的话,也一样需要加上权限修饰符。
当我们的权限是 public 的时候,好玩的事情就发生了,我们切回 Unity,然后选择 clock 对象,还记得我们之前已经把 clock 的脚本组件绑上去了吗?看看这个 clock 脚本里多出了一栏:
2.3 设置 3 个指针
按照刚才的方法,把 3 个指针的引用都拿到:
不建议偷懒合并成一行
处理完之后,我们的时针、分针和秒针都有了,如下:
2.4 掌握时间
现在我们已经能从代码层面找到时钟的指针了,下一步我们需要知道当前的时间是多少才行。那么我们就需要让代码做些事情了。
具体来说就是个类添加一些方法,比如我们先给 Clock 添加一个方法叫 Awake,这个是 Unity 脚本所支持的类,只要物体被加到场景就会执行一次,代码如下:
要访问真正的系统时间,我们需要使用 DataTime 结构,这个不是 Unity 内部的类型,它是 C# 语言的类型,在 System 的命名空间下。也是.NET framework 的一部分。所以我们要想得到真正的时间需要这样修改代码:
2.5 旋转指针
那么现在我们就要把 Log 去掉,做真正的事情了。Unity 本身支持任何物体的旋转 Rotations 。所以我们只要使用 Unity 提供的方法就可以完成角度的旋转,先看下方法怎么用:
那么时针的制作完了,下面把分针和秒针的也做完,效果如下:
现在看看,其实我们刚才一共用了 DateTime.Now 三次,分别是获取时、分、秒的时候。每一次都要消耗性能去获取对应类的属性。那么我们其实可以把它用变量缓存起来,这样我们直接去变量里拿就会节省很多性能开销。那么代码做如下改造:
2.6 让指针动起来
到现在为止,我们可以通过启动编辑器获取一个时钟的正确时间,以及时钟的三个指针都有真缺的位置,但它还是不能自己动。没关系,我们只需要把 Awake 改为 Update 就可以了。
这是为什么呢?前面说过 Awake 整个生命周期只执行一次,但是 Update 是每一帧都会执行一次,所以你现在可以看到动的钟表了。
2.7 连续旋转
到现在为止,我们已经做出了时钟,并且时间可以随着正确的时间而变化,但其实它还是有缺点。很多时候,我们会看到一个时钟,指针在走的时候不是每秒、每分才动一次,而是持续的以很小的间距不断变化。这里我们就看看如何优化这种情况。
首先我们加一个变量表示我们是否要启用这个功能,代码如下:
一个 bool 类型的变量只能由 true 和 false 两种类型,但是它可以从 inspector 面板下进行勾选赋值。
如果选中,表示这个变量值为 true, 没选中表示为 false。
现在我们需要把指针转动的逻辑分为两种情况了,一种是连续的,一种是不连续的。
然后在 Update 里,根据当前变量的值决定使用哪一个函数:
当然上面的其实只执行了连续的情况,不连续的需要用 else 来执行:
在 UpdateContinuous 函数里,我们需要做一个改变,之前使用的是 DataTime 只能告诉我们当前的时、分、秒的值,但它不能告诉我们这些极小的值是多少,比如 4 点半,应该是 4.5 小时。那么我们只能用另外一个结构来代替 TimeSpan。再修改一下代码:
OK,完美实现,但是它编译会报错。因为数据类型不匹配,也就是浮点的精度不匹配,需要做手动的转换,即在time.TotalSeconds前加上(float)作数据类型转换。
总结
这篇教程比较长,一共从两个部分介绍了 Unity:
第一部分教大家怎么认识和操作 Unity 的编辑器界面。以及一些基础的 Unity 概念和用法。
第二部分教大家怎么写脚本和代码,并介绍一些代码的基础知识。认识和了解自定义组件和 Unity 代码的一些基础知识。
所以,教程完结之后,能否真的做出时钟 demo 不重要,重要的是,是否掌握了一些 Unity 的基础知识。之所以很多东西没有一步搞到位,也是要更多的展示一下涉及到的相关操作和改进思路。
当然如果能够自己跟着教程做一遍,完成 demo 制作的话,也是非常了不起的!