一、准备闹钟模型
(一)下载模型
从Unity资源商店和其他模型网站可以下载到各种各样的闹钟模型。为了帮助大家了解机械钟表的设置原理,建议使用带有时针、分针和秒针的钟表,如下图。
注意:时针、分针和秒针最好是挂在闹钟父物体下的三个独立的子物体。
(二)指针归零
如果模型默认的指针未指向12点的位置,那么我们需要手动将其归零。归零后的钟表指针位置如下:
归零后,要观察并记住Inspector中各指针的本地Transform旋转角度(Rotation),这个角度在接下来的脚本中会用到。比如,我的模型各指针旋转角度如下:
二、令闹钟显示当前时间
在Project窗口中新建一个名为Clock的脚本,挂到闹钟物体上。
首先声明变量,除了时针、分针、秒针三个GameObject变量以外,还要声明一个DateTime变量来获取系统时间。因为后面我们还要声明其他变量,为方便区分,可以在这些变量前面加个Header,如下:
 
 
[Header("Clock Parts")] public GameObject hourHand; public GameObject minuteHand; public GameObject secondHand; DateTime currentTime;
用System.DateTime.Now可以获得当前系统时间。
currentTime.Hour:当前小时数
currentTime.Minute:当前分钟数
currentTime.Second:当前秒数
 
 
currentTime = System.DateTime.Now;
那么,如何将系统时间转换为指针的旋转角度呢?以时针为例,可能有小伙伴认为,直接用系统时间的小时数乘以360度再除以24不就行了吗?
这里有两个问题。首先,虽然1天有24个小时,但机械钟表是12小时制,需要先进行换算。第二,时针旋转的角度不仅受到小时数的影响,也受到分钟数和秒数的影响。比如,14:00:00和14:30:00、14:30:40的时针旋转角度肯定是不同的。
带着这个思路,我们先来写时针旋转角度的脚本。
 
 
hourHand.transform.localRotation = UnityEngine.Quaternion.Euler(0.0f, 0.0f, (90.0f + ((currentTime.Hour % 12.0f) * 360.0f / 12.0f) + ((360.0f/12.0f) * (currentTime.Minute % 60.0f) / 60.0f) + ((360.0f/12.0f) * (currentTime.Second % 60.0f)/3600.0f)));
掌握原理之后,分针和秒针旋转角度的计算也是同理。
 
 
void Update() { //更新系统时间并转化为对应的指针旋转角度 currentTime = System.DateTime.Now; hourHand.transform.localRotation = UnityEngine.Quaternion.Euler(0.0f, 0.0f, (90.0f + ((currentTime.Hour % 12.0f) * 360.0f / 12.0f) + ((360.0f/12.0f) * (currentTime.Minute % 60.0f) / 60.0f) + ((360.0f/12.0f) * (currentTime.Second % 60.0f)/3600.0f))); minuteHand.transform.localRotation = UnityEngine.Quaternion.Euler(0.0f, 0.0f, (90.0f + ((currentTime.Minute % 60.0f) * 360.0f / 60.0f) + ((360.0f/60.0f) * (currentTime.Second % 60.0f) / 60.0f))); secondHand.transform.localRotation = UnityEngine.Quaternion.Euler((270.0f - ((currentTime.Second % 60.0f) * 360.0f / 60.0f)), 90.0f, 0.0f); }
写好脚本后,将时针、分针、秒针的GameObject分别拖入相应栏位。
现在运行游戏,闹钟将会显示为当前的系统时间!
三、UI制作
(一)显示界面
显示界面中,将显示当前闹铃时间,以及闹钟的设定按钮和停止按钮。
首先,在Hierarchy窗口中单击右键,选择UI>Canvas,新建画布。在Canvas下新建空物体,重命名为Group,用作显示界面各元素的父物体。
在Group下新建两个Button,分别命名为Set Ring和Stop Ring,将按钮文字分别改为“设定闹铃”和“停止闹铃”,并调整按钮至合适的位置和大小。
再在Group下新建两个Text,并排放置,给第二个Text重命名为Time,将Text的默认文字分别改为“当前闹铃时间:”和“未设定闹铃”,并调整文字的大小、颜色等参数。
制作好的显示界面UI如下所示:
(二)设定闹钟界面
新建一个空物体,命名为Setting Panel,将Transform归零。在Setting Panel下新建画布Canvas。在Canvas下再新建一个Panel,命名为Background,作为闹钟设定界面的背景图,并按需要调整大小、颜色和背景图片(Source Image)。本例中将这张背景图放在屏幕正中。
在Background下新建一个下拉列表(UI>Dropdown)和一个Text,二者并排放在背景图左上,将下拉列表重命名为Dropdown-Hour,将Text的文字改为“时”。点开下拉列表时,应显示24小时制的所有可选小时数,即0-23,所以我们需要在该下拉列表的Options中加入这些选项,如图。
设定分钟和秒的UI制作同理,只不过下拉列表的项数变为60项(0-59)。将分钟和秒的下拉列表分别重命名为“Dropdown - Minute”和“Dropdown - Second”。完成后的UI如下所示:
然后,在Background下新建两个按钮,分别重命名为“Confirm Button”和“Cancel Button”按钮文字分别为“确定”和“取消”。完成后,设定闹钟界面如下图所示:
最后,取消勾选Setting Panel,使之隐藏。
四、设定闹铃脚本
这部分脚本将存储设定的闹铃时间,并显示在UI中。
首先声明变量,为了方便区分,仍然建议在声明变量之前加个Header。
 
 
[Header("UI Elements")] public GameObject settingPanel; public Button settingRingBtn; public Button stopRingBtn; public Button confirmBtn; public Button cancelBtn; public Dropdown hourDropdown; public Dropdown minuteDropdown; public Dropdown secondDropdown; public Text timeText; int hours; int minutes; int seconds;
声明变量后,回到Unity中,为变量赋值。
编写以下脚本,其中包括三个方法,分别用来打开设定面板、设定闹铃时间和关闭设定面板。
 
 
//打开设定面板 void OpenSettingPanel() { settingPanel.SetActive(true); } //设定闹铃时间 void SettingRingTime() { hours = hourDropdown.value; minutes = minuteDropdown.value; seconds = secondDropdown.value; timeText.text = hours + "时" + minutes + "分" + seconds + "秒"; settingPanel.SetActive(false); } //关闭设定面板 void CloseSettingPanel() { settingPanel.SetActive(false); }
最后,在Start中为按钮添加Listener。
 
 
void Start() { settingRingBtn.onClick.AddListener(OpenSettingPanel); confirmBtn.onClick.AddListener(SettingRingTime); cancelBtn.onClick.AddListener(CloseSettingPanel); }
四、闹钟响铃与振动脚本
(一)闹钟响铃
先从网上找一段你喜欢的闹铃音效,保存在Project>Assets文件夹中。然后,在Clock脚本所附的闹钟物体上添加Audio Source组件,将音频拖进去,如图:
在Update语句块中,加入以下脚本:
 
 
void Update() { //如果系统时间等于设定时间,则闹钟响铃 if(currentTime.Hour == hours && currentTime.Minute == minutes && currentTime.Second == seconds) { this.GetComponent<AudioSource>().Play(); } }
(二)闹钟振动
在这个案例中,我们不用动画控制闹钟振动,而用代码来实现。这样做的好处是可以方便地调整闹钟振动的幅度、时间等参数。
和上面一样,首先声明闹钟振动相关变量。
 
 
[Header("Shaking Parameters")] public float time = 3.0f; public float distance = 0.1f; public float delayBetweenShakes = 0f; private Vector3 startPos; private float timer; private Vector3 randomPos;
因为闹钟“振动”这个动作需要一定时间,而且可能是反复进行的,所以适合用协程来写,如下:
 
 
//闹钟振动 public void StartShaking() { StopAllCoroutines(); StartCoroutine(Shake()); } private IEnumerator Shake() { timer = 0f; //time为闹钟振动的总时间 while (timer < time) { timer += Time.deltaTime; //生成随机位置,随机位置=初始位置+(单位半径球体内部随机点位置 * 距离乘数) randomPos = startPos + (UnityEngine.Random.insideUnitSphere * distance); //闹钟移动至该随机位置 transform.position = randomPos; //delayBetweenShakes表示每次振动之间的时间间隔,如为0则连续振动 if (delayBetweenShakes > 0f) { yield return new WaitForSeconds(delayBetweenShakes); } else { yield return null; } } //闹钟回到初始位置 transform.position = startPos; }
注意,time的值最好不长于闹铃音频的时长,否则就会出现闹钟已经不响,但仍然在振动的情况。
只要系统时间等于设定时间,就调用StartShaking方法,让闹钟开始振动。
 
 
void Update() { //如果系统时间等于设定时间,则闹钟开始振动 if(currentTime.Hour == hours && currentTime.Minute == minutes && currentTime.Second == seconds) { StartShaking(); } }
最后,我们可能不想等待闹铃响完,这时点击之前制作的“停止闹铃”就可以关上它。为此,需要写一段停止闹铃的脚本,并在Start语句块中加入对按钮的Listener。
 
 
void Start() { stopRingBtn.onClick.AddListener(StopRing); } void StopRing() { this.GetComponent<AudioSource>().Stop(); StopAllCoroutines(); //由于中途结束了振动脚本,因此需要令闹钟回到初始位置 transform.position = startPos; }
附:本教程完整脚本
 
 
using UnityEngine; using System; using UnityEngine.UI; using System.Collections; public class Clock : MonoBehaviour { [Header("Clock Parts")] public GameObject hourHand; public GameObject minuteHand; public GameObject secondHand; DateTime currentTime; [Header("UI Elements")] public GameObject settingPanel; public Button settingRingBtn; public Button stopRingBtn; public Button confirmBtn; public Button cancelBtn; public Dropdown hourDropdown; public Dropdown minuteDropdown; public Dropdown secondDropdown; public Text timeText; int hours; int minutes; int seconds; [Header("Shaking Parameters")] public float time = 0.2f; public float distance = 0.1f; public float delayBetweenShakes = 0f; private Vector3 startPos; private float timer; private Vector3 randomPos; // Start is called before the first frame update void Start() { //记录闹钟初始位置 startPos = transform.position; settingRingBtn.onClick.AddListener(OpenSettingPanel); confirmBtn.onClick.AddListener(SettingRingTime); cancelBtn.onClick.AddListener(CloseSettingPanel); stopRingBtn.onClick.AddListener(StopRing); } // Update is called once per frame void Update() { //更新系统时间并转化为对应的指针旋转角度 currentTime = System.DateTime.Now; hourHand.transform.localRotation = UnityEngine.Quaternion.Euler(0.0f, 0.0f, (90.0f + ((currentTime.Hour % 12.0f) * 360.0f / 12.0f) + ((360.0f/12.0f) * (currentTime.Minute % 60.0f) / 60.0f) + ((360.0f/12.0f) * (currentTime.Second % 60.0f)/3600.0f))); minuteHand.transform.localRotation = UnityEngine.Quaternion.Euler(0.0f, 0.0f, (90.0f + ((currentTime.Minute % 60.0f) * 360.0f / 60.0f) + ((360.0f/60.0f) * (currentTime.Second % 60.0f) / 60.0f))); secondHand.transform.localRotation = UnityEngine.Quaternion.Euler((270.0f - ((currentTime.Second % 60.0f) * 360.0f / 60.0f)), 90.0f, 0.0f); //如果系统时间等于设定时间,则闹钟响铃 if(currentTime.Hour == hours && currentTime.Minute == minutes && currentTime.Second == seconds) { this.GetComponent<AudioSource>().Play(); } //如果系统时间等于设定时间,则闹钟开始振动 if(currentTime.Hour == hours && currentTime.Minute == minutes && currentTime.Second == seconds) { StartShaking(); } } //打开设定面板 void OpenSettingPanel() { settingPanel.SetActive(true); } //设定闹铃时间 void SettingRingTime() { hours = hourDropdown.value; minutes = minuteDropdown.value; seconds = secondDropdown.value; timeText.text = hours + "时" + minutes + "分" + seconds + "秒"; settingPanel.SetActive(false); } //关闭设定面板 void CloseSettingPanel() { settingPanel.SetActive(false); } //停止闹铃 void StopRing() { this.GetComponent<AudioSource>().Stop(); StopAllCoroutines(); //由于中途结束了振动脚本,因此需要令闹钟回到初始位置 transform.position = startPos; } //闹钟振动 public void StartShaking() { StopAllCoroutines(); StartCoroutine(Shake()); } private IEnumerator Shake() { timer = 0f; //time为闹钟振动的总时间 while (timer < time) { timer += Time.deltaTime; //生成随机位置,随机位置=初始位置+(单位半径球体内部随机点位置 * 距离乘数) randomPos = startPos + (UnityEngine.Random.insideUnitSphere * distance); //闹钟移动至该随机位置 transform.position = randomPos; //delayBetweenShakes表示每次振动之间的时间间隔,如为0则连续振动 if (delayBetweenShakes > 0f) { yield return new WaitForSeconds(delayBetweenShakes); } else { yield return null; } } //闹钟回到初始位置 transform.position = startPos; } }
现在,闹钟就制作完毕了。点击播放键来试试效果吧!
Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