ugui学习 案例篇

这里的所有都是siki学院的课程我本人自己的练习。

1.处理3d物体和ui之间交互

(1)ui响应带点击事件,3d物体不响应:让他们的脚本都继承 UnityEngine.EventSystems 命名空间下的IPointerClickHandler 接口然后在接口中实现changercolor方法即可(要在相机上添加PhysicsRaycast组件这样3d物体才能相应点击事件)
(2)ui和3d物体都响应点击事件在ui相应的脚本中添加一个方法,通过获取所有的点击物体,来实现。
在这里插入图片描述

2.通过vertexHelper实现圆形图标

1.其实可以通过mask遮罩实现,但是这样(1)多消耗一个drawcall来创建mask做,像素剔除(2)不利于层级的合并,原本同一图集里的ui可以合并层级,仅需一个Drawcall渲染,如果加入Mask,就会将一个ui整体分割成了Mask下的子ui与其他ui,两者只能各自进行层级合并,至少要两个Drawcall。Mask用得多了,一个ui整体会被分割得四分五裂,就会严重影响层次合并的效率了
3.不能精确点击。

using System.Collections;
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor.Sprites;
using UnityEngine;
using UnityEngine.UI;

[AddComponentMenu("UI/Effects/Circle2", 16)]
//[RequireComponent(typeof(Image))]
public class TestCircleScripts : BaseMeshEffect
{
    [SerializeField]
    private int segementCount = 100;
    [SerializeField]
    private float percent = 1;
    private float fill = 1;
    private int circleVertextCount;
    private RectTransform rectTransform = null;
    private Image image = null;     
    private bool isInstall;

    public List<Vector3> vertexList;

    void Start()
    {
        image = GetComponent<Image>();
        rectTransform = GetComponent<RectTransform>();
        Debug.Log("start");
    }
   
    public override void ModifyMesh(VertexHelper vh)
    {
        if (!isActiveAndEnabled) return;

        if (rectTransform == null)
        {
            rectTransform = GetComponent<RectTransform>();
        }

        if (image == null)
        {
            image = GetComponent<Image>();
        }

        if (image.type != Image.Type.Simple)
        {
            return;
        }

        Sprite sprite = image.overrideSprite;

        if (sprite == null || image.overrideSprite == null)
        {
            return;
        }

        vertexList = new List<Vector3>();

        float width = rectTransform.rect.width;
        float height = rectTransform.rect.height;
        float maxSide = Mathf.Max(width, height);

        /*    if(maxSide>512)        
                segementCount = 60;      
            else if(maxSide>256)        
                segementCount = 50;              
            else if(maxSide>128)        
                segementCount = 30;        
            else if(maxSide>64)        
                segementCount = 25;        
            else if(maxSide>32)       
                segementCount = 20;

            else        
                segementCount = 15;*/

        circleVertextCount = segementCount + 1;
        fill = (int)(segementCount * percent);

        vh.Clear();
        Vector2[] uvs = image.overrideSprite.uv;

        float minX = 10000, minY = 10000, maxX = -10000, maxY = -10000;
        for (int i = 0; i < uvs.Length; ++i)
        {
            var uv = uvs[i];
            minX = Mathf.Min(minX, uv.x);
            maxX = Mathf.Max(maxX, uv.x);
            minY = Mathf.Min(minY, uv.y);
            maxY = Mathf.Max(maxY, uv.y);
        }

        //   Debug.Log("MyTest:" +"minX" + minX + "minY" + minY + "maxX " + maxX + " maxY" + maxY);

        float uvCenterX = (minX + maxX) * 0.5f;
        float uvCenterY = (minY + maxY) * 0.5f;

        //  Debug.Log("MyTest:" + uvCenterX + " " + uvCenterY);
        
        float uvScaleX = (maxX - minX) / width;
        float uvScaleY = (maxY - minY) / height;

        float radian = 2 * Mathf.PI / segementCount;
        float radius = 0.5f * width;

        UIVertex centerVertex = new UIVertex();
        centerVertex.position = Vector2.zero;
        centerVertex.color = image.color;
        centerVertex.uv0 = new Vector2(uvCenterX, uvCenterY);
        vh.AddVert(centerVertex);
        
        //  Debug.Log("MyTest:" + centerVertex.uv0);
        float curRadian = 0;
        for (int i = 1; i < circleVertextCount; i++)
        {
            UIVertex vertex = new UIVertex();
            float x = Mathf.Cos(curRadian) * radius;
            float y = Mathf.Sin(curRadian) * radius;

            vertex.position = new Vector2(centerVertex.position.x + x, centerVertex.position.y + y);

            if (i <= fill)
            {
                vertex.color = image.color;
            }

            else
            {
                vertex.color = new Color32(60, 60, 60, 255);
            }

            var uv_x = x * uvScaleX + uvCenterX;
            var uv_y = y * uvScaleY + uvCenterY;
            vertex.uv0 = new Vector2(uv_x, uv_y);

            vh.AddVert(vertex);
            vertexList.Add(vertex.position);
            curRadian += radian;
        }
        
        for (int i = 1; i < circleVertextCount; i++)
        {

            if (i == circleVertextCount - 1)
            {
                vh.AddTriangle(i, 0, 1);
            }
            else
            {
                vh.AddTriangle(i, 0, i + 1);
            }
        }
    }
}

