Unity实战之制作时钟
大体步骤
在Hierarchy面板中创建一个对象
写一个脚本,将它附加到一个对象上
访问命名空间
通过方法更新对象数据
基于时间旋转物体
在这篇文章中,你将学到很多C#基础的知识,和一个有用的API。
由于昨天克森赶文章赶得太累了,没经过预览检查就直接发布了,导致出现了许多错误,在此克森向大伙们道个歉。因此今早便起来修改了一番,还望大伙们原谅。
在这篇文章中,我们将会写一个小的C#脚本,用于制作一个简单的时钟的动画。
假设你对Unity的编辑器有了一些基础的了解。如果你用Unity玩几分钟,也许你就大体上了解了。
最终效果如图
创建时钟
让我们创建一个新的工程。默认的场景中包含一个position为(0,1,-10)的相机。在相机视口中可以看到场景的一部分,选择这个相机,然后选择菜单中的 GameObject / AlignView便能获得你想看到场景中的某个部分。
我们需要一些游戏对象去代替这个时钟。选中菜单中的GameObject / Create Empty创建一个空的游戏物体(Empty GameObject),设置它的Position为(0,0,0),并且命名为“Clock”。然后再依次创建三个空物体(Empty GameObject)作为Clock的子物体,并且分别命名为“Hours”,“Minutes”,“Seconds”(不用说了吧,就是时针,分针,秒针)。确保它们的Position都为(0,0,0)。
GameObject是什么?
基本上,在场景中的所以物体都是一个GameObject(游戏对象)。它由一个名字(name),一个标签(tag),一个层(layer),一个Transform组件组成,它也可以标记为静态的(static)。它自身不做任何事情,只是一个空的容器。你可以通过给它添加组件让它变为有用的东西。
什么是子物体(child)?
你把一个物体A放到另一个物体B里面(通过在Hierarchy面板中拖拽),那么物体A就被称作是另一个物体B的子物体,也可以说是另一个物体B包含物体A,而物体B被称为父物体。父物体(就是B)的Transform被子物体给继承。
我们将使用简单的盒子去显示时钟的指针。通过菜单中的GameObject / Create Other / Cube 为每一个指针创建一个Cube作为该指针的子物体。为Hours的子物体(Cube)的position设置为(0,1,0),Scale设置为(0.5,2,0.5)。为Minutes的子物体(Cube)的position设置为(0,1.5,0),Scale设置为(0.25,3,0.25)。为Seconds的子物体(Cube)的position设置为(0,2,0),Scale设置为(0.1,4,0.1)。
Clock在Hierarchy中的层次结构
时钟动画
我们需要为Clock的动画创建一个脚本。创建一个新的C#脚本(在Assets面板中右键->Creat->C#Script),命名为“ClockAnimator”。双击打开该脚本,然后把默认的代码都删除了,我们重头再来过。
首先,我们通过使用UnityEngine命名空间来指明我们想要使用的类库。然后我们声明ClockAnimator类,它的访问权限是public(公有的),继承自MonoBehaviour。
using UnityEngine;
public class ClockAnimator : MonoBehaviour
{
}
什么是namespace(命名空间)?
就像一个网站域名一样,但它是一行代码。例如:MonoBehaviour被定义在UnityEngine命名空间里,那么,它可以通过UnityEngine.MonoBehavour来访问。
就像域名一样,命名空间可以嵌套使用。最大的区别在于它的写法是相反的。如使用com.unity3d.forum替代forum.unity3d.com。例如,ArrayList类型存放在Collections命名空间下,而Collection存放在System命名空间下。所以,你需要通过System.Collections.ArrayList去访问到ArrayList。
通过声明使用一个命名空间,我们便不需要写那么复杂的代码来访问某个类了。例如,当我们使用using UnityEngine后,之前的UnityEngine.MonoBehaviour便可直接使用MonoBehaviour访问到。
什么是class(类)?
class是一个蓝图,它常用于创建对象,并存储在你的电脑内存中。蓝图定义了改对象包含了什么数据和它们如何运作。
什么是MonoBehaviour?
MonoBehaviour是一个位于UnityEngine命名空间下的类。如果你创建了一个类,你想要让一个类作为Unity组件来使用,那么你就必须让这个类继承自MonoBehaviour。它包含一些有用的材料(属性和方法)和让某些事情像Update()函数一样工作。
保存它(ClockAnimator类),然后把它添加到Clock对象上(通过直接拖拽或者在Hierarchy面板中选中Clock对象,然后点击Inspector面板中的Add Component按钮为其添加)。
为Clock添加ClockAnimator脚本
该类用于指针动画,我们需要访问他们的Transform组件。为每一个指针添加一个访问权限为public的Transform类型的变量,然后保存该类。
using UnityEngine;
public class ClockAnimator : MonoBehaviour
{
public Transform hours, minutes, seconds;
}
那些public(公有的)变量将成为组件属性,你可以指定一些相应的对象到该属性卡槽中。现在将相对应的Transform对象拖拽到卡槽中,如下图所示:
什么是变量?
所谓变量,就是它是可以改变的。它可以是一个引用类型(如class类型),也可以是一个值类型(如int类型)。一个变量必须指定它的类型,才能使用该变量做事情。
下一个,我们将添加一个Update()函数。这是一个特殊的方法,它将会在游戏运行时的每一帧都调用。我们将使用它去设置指针的旋转。
using UnityEngine;
public class ClockAnimator : MonoBehaviour
{
public Transform hours, minutes, seconds;
private void Update()
{
//暂时不做任何事情
}
}
什么是方法?
方法是一个行为数据块(语句块),在一个类中定义。它可以接收输入和返回一个输出。输入的定义位于方法名的后面,附带括号。这个方法的类型就是它的输出。如果没有输出,那么该方法的类型就是 void 。
Update()函数应该声明为public(公有的)吗?
我们不应该声明Update()函数为公有的(public),因为它不需要被其他类调用和修改。private(私有的)用于某些行为或属性不被外界访问,这样会让你的代码变得更安全,因为其它与它无关的东西都不会改变它。
我们在声明一个行为或属性的时候可以省略private关键字,因为C#默认访问权限就是private。
在保存脚本之前,编辑器将提示我们的组件有一个更新方法和显示一个复选框,该复选框允许我们禁用该脚本,但让,我们保持该脚本是开启的。
时针每一个小时旋转 360/12 度。分针每一分钟旋转 360/60 度。最后,秒钟每一秒钟旋转 360/60 度。让我们第一那些变量,这些变量都是private const float 类型。如下图所示:
using UnityEngine;
public class ClockAnimator : MonoBehaviour
{
private const float
hoursToDegrees = 360f/12f,
mimutesToDegrees = 360f/60f,
secondsToDegrees = 360f/60f;
public Transform hours, minutes, seconds;
private void Update()
{
//暂时不做任何事情
}
}
const(常量)有什么特别之处?
const(常量)关键字表示一个永远改变不了的值。它的值将会在编译时计算,并且可以在任何地方直接引用到它。
每一次更新,我们都需要获得当前时间。System命名空间包含DateTime结构体,DateTime对我们非常有用。他有一个静态属性名Now,该Now包含了当前时间的数据。每次更新我们都需要去获取它的信息,然后用一个变量去存储它的信息。
using UnityEngine;
using System;
public class ClockAnimator : MonoBehaviour
{
private const float
hoursToDegrees = 360f/12f,
mimutesToDegrees = 360f/60f,
secondsToDegrees = 360f/60f;
public Transform hours, minutes, seconds;
private void Update()
{
DateTime time = DateTime.Now;
}
}
什么是struct(结构体)?
结构体是一个蓝图,和class(类)和类似。区别是它可以在任何地方声明,它是一个值类型(如int),而不是一个对象。它不能作为对象来使用。
什么是property(属性)?
property(属性)是一个访问器,它可能是只读或只写。
注意Unity编辑器的属性卡槽也可以看做是一个属性,但是它们两个不是相同的概念。
获得指针的旋转,我们想要改变他们的本地旋转。我们通过使用Quaternion(四元素)直接设置指针的localRotation。Quaternion(四元素)有一个非常好的方法(函数),我们可以使用它去定义一个任意的旋转。
因为我们目视方向是Z轴,Unity使用的是左手坐标系,所以旋转必须围绕-Z轴旋转。
using UnityEngine;
using System;
public class ClockAnimator : MonoBehaviour
{
private const float
hoursToDegrees = 360f/12f,
mimutesToDegrees = 360f/60f,
secondsToDegrees = 360f/60f;
public Transform hours, minutes, seconds;
private void Update()
{
DateTime time = DateTime.Now;
hours.localRotation =
Quaternion.Euler(0f, 0f, time.Hour*-hoursToDegrees);
minutes.localRotation =
Quaternion.Euler(0f, 0f, time.Minute * -mimutesToDegrees);
seconds.localRotation =
Quaternion.Euler(0f, 0f, time.Second * -secondsToDegrees);
}
}
时钟显示为12:44
什么是Quatermion(四元素)?
Quatermion使用它去表示一个3D旋转。虽然比简单的三维向量要难以理解,但是他们有一些有用的特性。Quatermion结构体包含是UnityEngine命名空间下。
改善时钟
在播放模式中,我们的时钟将显示当前的时间。不管怎样,它的行为非常像一个数字时钟,因为它只显示离散的步骤。让我们包括一个选项来更好的模拟显示时间吧。让我们在脚本中添加一个名为“analog”的公有(public)bool类型的变量。使用该变量去确定在Update()函数中做些事情。我们可以在编辑器中切换这个变量的值,即使在游戏运行时。
using UnityEngine;
using System;
public class ClockAnimator : MonoBehaviour
{
private const float
hoursToDegrees = 360f/12f,
mimutesToDegrees = 360f/60f,
secondsToDegrees = 360f/60f;
public Transform hours, minutes, seconds;
public bool analog;
private void Update()
{
DateTime time = DateTime.Now;
hours.localRotation =
Quaternion.Euler(0f, 0f, time.Hour*-hoursToDegrees);
minutes.localRotation =
Quaternion.Euler(0f, 0f, time.Minute * -mimutesToDegrees);
seconds.localRotation =
Quaternion.Euler(0f, 0f, time.Second * -secondsToDegrees);
}
}
对于analog选项,我们需要一个稍微不同的方法。我们将使用DateTime.Now.TimeOfDay来代替DateTime.Now。它是一个 TimeSpan 。这让我们很容易的访问部分运行时间。因为它是一个double类型的值。因此我们需要把它强制转换成float类型。
using UnityEngine;
using System;
public class ClockAnimator : MonoBehaviour
{
private const float
hoursToDegrees = 360f/12f,
mimutesToDegrees = 360f/60f,
secondsToDegrees = 360f/60f;
public Transform hours, minutes, seconds;
public bool analog;
private void Update()
{
if (analog)
{
TimeSpan timeSpan = DateTime.Now.TimeOfDay;
hours.localRotation = Quaternion.Euler(0f,0f,(float)timeSpan.TotalHours * -hoursToDegrees);
minutes.localRotation = Quaternion.Euler(0f, 0f, (float)timeSpan.TotalMinutes * -mimutesToDegrees);
seconds.localRotation = Quaternion.Euler(0f, 0f, (float)timeSpan.TotalSeconds * -secondsToDegrees);
}
else
{
DateTime time = DateTime.Now;
hours.localRotation =
Quaternion.Euler(0f, 0f, time.Hour * -hoursToDegrees);
minutes.localRotation =
Quaternion.Euler(0f, 0f, time.Minute * -mimutesToDegrees);
seconds.localRotation =
Quaternion.Euler(0f, 0f, time.Second * -secondsToDegrees);
}
}
}
时钟在analog模式下显示为12:56
现在,我们的时钟就做好了!!
妈蛋,终于完成了,累死克森了。It's so tired!!!!
PS:这篇主要是面向新手,请大神勿喷,也不要觉得这个是一个烂文章,谢谢。
标题:Unity实战之制作时钟
作者:shirln
地址:https://www.mmzsblog.cn/articles/2020/10/21/1603290439731.html
如未加特殊说明,文章均为原创,转载必须注明出处。均采用CC BY-SA 4.0 协议!
本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。若本站转载文章遗漏了原文链接,请及时告知,我们将做删除处理!文章观点不代表本网站立场,如需处理请联系首页客服。• 网站转载须在文章起始位置标注作者及原文连接,否则保留追究法律责任的权利。
• 公众号转载请联系网站首页的微信号申请白名单!
