欢迎来到我的博客小站。  交流请加我微信好友: studyjava。  也欢迎关注同名公众号:Java学习之道

Unity实战之制作时钟

  |   0 评论   |   0 浏览

大体步骤

在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://mmzsblog.cn/articles/2020/10/21/1603290439731.html
-----------------------------
如未加特殊说明,此网站文章均为原创。
网站转载须在文章起始位置标注作者及原文连接,否则保留追究法律责任的权利。
公众号转载请联系网站首页的微信号申请白名单!

个人微信公众号 ↓↓↓                 

微信搜一搜爱上游戏开发