实现的效果如下
在这里插入图片描述
这个代码并且实现了技能冷却
在这里插入图片描述

2.1在以上基础上进行图片的精确点击

在这里插入图片描述

因为第二个只是进行了图片的mesh顶点的重构,但是这个响应的区域是在这个框内,所以编写脚本实现精确点击的算法。

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

public class ship : Image
{
    TestCircleScripts testCircleScripts;
    List<Vector3> vertexList = new List<Vector3>();

    public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
    {
        Vector2 localPoint;

        RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, null, out localPoint);
        Debug.Log("localPoint:" + localPoint);

        return IsValid(localPoint);
    }

    private void Start()
    {
        testCircleScripts = GetComponent<TestCircleScripts>();
        vertexList = testCircleScripts.vertexList;
        Debug.Log(vertexList[1]);
        Debug.Log("vertexlistcount:" + vertexList.Count);
    }



    private bool IsValid(Vector2 localPosition)
    {
        bool isvalidbool;
        isvalidbool = GetCrossPointNum(localPosition) % 2 == 1;
        Debug.Log("isvalidbool:" + isvalidbool);
        return isvalidbool;
    }

    private int GetCrossPointNum(Vector2 localPosition)
    {
        int count = 0;
        Vector3 vert1 = Vector3.zero;
        Vector3 vert2 = Vector3.zero;
        int vertexCount = vertexList.Count;

        for (int i = 0; i < vertexCount; i++)
        {
            vert1 = vertexList[i];
            vert2 = vertexList[(i + 1) % vertexCount];

            if (IsYInRange(localPosition, vert1, vert2))
            {
                float k = (vert2.y - vert1.y) / (vert2.x - vert1.x);
                float b = vert2.y - (vert2.x * k);
                float x = (localPosition.y - b) / k;
                if (localPosition.x <= x)
                {
                    count++;
                }

            }
        }
        Debug.Log("count:" + count);
        return count;
    }

    private bool IsYInRange(Vector2 localPosition, Vector3 vert1, Vector3 vert2)
    {
        bool isyinrangeBool;
        if (vert1.y < vert2.y)
        {
            isyinrangeBool = vert1.y < localPosition.y && vert2.y > localPosition.y;

        }
        else
        {
            isyinrangeBool = vert1.y > localPosition.y && vert2.y < localPosition.y;

        }
        Debug.Log("isyinrang:" + isyinrangeBool);
        return isyinrangeBool;
    }
}

2.2实现精确点击的方法二

给图片开启read write enable 模式
然后在写一个脚本 获得image组件中的alphaHitTestMinimumThreshold并且将它的值设置为0.1f即可,这种方法不推荐使用因为这样的图片无法打包成图集,并且图片开启这个模式会造成资源的浪费。

2.3实现精确点击的方法三

(1)给图片添加 Polygon Collider 2D 组件,在图片上编辑碰撞体的大小
(2)给物体添加脚本实现碰撞体的检测

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

public class CustomImage : Image
{
    private PolygonCollider2D _PolygonCollider2D;
    public PolygonCollider2D PolygonCollider2D
    {
        get
        {
            if (_PolygonCollider2D == null)
            {
                _PolygonCollider2D = GetComponent<PolygonCollider2D>();
            }

            return _PolygonCollider2D;
        }        
    }
       
    public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
    {
        Vector3 point;
        RectTransformUtility.ScreenPointToWorldPointInRectangle(rectTransform, screenPoint, eventCamera, out point);
        return PolygonCollider2D.OverlapPoint(point);
    }
}

4.1用2D imag实现3D的轮转

(1)首先在canvas下创建一个空物体
(2)通过代码创建子物体并未它添加脚本

 /// <summary>
    /// 创建一个旋转图的游戏物体,并为它们添加组件
    /// </summary>
    /// <returns></returns>
    private GameObject CreatTeamMate()
    {
        GameObject item = new GameObject("TempMate");
        item.AddComponent<RectTransform>().sizeDelta = ItemSize;
        item.AddComponent<Image>();
        item.AddComponent<RotationDiagramItem>();
        return item;
    }
    
    /// <summary>
    /// 为创建的游戏物体的相关组件赋值
    /// </summary>
    private void CreatItem()
    {
        GameObject item = CreatTeamMate();

        RotationDiagramItem itemTemp = null;
        foreach (Sprite sprite in sprites)
        {
            itemTemp = Instantiate(item).GetComponent<RotationDiagramItem>();
            itemTemp.SetParent(transform);
            itemTemp.SetSprite(sprite);

            itemTemp.AddMoveListener(Change);

            items.Add(itemTemp);
        }
    }

