うにてぃブログ

主にUnityとC#に関する記事を書いていきます

【Unity】uGUI で時計盤を作る

時計盤を作成することがあったので実装をこちらにメモしておきます

特殊な実装はしていないのでスクリプトを読めばだいたい理解できると思います

f:id:hacchi_man:20211213010221g:plain

スクリプト

using System;
using UnityEngine;
using UnityEngine.UI;
 
public class UIClock : MonoBehaviour
{
    [SerializeField]
    private Transform _imageHourHand;
 
    [SerializeField]
    private Transform _imageMinuteHand;
 
    [SerializeField]
    private Transform _imageSecondHand;
 
    private int _hour;
    private int _minute;
    private int _second;
    private float _millisecond;
 
    private bool _valid;
 
    /// <summary>
    /// 現在の時間を取得
    /// </summary>
    public int Hour => _hour;
    public int Minute => _minute;
    public int Second => _second;
 
#if UNITY_EDITOR
     
    private void Reset()
    {
        if (_imageHourHand == null) 
            _imageHourHand = CreateRectTransform("Hour", 6, 0.5f);
        if (_imageMinuteHand == null) 
            _imageMinuteHand = CreateRectTransform("Minute", 4, 0.9f);
        if (_imageSecondHand == null) 
            _imageSecondHand = CreateRectTransform("Second", 2);
    }
 
    /// <summary>
    /// Temp の針を作成
    /// </summary>
    private RectTransform CreateRectTransform(string name, float width, float heightRate = 1f)
    {
        var rootRect = transform as RectTransform;
        var gameObject = new GameObject(name, typeof(RectTransform));
        gameObject.transform.SetParent(transform);
        gameObject.transform.localPosition = Vector3.zero;
        gameObject.transform.localEulerAngles = Vector3.zero;
        gameObject.transform.localScale = Vector3.one;

        var image = gameObject.AddComponent<Image>();
        image.color = Color.black;
        
        var rectTransform = gameObject.GetComponent<RectTransform>();
        rectTransform.pivot = new Vector2(0.5f, 0f);
        rectTransform.sizeDelta = new Vector2(width, rootRect.rect.height / 2f * heightRate);
        return rectTransform;
    }
    
#endif
 
    /// <summary>
    /// 起動時に現在の時間を反映
    /// </summary>
    private void Awake()
    {
        UpdateTime(DateTime.Now);
        _valid = true;
    }
 
    /// <summary>
    /// 一時的に停止
    /// </summary>
    public void Enable(bool enable)
    {
        _valid = enable;
    }
 
    /// <summary>
    /// 時間を更新
    /// </summary>
    public void UpdateTime(DateTime dateTime)
    {
        UpdateTime(dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond / 1000f);
    }
 
    public void UpdateTime(int hour, int minute, int second, float millisecond = 0f)
    {
        _hour = hour;
        _minute = minute;
        _second = second;
        _millisecond = millisecond;
        SetAngles();
    }
 
    /// <summary>
    /// 現在の値を角度に反映
    /// 時計は時計回りに動いてるので角度を逆にする必要があります
    /// </summary>
    private void SetAngles()
    {
        var localEulerAngles = _imageSecondHand.localEulerAngles;
        localEulerAngles.z = 360 - _second * (360 / 60);
        _imageSecondHand.localEulerAngles = localEulerAngles;

        var t = Mathf.InverseLerp(0, 60, _second);
        localEulerAngles = _imageMinuteHand.localEulerAngles;
        localEulerAngles.z = 360 - (_minute + t) * (360 / 60);
        _imageMinuteHand.localEulerAngles = localEulerAngles;

        t = Mathf.InverseLerp(0, 60, _minute);
        localEulerAngles = _imageHourHand.localEulerAngles;
        localEulerAngles.z = 360 - (_hour + t) * (360 / 12);
        _imageHourHand.localEulerAngles = localEulerAngles;
    }
 
    private void Update()
    {
        if (!_valid)
            return;
 
        _millisecond += Time.deltaTime;
        if (_millisecond < 1f)
            return;
 
        // 時間の加算
        _millisecond -= 1f;
        _second++;
        if (_second >= 60)
        {
            _second = 0;
            _minute++;
            if (_minute >= 60)
            {
                _minute = 0;
                _hour = _hour + 1 % 24;
            }
        }
        SetAngles();
    }
}