第44天UGUI(四)Dropdown、Canvas Group、UGUI的事件接口、RectTransform坐标、右键点击,RankPanel

Dropdown(下拉菜单)

核心

显示当前选项的文本框

下拉列表的模板(Template)

下拉列表中选项的模板(Item),Item上必须有Toggle组件

运行原理

当点击下拉菜单时,通过Template创建一个下拉列表

通过选项的模板(Item)创建每一个选项

属性

Template

下拉列表的模板

Caption Text

指定当前选项的文本框

Caption Image

指定当前选项的图片

Item Text

下拉列表中选项的模板(Item)中的文本框

Item Image

下拉列表中选项的模板(Item)中的图片

解释:选择哪一个Item,就将该Item中文本框的内容显示在Caption Text,将Item中的图片显示Caption Image

Options

下拉选项(文字和图片)

Value

当前选择是Options第几个选项(索引)

OnValueChanged

当索引Value值发生改变时触发OnValueChanged中的方法

代码添加的方式只能添加无返回值,有一个Int类型参数的方法

Canvas Group

控制当前游戏物体下的所有子物体的相关属性

属性

Alpha:透明度

Interactable:控制当前物体下所有子物体是否可以交互

Blocks Raycast:控制当前物体所有的子物体是否可以接受射线检测(UGUI事件系统的射线)

Ignore Parent Groups:是否忽略父物体中Canvas Group的影响,勾选是忽略,不能再Canvas里测试,要在子物体中才有效

UGUI的事件接口

命名空间

UnityEngine.EventSystems

接口

IPointerDownHandler

当鼠标在当前游戏物体的矩形框或其子物体的矩形框中按下时,触发该接口的方法

IPointerUpHandler

当鼠标在当前游戏物体的矩形框或其子物体的矩形框中抬起时,触发该接口的方法

IPointerEnterHandler

当鼠标进入到当前游戏物体或其子物体的矩形框时,触发该接口的方法

IPointerExitHandler

当鼠标移出到当前游戏物体或其子物体的矩形框时,触发该接口的方法

IPointerClickHandler

当鼠标点击当前游戏物体或其子物体时,触发该接口的方法

点击:按下跟抬起必须都在矩形框中才是点击,可以在父物体按下,子物体抬起

IDragHandler

拖动时(鼠标在当前游戏物体的矩形框按下后移动鼠标表示拖动)触发该方法

只要拖动就反复执行

IBeginDragHandler

开始拖动的一瞬间执行一次

IEndDragHandler

结束拖动的一瞬间执行一次

鼠标之前处于拖动情况,抬起的瞬间表示拖动结束

PinterEventData

button

判断左键,中键,右键

clickCount

点击次数

可以用于双击效果

enterEventCamera

进入矩形框时的事件相机

pressEventCamera

按下矩形框时的事件相机

pointerCurrentRaycast

鼠标当前射线检测的结果

gameObject:当前射线检测的游戏物体

pointerPressRaycast

鼠标按下时射线检测的结果

gameObject:鼠标按下时检测的游戏物体

position

当前鼠标在屏幕中的坐标

pressPosition

当前鼠标按下时在屏幕中的坐标

RectTransform坐标

position

世界坐标

当前游戏物体的轴心点相对于世界原点的坐标

localPosition

相对于父物体的坐标

当前游戏物体的轴心点相对于父物体的轴心点的坐标

anchoredPosition

锚点坐标

当前游戏物体的轴心点相对于锚点的坐标

右键点击事件

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

public class RightButton : Selectable, IPointerClickHandler
{

    //当点击按钮时,触发onClick中存储的方法
    public ButtonClickedEvent onClick;

    public void OnPointerClick(PointerEventData eventData)
    {
        //当鼠标右键点击时,触发onClick的方法
        if (eventData.button == PointerEventData.InputButton.Right)
        {
            onClick.Invoke();
        }
    }

    [System.Serializable]//特性,修饰的是类ButtonClickedEvent 
    //表示该ButtonClickedEvent类对象是可以序列化的
    public class ButtonClickedEvent : UnityEvent
    {
        public ButtonClickedEvent() { }
    }
}

 PlayView.cs

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

public class PlayerView : MonoBehaviour
{
    public int rank;

    public Text rankText;
    public Text nameText;
    public Text levelText;
    public Text scoreText;

    public Image rankImage;