(3)因为这是用2D Image 模仿3D轮转图 所以说是通过控制它的位置x和缩放
这里是它控制缩放和位置的算法

 private float GetX(float radio , float length)
    {
        if(radio<0||radio>1)
        {
            return 0;
        }
        else if(radio>=0&&radio<=0.25)
        {
            return radio * length;
        }
        else if(radio>0.25&&radio<0.75)
        {
            return (0.5f - radio) * length;
        }
       
        else
        {
            return (radio - 1) * length;
        }


    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="min"></param>
    /// <param name="max"></param>
    /// <param name="radio">相当于在这个3D轮转中这个物体的x值,这个圆环是从零到一的一个轮转</param>
    /// <returns></returns>
    private float GetScaleTimes(float min,float max,float radio)
    {
        float scalePercent = (max - min) / 0.5f;

        if(radio<0&&radio>1)
        {
            return 0;
        }

        else if(radio==0||radio==1)
        {
            return scalePercent;
        }

        else if(radio>0&&radio<=0.5)
        {
            return scalePercent * (1 - radio);
        }
        else
        {
            return scalePercent * radio;
        }

    }

(4)最后就是实现位置的替换,和拖拽详细代码我放在下边

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;

public class RotationDiagram2D : MonoBehaviour
{
    public Sprite[] sprites;
    public Vector2 ItemSize;

    /// <summary>
    /// 是一个类存的子物体的位置信息
    /// </summary>
    public List<itemPosData> ItemPose;
    /// <summary>
    /// 是一个结构存的子物体的id信息
    /// </summary>
    public List<itemID> itemIDs;

    

    public float offest;
    public float scaleMax;
    public float scaleMin;

    /// <summary>
    /// 是子物体上的组件
    /// </summary>
    private List<RotationDiagramItem> items;

    void Start()
    {
        items = new List<RotationDiagramItem>();
        ItemPose = new List<itemPosData>();
        itemIDs = new List<itemID>();

        CreatItem();

        CalulaetData();

        SetItemData();
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    
    /// <summary>
    /// 创建一个旋转图的游戏物体,并为它们添加组件
    /// </summary>
    /// <returns></returns>
    private GameObject CreatTeamMate()
    {
        GameObject item = new GameObject("TempMate");
        item.AddComponent<RectTransform>().sizeDelta = ItemSize;
        item.AddComponent<Image>();
        item.AddComponent<RotationDiagramItem>();
        return item;
    }
    
    /// <summary>
    /// 为创建的游戏物体的相关组件赋值
    /// </summary>
    private void CreatItem()
    {
        GameObject item = CreatTeamMate();

        RotationDiagramItem itemTemp = null;
        foreach (Sprite sprite in sprites)
        {
            itemTemp = Instantiate(item).GetComponent<RotationDiagramItem>();
            itemTemp.SetParent(transform);
            itemTemp.SetSprite(sprite);

            itemTemp.AddMoveListener(Change);

            items.Add(itemTemp);
        }
    }

    private void Change(float offsetX)
    {
        int symbol = offsetX > 0 ? 1 : -1;

        Debug.Log(symbol);
    
        foreach (RotationDiagramItem item in items)
        {
            item.ChangeId(symbol, items.Count);
        }

        for(int i=0;i<ItemPose.Count;i++)
        {
            items[i].SetData(ItemPose[items[i].ID]);
        }


    }




    private void CalulaetData()
    {
        float length = (ItemSize.x + offest) * items.Count;
        float radioOffset = 1.0f / items.Count;

        float radio = 0;
        
        for(int i=0;i<items.Count;i++)
        {
            itemID itemID = new itemID();
            itemID.id = i;
            itemIDs.Add(itemID);

            items[i].ID = i;




            itemPosData data = new itemPosData();
            data.x = GetX(radio, length);
            data.ScaleTimes = GetScaleTimes(scaleMin, scaleMax, radio);

            radio += radioOffset;
            ItemPose.Add(data);
        }

        itemIDs =  itemIDs.OrderBy(u => ItemPose[u.id].ScaleTimes).ToList();

        for(int i=0;i<itemIDs.Count;i++)
        {
            ItemPose[itemIDs[i].id].order = i;
        }

    }


    private void SetItemData()
    {
        for(int i=0; i<ItemPose.Count;i++)
        {
            items[i].SetData(ItemPose[i]);
        }

    }



    private float GetX(float radio , float length)
    {
        if(radio<0||radio>1)
        {
            return 0;
        }
        else if(radio>=0&&radio<=0.25)
        {
            return radio * length;
        }
        else if(radio>0.25&&radio<0.75)
        {
            return (0.5f - radio) * length;
        }
       
        else
        {
            return (radio - 1) * length;
        }


    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="min"></param>
    /// <param name="max"></param>
    /// <param name="radio">相当于在这个3D轮转中这个物体的x值,这个圆环是从零到一的一个轮转</param>
    /// <returns></returns>
    private float GetScaleTimes(float min,float max,float radio)
    {
        float scalePercent = (max - min) / 0.5f;

        if(radio<0&&radio>1)
        {
            return 0;
        }

        else if(radio==0||radio==1)
        {
            return scalePercent;
        }

        else if(radio>0&&radio<=0.5)
        {
            return scalePercent * (1 - radio);
        }
        else
        {
            return scalePercent * radio;
        }

    }

    public class itemPosData
    {
        public float x;
        public float ScaleTimes;
        public int order;
    }

    public struct itemID
    {
        public int id;
    }



}

然后是子物体上的代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using static RotationDiagram2D;
using UnityEngine.EventSystems;
using System;
using DG.Tweening;

public class RotationDiagramItem : MonoBehaviour,IDragHandler,IEndDragHandler
{

    public int ID;
    public float offset;

    private Action<float> moveAction;

    private Image _Image;
    private Image Image
    {
        get
        {
            if (_Image == null)
            {
               _Image = GetComponent<Image>();
            }
            return _Image;
        }
    }

    private RectTransform _rect;

    private RectTransform rect
    {
        get
        {
            if(_rect ==null)
            {
                _rect = GetComponent<RectTransform>();
            }
            return _rect;
        }        
    }
   

    public void SetParent(Transform parent)
    {
        transform.parent = parent;
    }

    public void SetSprite(Sprite sprite)
    {
        Image.sprite = sprite;
    }


    /// <summary>
    /// 设置子物体的位置信息
    /// </summary>
    /// <param name="data"></param>
    public void SetData(itemPosData data)
    {

        rect.DOAnchorPos(Vector2.right * data.x, 0.5f);
        rect.DOScale(Vector3.one * data.ScaleTimes, 0.5f);

        // rect.anchoredPosition = data.x * Vector2.right;
        // rect.localScale = Vector3.one * data.ScaleTimes;
        StartCoroutine(Wait(data.order));
    }

    private IEnumerator Wait(int order)
    {
        yield return new WaitForSeconds(0.5f);

        transform.SetSiblingIndex(order);
    }



    public void ChangeId(int symbol,int totalNum)
    {
        int id = ID;

        id += symbol;
        if(id<0)
        {
            id += totalNum;
        }
        ID = id % totalNum;
    }


   

    public void OnDrag(PointerEventData eventData)
    {
        offset += eventData.delta.x;
    }




    public void OnEndDrag(PointerEventData eventData)
    {
        moveAction(offset);
        offset = 0;
    }

    public void AddMoveListener(Action<float> onMove)
    {
        moveAction = onMove;
    }
}

5实现雷达图

因为在游戏中一个雷达图能够直观的反映出你的能力,所以本次针对这个写了一个雷达图
(1)创建一个image附上背景图,在创建一个子物体
(2)将代码添加到这个物体上即可

添加到子物体上的脚本

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

public class RadarChart : Image
{
    [SerializeField]
    private int _pointCount;
    [SerializeField]
    private List<RectTransform> _points;
    [SerializeField]
    private float[] _handlerRadio;
    [SerializeField]
    private List<RadarChartHandler> _handlers;
    [SerializeField]
    private Color _pointColor = Color.white;
    [SerializeField]
    private Sprite _pointSprite;
    [SerializeField]
    private Vector2 _pointSize = new Vector2(10,10);

    /// <summary>
    ///填充Mesh
    /// </summary>
    /// <param name="vh"></param>
    protected override void OnPopulateMesh(VertexHelper vh)
    {
        vh.Clear();
        AddVertexs(vh);
        AddTriangle(vh);
    }

    /// <summary>
    /// 添加顶点
    /// </summary>
    /// <param name="vh"></param>
    private void AddVertexs(VertexHelper vh)
    {
        vh.AddVert(rectTransform.pivot, color, new Vector2(0.5f, 0.5f));
            /* foreach (RadarChartHandler handler in _handlers)
         {
             vh.AddVert(handler.transform.localPosition, color, Vector2.zero);
         }*/
        AddVertexUV(vh);
    }
    /// <summary>
    /// 添加uv
    /// </summary>
    /// <param name="vh"></param>
    private void AddVertexUV(VertexHelper vh)
    {
        vh.AddVert(_handlers[0].transform.localPosition, color,new Vector2(0.5f, 1));
        vh.AddVert(_handlers[1].transform.localPosition, color, new Vector2(0, 1));
        vh.AddVert(_handlers[2].transform.localPosition, color, new Vector2(0, 0));
        vh.AddVert(_handlers[3].transform.localPosition, color, new Vector2(1, 0));
        vh.AddVert(_handlers[4].transform.localPosition, color, new Vector2(1, 1));

    }


    /// <summary>
    /// 设置三角序列
    /// </summary>
    /// <param name="vh"></param>
    private void AddTriangle(VertexHelper vh)
    {
        for(int i=1;i<_pointCount;i++)
        {
            vh.AddTriangle(i, 0, i + 1);
        }
        vh.AddTriangle(_pointCount, 0, 1);

    }


    /// <summary>
    /// 初始化顶点
    /// </summary>
    public void InitPoint()
    {
        ClearPoints();
        _points = new List<RectTransform>();
        SpawnPoint();
        SetPointPos();
    }

    /// <summary>
    /// 清空顶点
    /// </summary>
    private void ClearPoints()
    {
        if(_points==null)
        {
            return;
        }

        foreach (RectTransform point in _points)
        {
            if(point!=null)
            {
                DestroyImmediate(point.gameObject);
            }
        }
    }

    /// <summary>
    /// 初始化可拖动顶点
    /// </summary>
    public void InitHandlers()
    {
        ClearHandlers();
        _handlers = new List<RadarChartHandler>();
        SpawnHandlers();
        SetHandlerPos();
    }

    /// <summary>
    /// 清空可拖动顶点
    /// </summary>
    private void ClearHandlers()
    {
        if (_handlers == null)
            return;

        foreach (RadarChartHandler handler in _handlers)
        {
            if (handler != null)
                DestroyImmediate(handler.gameObject);
        }
    }


    /// <summary>
    /// 生成背景顶点
    /// </summary>
    private void SpawnPoint()
    {
        for(int i=0;i<_pointCount;i++)
        {
            GameObject point = new GameObject("point" + i);
            point.transform.SetParent(transform);
            _points.Add(point.AddComponent<RectTransform>());
        }
    }

    /// <summary>
    /// 设置顶点位置
    /// </summary>
    private void SetPointPos()
    {
        float radian = 2 * Mathf.PI / _pointCount;
        float radius = 100;

        float curRadian = 2 * Mathf.PI / 4.0f;

        for(int i=0;i<_pointCount;i++)
        {
            float x = Mathf.Cos(radian) * radius;
            float y = Mathf.Sin(radian) * radius;
            curRadian += radian;

            _points[i].anchoredPosition = new Vector2(x, y);
        }

    }

    /// <summary>
    /// 生成可拖动顶点
    /// </summary>
    private void SpawnHandlers()
    {
        RadarChartHandler handler = null;
        for(int i=0;i<_pointCount;i++)
        {
            GameObject Handler = new GameObject("Handler" + i);
            
            Handler.AddComponent<Image>();
            Handler.AddComponent<RectTransform>();

            handler = Handler.AddComponent<RadarChartHandler>();
            handler.SetParents(transform);

            handler.ChangColor(_pointColor);
            handler.ChangeSprite(_pointSprite);

            handler.SetSize(_pointSize);
            handler.SetScale(Vector3.one);

            _handlers.Add(handler);
        }
    }


    /// <summary>
    /// 设置可拖动顶点位置
    /// </summary>
    private void SetHandlerPos()
    {
        if(_handlerRadio==null||_handlerRadio.Length!=_pointCount)
        {
            for(int i=0;i<_pointCount;i++)
            {

                _handlers[i].SetPos(_points[i].anchoredPosition);
            }



        }
        else
        {

            for(int i=0;i<_pointCount;i++)
            {
                _handlers[i].SetPos(_points[i].anchoredPosition * _handlerRadio[i]);
            }

        }



    }



    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        //可以实时更新顶点
        SetVerticesDirty();
    }
}

handler上的脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class RadarChartHandler : MonoBehaviour,IDragHandler
{
    private Image _image;
    private Image Image
    {
        get
        {
            if(_image==null)
            {
                _image = GetComponent<Image>();
            }
            return _image;
        }        
    }

    private RectTransform _rect;
    private RectTransform Rect
    {
        get
        {
            if(_rect==null)
            {
                _rect=GetComponent<RectTransform>();
            }
            return _rect;
        }


    }






    public void SetParents(Transform _transform)
    {
        transform.SetParent(_transform);
    }

    public void ChangColor(Color color)
    {
        Image.color = color;
    }

    public void ChangeSprite(Sprite sprite)
    {
        Image.sprite = sprite;
    }

    public void SetPos(Vector2 pos)
    {
        Rect.anchoredPosition = pos;
    }

    public void SetSize(Vector2 size)
    {
        Rect.sizeDelta = size;
    }
    public void SetScale(Vector3 scale)
    {
        Rect.localScale = scale;
    }


    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void OnDrag(PointerEventData eventData)
    {
        Rect.anchoredPosition += eventData.delta/GetScale();
    }
    private float GetScale()
    {
        return Rect.lossyScale.x;
    }
}

编辑器脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(RadarChart), true)]
[CanEditMultipleObjects]
public class RadarChartEditor : UnityEditor.UI.ImageEditor
{
    SerializedProperty _pointCount;
    SerializedProperty _pointSprite;
    SerializedProperty _pointColor;
    SerializedProperty _pointSize;
    SerializedProperty _handlerRadio;

