Unity3D开发之时间段选择

最近在帮同事做一点项目。分配给我了人员位置热力图的功能块。其中一块需要时间段的的选取。效果如下:

看到这个瞬间让我想到很久之前画折线图那块,我们通过给Image新的顶点来绘制我们需要的图形。所以我们需要在Canvas下创建一个空物体并挂上我们自己创建的脚本MyDoubleSlider,由于我们看效果图可以看到一个横条是被分成了12份,所以根据UI我们自己设定空物体的width=960,heigt=30代码如下:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System;

namespace Unite
{
    public class MyDoubleSlider : MaskableGraphic
    {
        //开始时间
        public DateTime StartTime { get {  DateTime dateTime = DateTime.Now.Date; return dateTime.Add(GetTime(leftTran.localPosition.x));}}

        //结束时间
        public DateTime EndTime { get { DateTime dateTime = DateTime.Now.Date; return dateTime.Add(GetTime(rightTran.localPosition.x)); } }

        public string StartTimeStr
        {
            get {return StartTime.ToString("yyyy-MM-dd HH:mm:ss"); }
        }

        public string EndTimeStr
        {
            get { return EndTime.ToString("yyyy-MM-dd HH:mm:ss"); }
        }

        [SerializeField] [Tooltip("是否是被分为整数段 如果是 勾选")]
        private bool IsInteger = true;
        private Transform leftTran, rightTran;

        float width, height,InitPosX;
        float uniteLength;
        protected override void Awake()
        {
            base.Awake();
            leftTran = transform.Find("Left");
            rightTran = transform.Find("Right");
        }

        void Start()
        {
            width = rectTransform.rect.width;
            height = rectTransform.rect.size.y;
            InitPosX = transform.localPosition.x;
            uniteLength = width / 12;
        }


        void Update()
        {
            if(Input.GetKeyDown(KeyCode.Space))
                Debug.Log(StartTime.ToString("yyyy-MM-dd HH:mm:ss")+":"+EndTime.ToString("yyyy-MM-dd HH:mm:ss"));
        }

        TimeSpan GetTime(float xpos)
        {
            float offset = xpos- InitPosX;
            if (IsInteger)
            {
               int count = Mathf.RoundToInt(offset / uniteLength);
                return new TimeSpan(0,12+(int)count*2,0,0);
            }

            else
            {
                float count = offset / uniteLength;

                count = (float)Math.Round(12 + count * 2, 2);
                int inter = (int)count;

                float floater = count - inter;

                int totalseconds = (int)(floater * 3600);

                int minutes = (int)totalseconds / 60;

                int second = totalseconds % 60;
                return new TimeSpan(0, inter, minutes, second);
            }
               
        }


        /// 
        /// 限制一下移动范围 以及移动固定单位时的判定
        /// 
        /// 
        /// 
        /// 
        public Vector2 Clamp(Vector2 v, Transform tran)
        {
            v.y = 0;
            if (tran.Equals(leftTran))
            {
                v.x = Mathf.Clamp(v.x, -width / 2+ InitPosX, rightTran.localPosition.x);
                if (IsInteger)
                {
                    float offset = v.x - leftTran.localPosition.x;
                    offset = Mathf.RoundToInt(offset / uniteLength) * uniteLength;
                    v.x = leftTran.localPosition.x + offset;
                }
            }
            else
            {
                v.x = Mathf.Clamp(v.x, leftTran.localPosition.x, width / 2+ InitPosX);
                if (IsInteger)
                {
                    float offset = v.x - rightTran.localPosition.x;
                    offset = Mathf.RoundToInt(offset / uniteLength) * uniteLength;
                    v.x = rightTran.localPosition.x + offset;
                }
            }
            return v;
        }
        

        /// 
        /// 拖动两端的时候调用这个脚本  
        /// 
        public void OnDrag()
        {
            SetVerticesDirty();//更改顶点  会调用OnPopulateMesh函数
        }

        protected override void OnPopulateMesh(VertexHelper vh)
        {
            vh.Clear();
            UIVertex[] verts = new UIVertex[4];
            verts[0].position = new Vector3(leftTran.localPosition.x, -height / 2);
            verts[0].color = color;
            verts[0].uv0 = Vector2.zero;

            verts[1].position = new Vector3(leftTran.localPosition.x, height / 2);
            verts[1].color = color;
            verts[1].uv0 = Vector2.zero;

            verts[2].position = new Vector3(rightTran.localPosition.x, height / 2);
            verts[2].color = color;
            verts[2].uv0 = Vector2.zero;

            verts[3].position = new Vector3(rightTran.localPosition.x, -height / 2);
            verts[3].color = color;
            verts[3].uv0 = Vector2.zero;

            vh.AddUIVertexQuad(verts);
        }
    }
}

同时需要在此物提下创建两个新的Image,并分别命名为Left,Right。分别设置Left物体的pivot为(1,0.5f),Right的pivot为(0,0.5f)。这样是为了能让两个物体在边界位置正好拼接在一起而不是覆盖。保证点击不被遮挡。Left参数如下:

Unity3D开发之时间段选择_第1张图片

在创建一个专门控制左右滑动的脚本MySliderBtn,分别挂载在Left,Right物体上,代码如下:

  public class MySliderBtn : MonoBehaviour , IDragHandler
    {
        RectTransform rectTran;
        MyDoubleSlider myslider;

        void Awake()
        {
            rectTran = transform.parent.GetComponent();
            myslider = rectTran.GetComponent();
        }


        /// 
        /// Object被拖动的时候
        /// 
        /// 
        public void OnDrag(PointerEventData eventData)
        {
            Vector2 pos;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTran.GetComponent(),
                Input.mousePosition, null, out pos);

            pos = myslider.Clamp(pos,transform);     
            transform.localPosition = pos;
            myslider.OnDrag();
        }
    }

整个功能小块的布局如下图:

这里注意下:OnPopulateMesh函数(专门用于绘制UI组件里的函数,我们只需要传入顶点数据即可)的调用需要更改ui元素的顶点坐标,官方文档并没有讲怎样会调用(Callback function when a UI element needs to generate vertices.)。一顿搜索后找到当调用SetVerticesDirty()函数会去回调OnPopulateMesh。之前写折线图的时候一直以为改变RectTransform的大小就可以刷新OnPopulateMesh函数调用,其实是不正确的。

人员位置热力图已经做完了,但是因为现在都是随机的假数据(噪点太多)以及颜色图谱没有确定,所以做出来的效果不美观。等真实数据来了以后如果效果还不错,就会把位置热力图的实现过程更新上来。

项目工程地址:源码地址。

你可能感兴趣的:(Unity)