    /// <summary>
    /// 设置数据,修改显示内容
    /// </summary>
    public void SetPlayerData(int rank, RankModel model)
    {
        this.rank = rank;

        nameText.text = model.name;
        levelText.text = "LV " + model.level;

        scoreText.text = string.Format("{0:N0}", model.maxScore);//model.maxScore.ToString();

        SpriteAtlas atlas = Resources.Load<SpriteAtlas>("UITexture/Ranking");
        //排名显示
        //1,2,3名使用不同的图片, 显示的文本框隐藏
        rank++;
        if (rank >= 1 && rank <= 3)
        {
            //从图集中加载精灵图
            string spriteName = "icon_rank_" + rank;
            Sprite sprite = atlas.GetSprite(spriteName);
            rankImage.sprite = sprite;
            rankText.gameObject.SetActive(false);
        }
        else
        {
            rankText.gameObject.SetActive(true);
            rankText.text = rank.ToString();
            rankImage.sprite = atlas.GetSprite("icon_rank_default");
        }
    }

    
}

RankPanel

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

public class RankPanel : MonoBehaviour
{

    //测试
    public int rankCount = 10;

    public int contentMaxChild = 6;

    public int playerTemplateHeight = 195;

    List<RankModel> ranks = new List<RankModel>();

    private GameObject playerTemplate;
    private RectTransform cantent;

    //存储第一个子物体与最后一个子物体的 
    private PlayerView firstChild;
    private PlayerView lastChild;

    private void Awake()
    {
        InitTempData();

        playerTemplate = transform.Find("Window/PlayerTemplate").gameObject;
        RectTransform ptrt = playerTemplate.transform as RectTransform;
        //先修改模板的高度
        ptrt.sizeDelta = new Vector2(ptrt.sizeDelta.x, playerTemplateHeight);
        cantent = transform.Find("Window/PlayerRect/Viewport/Content") as RectTransform;
        //修改Content的高度, 高度 = 数据个数 * playerTemplateHeight
        cantent.sizeDelta = new Vector2(cantent.sizeDelta.x, playerTemplateHeight * ranks.Count);
    }

    private void Start()
    {
        InitPlayerView();
    }

    private void Update()
    {
        CheckChildSwitch();
    }

    /// <summary>
    /// 初始化临时数据
    /// </summary>
    void InitTempData()
    {
        //自动生成10条数据
        for (int i = 0; i < rankCount; i++)
        {
            RankModel rm = new RankModel();

            rm.name = "";
            for (int j = 0; j < Random.Range(5, 21); j++)
            {
                rm.name += (char)Random.Range('A', 'Z');
            }

            rm.level = Random.Range(1, 100);
            rm.maxScore = Random.Range(0, 10000000);

            ranks.Add(rm);
        }

        //List的排序
        //排序是根据分数进行排序
        //Sort(Comparison<T> comparison)
        //public delegate int Comparison<T>(T x, T y);
        //Sort方法的参数是一个委托类型, 证明可以将一个方法作为参数传递进去
        //但是方法必须与 Comparison 委托类型返回值与参数保持一致
        // Comparison  返回值是int, 参数有两个 分别是 T 类型, T是泛型

        /*
         public class List<T>
         {
            Sort(Comparison<T> comparison)
         }

         List<RankModel> ranks = new List<RankModel>();
         * */

        //对于ranks来说 T是RankModel
        //那么Comparison<T> T就是RankModel
        //总结,该Sort方法中参数可以传入一个  有int返回值, 两个参数,并且两个参数是RankModel类型

        ranks.Sort(
            (RankModel m1, RankModel m2) =>
            {
                //判断分数
                if (m1.maxScore > m2.maxScore)
                {
                    return -1;
                }
                else if (m1.maxScore < m2.maxScore)
                {
                    return 1;
                }
                return 0;
            }
        );
    }


    /// <summary>
    /// 初始化生成每一个玩家
    /// </summary>
    void InitPlayerView()
    {
        int childCount = 0;
        if (ranks.Count < contentMaxChild)
        {
            //当排行榜的数据小于6个时,就不需要生成6个了
            childCount = ranks.Count;
        }
        else
        {
            //最多生成6个
            childCount = contentMaxChild;
        }

        for (int i = 0; i < childCount; i++)
        {
            GameObject player = Instantiate(playerTemplate, cantent);
            player.SetActive(true);
            player.name = "P_" + i.ToString();

            //修改RectTransform的Left和Right为0
            RectTransform rt = player.transform as RectTransform;
            //offsetMax    -x = right  -y = top
            //offsetMin     x = left    y = bottom
            rt.offsetMax = new Vector2(0, rt.offsetMax.y);
            rt.offsetMin = new Vector2(0, rt.offsetMin.y);

            //修改每一个的ancharedPosition的 Y
            //第0个就是0
            //第1个就是一个的高度
            rt.anchoredPosition = new Vector2(rt.anchoredPosition.x, -playerTemplateHeight * i);

            //告诉当前玩家显示区的数据是什么,排序是多少
            player.GetComponent<PlayerView>().SetPlayerData(i, ranks[i]);
        }

        if (cantent.childCount > 0)
        {
            firstChild = cantent.GetChild(0).GetComponent<PlayerView>();
            lastChild = cantent.GetChild(cantent.childCount - 1).GetComponent<PlayerView>();
        }
    }