    protected override void OnEnable()
    {
        base.OnEnable();
        _pointCount = serializedObject.FindProperty("_pointCount");
        _pointSprite = serializedObject.FindProperty("_pointSprite");
        _pointColor = serializedObject.FindProperty("_pointColor");
        _pointSize = serializedObject.FindProperty("_pointSize");
        _handlerRadio = serializedObject.FindProperty("_handlerRadio");
    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        serializedObject.Update();

        EditorGUILayout.PropertyField(_pointCount);
        EditorGUILayout.PropertyField(_pointSprite);
        EditorGUILayout.PropertyField(_pointColor);
        EditorGUILayout.PropertyField(_pointSize);
        EditorGUILayout.PropertyField(_handlerRadio, true);

        RadarChart radar = target as RadarChart;
        if (radar != null)
        {
            if (GUILayout.Button("生成雷达图顶点"))
            {
                radar.InitPoint();
            }

            if (GUILayout.Button("生成内部可操作顶点"))
            {
                radar.InitHandlers();
            }
        }
        serializedObject.ApplyModifiedProperties();
        if (GUI.changed)
        {
            EditorUtility.SetDirty(target);
        }

    }
}

6.scroll 实现图片的加载

1.首先获得显示item的数量,并且生成item
2.然后根据图片的数量设置滚动视图的大小
3.设置id
4.当发生滑动时调用事件changid

挂在父物体上的脚本

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

public class LoopList : MonoBehaviour
{
    // Start is called before the first frame update

    public float offsetY;
    private float _itemHeight;

    private int num;

    private RectTransform _content;

    private List<LoopListItem> _items;
    private List<LoopListItemModel> _models;
   
    void Start()
    {
        _items = new List<LoopListItem>();
        _models = new List<LoopListItemModel>();
        GetModel();

        _content = transform.Find("Viewport/Content").GetComponent<RectTransform>();
        
        GameObject itemPerfab = Resources.Load<GameObject>("LoopListItem");
        _itemHeight = itemPerfab.GetComponent<RectTransform>().rect.height;
        num =  GetShowItemNum(_itemHeight);
        SpawnItem(itemPerfab);

        SetContentSize();
        transform.GetComponent<ScrollRect>().onValueChanged.AddListener(ValueChange);


    }

    private void ValueChange(Vector2 data)
    {
        foreach (LoopListItem item in _items)
        { 
            item.OnValueChange();
        }
    }

    


    private int GetShowItemNum(float itemHeight)
    {
        float Height = GetComponent<RectTransform>().rect.height;

        return Mathf.CeilToInt(Height / (itemHeight+offsetY)) + 1;

    }
        
    /// <summary>
    /// 生成每个物体,并为他们添加组件
    /// </summary>
    private void SpawnItem(GameObject perfab)
    {
        GameObject tempObject = null;
        LoopListItem item = null;

        for(int i=0;i<num;i++)
        {
            tempObject = Instantiate(perfab, _content);
            item = tempObject.AddComponent<LoopListItem>();

            item.AddGetDataListener(GetData);
            item.Init(i,offsetY, num);
            item._id = i;

            _items.Add(item);


        }
    }