    /// <summary>
    /// 检测子物体是否需要交换
    /// </summary>
    void CheckChildSwitch()
    {
        //当前数据数量超出 6条时才需要判断是否交换
        if (ranks.Count < contentMaxChild)
        {
            return;
        }

        //通过Content的Y坐标判断首个子物体是否需要移动到最后
        //Content.Y = 195 * 0   显示的Rank  0 - 5
        //Content.Y = 195 * 1   显示的Rank  1 - 6
        //Content.Y = 195 * 2   显示的Rank  2 - 7

        //通过Content.Y能计算出 当前第一个子物体应该显示第几个人
        //  Content.Y / 195 = 第一个子物体的Rank
        // 195 就是 playerTemplateHeight

        //总结: 当前第一个子物体的Rank = 1   ,  通过Content.Y计算出的第一个子物体的Rank = 2
        //则 当前第一个子物体应该移动最后一个

        int showFirstRank = (int)cantent.anchoredPosition.y / playerTemplateHeight;
        showFirstRank = Mathf.Clamp(showFirstRank, 0, ranks.Count - contentMaxChild);

        //Debug.Log("当前第一个应该显示:" + showFirstRank);

        //当前子物体 实际显示
        //P_1, P_2, P_3, P_4, P_5, P_6

        //通过坐标计算  应该显示的第一个的showFirstRank = 0,  最后一个lastRank = 5
        //应该显示 P_0   ~   P_5
        //该种情况,  就是最后一个需要移动到第一位置


        //通过坐标计算  应该显示的第一个的showFirstRank = 2,  最后一个lastRank = 7
        //应该显示 P_2   ~   P_7
        //该种情况,  就是第一个需要移动到最后一个位置

        //如果第一个子物体的Rank不是要显示的showRank, 就要交换
        if (firstChild.rank < showFirstRank)
        {
            SwitchFirstToLast();
        }
        else if (firstChild.rank > showFirstRank)
        {
            //最后一个移动到第一个
            SwitchLastToFirst();
        }


    }

    /// <summary>
    /// 交换第一个子物体到最后
    /// </summary>
    void SwitchFirstToLast()
    {
        //当最后一个索引已经是最后一条数据了,那么就不需要移动到最后了
        if (lastChild.rank == ranks.Count - 1)
        {
            return;
        }

        //第一个物体变成实际第二个物体
        firstChild = cantent.GetChild(1).GetComponent<PlayerView>();
        //第一个物体要移动到最后
        //设置子物体的索引到最后一个
        Transform temp = cantent.GetChild(0);
        temp.transform.SetAsLastSibling();
        //计算最后一个的索引值, 根据之前最后一个子物体的Rank
        int lastIndex = lastChild.rank + 1;

        temp.gameObject.name = "P_" + lastIndex;
        temp.GetComponent<PlayerView>().SetPlayerData(lastIndex, ranks[lastIndex]);
        RectTransform rt = temp as RectTransform;
        rt.anchoredPosition = new Vector2(rt.anchoredPosition.x, -playerTemplateHeight * lastIndex);

        lastChild = temp.GetComponent<PlayerView>();
    }

    /// <summary>
    /// 交换最后一个到第一个
    /// </summary>
    void SwitchLastToFirst()
    {
        //如果当前第一个已经是第一名了,就不需要交换了
        if (firstChild.rank == 0)
        {
            return;
        }

        //最后一个指向需要变为倒数第二个
        lastChild = cantent.GetChild(cantent.childCount - 2).GetComponent<PlayerView>();

        Transform temp = cantent.GetChild(cantent.childCount - 1);
        //将最后一个物体移动到第一个位置
        temp.SetAsFirstSibling();

        //计算当前第一个应该显示的rank值
        int firstIndex = firstChild.rank - 1;

        temp.gameObject.name = "P_" + firstIndex;
        temp.GetComponent<PlayerView>().SetPlayerData(firstIndex, ranks[firstIndex]);
        RectTransform rt = temp as RectTransform;
        rt.anchoredPosition = new Vector2(rt.anchoredPosition.x, -playerTemplateHeight * firstIndex);

        firstChild = temp.GetComponent<PlayerView>();
    }

}

 RankModel

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

public class RankModel
{
    public string name;
    public int level;
    public int maxScore;

    public RankModel()
    { }

    public RankModel(string name, int level, int maxScore)
    {
        this.name = name;
        this.level = level;
        this.maxScore = maxScore;
    }


    public override string ToString()
    {
        return string.Format("name: {0}, level: {1}, maxScore: {2}", name, level, maxScore);
    }

}
原文地址:https://www.cnblogs.com/yifengs/p/14279126.html