    private LoopListItemModel GetData(int index)
    {
       if(index<0||index>=_models.Count)
       {
            return new LoopListItemModel();  
       }

        return _models[index];
    }

    private void GetModel()
    {
        foreach (var sprite in Resources.LoadAll<Sprite>("Icon"))
        {
            _models.Add(new LoopListItemModel(sprite, sprite.name));
        }
    }


    private void SetContentSize()
    {
        float height = _models.Count * (_itemHeight + offsetY) - offsetY;
        _content.sizeDelta = new Vector2(_content.sizeDelta.x, height);
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

子物体脚本

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

public class LoopListItem : MonoBehaviour
{
    public int _id;
    private RectTransform _rect;
    private RectTransform Rect
    {
        get
        {
            if(_rect ==null)
            {
                _rect = GetComponent<RectTransform>();
            }
            return _rect;
        }
    }

    private Image _icon;
    private Image Icon
    {
      get
      {
          if(_icon == null)
          {
                _icon = transform.Find("Image").GetComponent<Image>();
                    //GetComponent<Image>();
          }
            return _icon;
      }
    }

    private Text _des;
    public Text Des
    {
        get
        {
            if (_des == null)
                _des = transform.Find("Text").GetComponent<Text>();
            return _des;
        }
    }

    private RectTransform _connet;

    private Func<int, LoopListItemModel> _getData;

    private float _offfset;
    private int _showNum;
    private LoopListItemModel _modle;

    public void AddGetDataListener(Func<int ,LoopListItemModel> getData)
    {
        _getData = getData;
    }

    public void Init(int id,float offset,int showNum)
    {
        _id = -1;
        _connet = transform.parent.GetComponent<RectTransform>();
        _offfset = offset;
        _showNum = showNum;

        ChangeId(id);
    }


    public void OnValueChange()
    {
        int startId, endId = 0;
        UpdataIdRange(out startId,out endId);
        JudgeSelfId(startId, endId);
    }

    private void UpdataIdRange(out int startId, out int endId)
    {
        startId = Mathf.FloorToInt(_connet.anchoredPosition.y / (Rect.rect.height + _offfset));
        endId = startId + _showNum - 1;
       // JudgeSelfId(startId,endId)
    }

    private void JudgeSelfId(int startId,int endId)
    {
        int offset = 0;
        if(_id<startId) 
        {
           // offset = startId - _id - 1;
            ChangeId(endId-offset);
        }
        else if(_id>endId)
        {
           // offset = _id - endId - 1;
            ChangeId(startId+offset);
        }
    }

    private void ChangeId(int id)
    {
        if(_id !=id&&JudgeIdValid(id))
        {
            _id = id;
            _modle = _getData(id);
            Icon.sprite = _modle.Icon;
            Des.text = _modle.Describe;
            SetPos();
        }
    }

    private void SetPos()
    {
        Rect.anchoredPosition = new Vector2(0, -_id * (Rect.rect.height + _offfset));
    }


    private bool JudgeIdValid(int id)
    {
        return !_getData(id).Equals(new LoopListItemModel());
    }

}

模型用来储存图片

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public struct LoopListItemModel
{   
    public Sprite Icon;
    public string Describe;

    public LoopListItemModel(Sprite icon, string describe)
    {
        Icon = icon;
        Describe = describe;
    }

}





效果
在这里插入图片描述

7.实现血条

0.设置血条的中心点和自适应(左边)
1.把血条加载出来并为它添加脚本(C: SpawnLifeBar)
2.设置血条的位置,为模型的上方。(Bar:GetOffset Update)
3.血条的数值设置(血条的最大值以及血条里的数据存在struct里)(LifeBarData)
4.获得血条每单位的长度( _unitLifeScale)
5.设置子项数据(Bar Item:SetBarData SetData)
6.子项数据初始化
7.扣血逻辑,超限逻辑,跟换血条逻辑。(ChangLife,GetOutOfRange,ChangeIndex)

控制模型脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Controller : MonoBehaviour
{

    private LifeBar _bar;
    // Start is called before the first frame update
    void Start()
    {
        Canvas canvas = FindObjectOfType<Canvas>();
        if(canvas == null)
        {
            return;
        }


        SpawnLifeBar(canvas);
    }

    private void SpawnLifeBar(Canvas canvas)
    {
        GameObject perfab = Resources.Load<GameObject>("LifeBar");
        _bar = Instantiate(perfab, canvas.transform).AddComponent<LifeBar>();

        List<LifeBarData> datas = new List<LifeBarData>();
        datas.Add(new LifeBarData(null, Color.green));
        datas.Add(new LifeBarData(null, Color.red));
        datas.Add(new LifeBarData(null, Color.yellow));


        _bar.Init(transform,datas,350);
    }


    // Update is called once per frame
    void Update()
    {
        if(Input.GetKey(KeyCode.A))
        {
            Move(Vector3.left);
        }
        if (Input.GetKey(KeyCode.W))
        {
            Move(Vector3.forward);
        }
        if (Input.GetKey(KeyCode.S))
        {
            Move(Vector3.back);
        }
        if (Input.GetKey(KeyCode.D))
        {
            Move(Vector3.right);
        }
        if(Input.GetMouseButtonDown(0))
        {
            _bar.ChangLife(-8);
        }
        if(Input.GetMouseButtonDown(1))
        {
            _bar.ChangLife(8);
        }
    }

    private void Move(Vector3 direction)
    {
        transform.Translate(direction * Time.deltaTime * 3);
    }
}

父物体

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LifeBar : MonoBehaviour
{
    /// <summary>
    /// target就是这个飞机模型
    /// </summary>
    private Transform _target;
    private Vector3 _offset;

    /// <summary>
    /// 每个血条的颜色和图片
    /// </summary>
    private List<LifeBarData> _data;

    private LifeBarItem _currentBar;
    private LifeBarItem _nextBar;

    private float _unitLifeScale;
    private int _lifeMax;

    public int _currentIndex;
    // Start is called before the first frame update
    void Start()
    {
        
    }


    public void Init(Transform parent,List<LifeBarData> data,int lifeMax)
    {
        _currentIndex = 0;
        _lifeMax = lifeMax;
        _target = parent;
        _offset = GetOffset(_target);
        _data = data;

        _currentBar = GameObject.Find("CurrentBar").AddComponent<LifeBarItem>();
        _nextBar = GameObject.Find("NextBar").AddComponent<LifeBarItem>();

        

        _currentBar.Init();
        _nextBar.Init();

        RectTransform rect = GetComponent<RectTransform>();
        _unitLifeScale = rect.rect.width * _data.Count / _lifeMax;

        SetBarData(_currentIndex, data);
    }

    public void ChangLife(float value)
    {
        float width = _currentBar.ChangLife(value * _unitLifeScale);

        if(width<0&&ChangeIndex(1))
        {
            Exchange();
            _currentBar.transform.SetAsLastSibling();
            _nextBar.RestDefault();
            SetBarData(_currentIndex, _data);
            ChangLife(width / _unitLifeScale);
        }

        else if(width>0&&ChangeIndex(-1))
        {
            Debug.Log("width<0");
            Exchange();
            _currentBar.transform.SetAsLastSibling();
            _currentBar.RestToZero();
            SetBarData(_currentIndex, _data);
            ChangLife(width / _unitLifeScale);
        }

    }

    private bool ChangeIndex(int symbol)
    {

        int index = _currentIndex + symbol;
     //   Debug.Log(index);
        if(index>=0&&index<_data.Count)
        {
            _currentIndex = index;
            return true;
        }
        return false;
    }


    /// <summary>
    /// 血条交换位置
    /// </summary>
    private void Exchange()
    {
        var temp = _nextBar;
        _nextBar = _currentBar;
        _currentBar = temp;
        
    }

    private Vector3 GetOffset(Transform target)
    {
        Renderer renderer = target.GetComponent<Renderer>();
        if(renderer==null)
        {
            return Vector3.zero;
        }

       return renderer.bounds.max.y*Vector3.up;
    }

    private void SetBarData(int index,List<LifeBarData> data)
    {
        if(index<0||index>data.Count)
        {
            return;
        }

        _currentBar.SetData(data[index]);

        if(index+1>=data.Count)
        {
            _nextBar.SetData(new LifeBarData(null, Color.white));

        }
        else
        {
            _nextBar.SetData(data[index + 1]);
        }
    }
  

    public void Update()
    {
        if (_target == null)
        {
            return;
        }
        transform.position = Camera.main.WorldToScreenPoint(_target.position + _offset);
    }


    
    
}

public struct LifeBarData
{
    public Sprite BarSprite;
    public Color BarMainColor;

    public LifeBarData(Sprite sprite, Color color)
    {
        BarSprite = sprite;
        BarMainColor = color;
    }

}

子物体

using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using static LifeBar;

public class LifeBarItem : MonoBehaviour
{
    private RectTransform _rect;
    private RectTransform Rect
    {
        get
        {
            if(_rect==null)
            {
                _rect=GetComponent<RectTransform>();
            }
            return _rect;
        }
    }

    private Image _image;
    private Image Image
    {
        get
        {
            if(_image==null)
            {
                _image=GetComponent<Image>();
            }
            return _image;
        }
    }

    private LifeBarItem _child;

    private float defaultWidth;


    public void SetData(LifeBarData  data)
    {
        if(data.BarSprite!=null)
        {
            Image.sprite = data.BarSprite;
        }
        Image.color = data.BarMainColor;

        if (_child != null)
            _child.SetData(data);
    }

    public void Init()
    {
        defaultWidth = Rect.rect.width;
        if(transform.Find("AdditionBar")!=null)
        _child = transform.Find("AdditionBar").gameObject.AddComponent<LifeBarItem>();
    }

    public float ChangLife(float value)
    {
        Rect.sizeDelta += value * Vector2.right;
        if (_child!=null)
        {
            _child.DOKill();
            _child.Image.color = Image.color;
            _child.Rect.sizeDelta = Rect.sizeDelta;
            _child.Image.DOFade(0, 0.5f).OnComplete(()=>_child.ChangLife(value));
        }
       

        return GetOutOfRange();
    }

    private float GetOutOfRange()
    {
        float offset = 0;

        if(Rect.rect.width<0)
        {
            offset = Rect.rect.width;
            RestToZero();
        }
        else if(Rect.rect.width>defaultWidth)
        {
            offset = Rect.rect.width-defaultWidth;
            RestDefault();
        }
        return offset;
    }

    public void RestToZero()
    {
        Rect.sizeDelta = Vector2.zero;
    }

    public void RestDefault()
    {
        Rect.sizeDelta = Vector2.right * defaultWidth;
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

   

    // Update is called once per frame
    void Update()
    {
        
    }
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值