UGUI实现长按显示道具详细信息(类背包道具信息显示)

    游戏中很普遍的一个功能,长按道具,显示道具的信息。类似下面这样的一个功能:

    分析一下这个功能需要的知识点:

        1.文本框的长宽根据文本内容自适应;

        2.点击位置转换成实际UI位置(弹窗位置由点击位置决定而不是点击对象的位置决定,后者更简单)

        3.若道具在屏幕边界,则需要动态修改弹窗的位置以及轴点。

    先说一下画布设置,博主在canvas下新加了UIcamera,相机只渲染UI,方式为Orthographic。画布渲染方式为根据ui相机渲染,ui缩放方式为根据屏幕分辨率,画布尺寸为1080*1920,如图

    第一个问题:弹窗由背景父物体image以及内容子物体text组成。首先背景大小是不定的,所以背景image的填充方式应该为sliced,则需要对图片进行切割,切割图片只需要在图片的inspector视图中点击sprite editor按钮进行切割,切割成拉伸时不影响边框即可。然后在父物体中添加组件Vertical Layout Group和Content Size Fitter,其中Content Size Fitter要设置为preferred。Vertical Layout Group根据自身需要设置即可,博主设置如下。子物体不需要添加额外的组件,之后就可以自适应了。记做预设UITip,供下文使用。这里注意的是如果要改子物体text的文本对齐方式,需要改父物体的轴点。

    第二个问题:博主最初的做法是讲鼠标坐标转换为屏幕坐标,然后乘以画布缩放值得到ui坐标,但是这样算的换个分辨率就不对了,不清楚为什么,这一块也不是很熟悉,找不到问题,只能换方法,直接用一个api解决了,博主依旧贴出来原来的算法,希望知道问题的能指点一下

        Vector3 pos = Input.mousePosition;
        Debug.Log("mousePos:"+pos);
        Vector2 pos2d = RectTransformUtility.WorldToScreenPoint(uiCamera, pos);
        Vector2 pos2d_1 = pos2d * canvas.transform.localScale.x;
        x = pos2d_1.x - Screen.width / 2;
        y = pos2d_1.y - Screen.height / 2;
        Debug.Log(x+"----"+y);

其中pos2d_1为博主算的坐标,在1080*1920可以,换了其他分辨率就不行了,而且差距非常大。之后博主更换其他方式,一个api直接解决

        Vector2 pos2D_01;
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Input.mousePosition, uiCamera, out pos2D_01))
        {
            Debug.Log(pos2D_01);
        }

其实最初发现了这个方法,但是api写错了...后来检查才发现的,很尴尬。pos2D_01坐标即为当前鼠标点击的位置转成的UI坐标,此时只需要将上面的预设UITip 的相对坐标(localposition)设置为pos2D_01即可。

    第三个问题:首先用博主自己的语言解释下轴点pivot,轴点即为物理中心点,recttransform的坐标以及旋转都是根据轴点决定的,对象的坐标即轴点的坐标。假设一个正方形,则左下为(0,0),右上为(1,1)。设置此正方形轴点为(0,1),即以左上为轴点,此时设置正方形坐标为(100,100),则左上点坐标为(100,100)。 博主默认轴点设置为(0,1),也就是UITip是由左向右铺开,即不可能超过左边框(边框指画布边框),判断是否超过右边框只需要判断UITip的x坐标与UITip的width是否大于画布width的1/2(这里建议读者画一个xy坐标系,画出来就很好理解,但是文字表达是真的麻烦,就省过了)。大于则表示超过。假设右边框超过了,则设置UITip为从右向左铺开,即设置轴心的x值为1。这样的话UITip的最右边即是鼠标点击的地方,是不可能超框的。如图


x方向解决了,y方向同理。上面说的还有一个变量是未知的,即UITip的width。有人说直接recttransform.width。博主试了,这样是不行的,因为UITip的width是由text控制的,将text赋值后,text的width是直接生效的,然后才会影响UITip的width,这中间存在一个时间延迟,也就是说,运行这句代码Text.text =“这是个例子”之后,直接获取text的width是没问题的,但是直接获取UITip的width并不正确,但是延时再获取UITip的width就没问题,博主亲测。所以博主说这里有个延时,没办法,这里只好自己算了。上面Vertical Layout Group组件的属性padding为子物体与父物体的各个方向的边距,则父物体的width只需要text的width加上text与父物体的左右边距即可,height同理。之后与画布的长宽相比即可算出是否超框。代码如下

        x = pos2D_01.x;
        y = pos2D_01.y;
        //当到达边界时候的处理,根据不同的情况设置不同的轴点
        //先处理左右边界,默认轴点为(0,1),左边界可以不用处理,上边界不用处理
        Debug.Log(Screen.width + "**"+Screen.height);
        if (x + width > (1080/2) )//右边界,设置轴点x为1
        {
            Debug.Log("超右边界");
            PivotX = 1;
        }
        if (Mathf.Abs(-y + height) > (1920/2))//下边界,设置轴点y为0
        {
            Debug.Log("超下边界");
            PivotY = 0;
        }

注意这里为什么会跟1080/2 和1920/2比较,这是因为画布的尺寸设置为如此,而画布的尺寸是不随分辨率改变而改变的,画布只是会根据不同的分辨率进行相应的缩放显示,但是实际尺寸还是最初设定的。这是博主的理解,不清楚是否正确,但是博士实验结果是这样的。有不对的欢迎指正,谢谢。

    整个过程就是这个样子,博主粘贴一下整个代码

public class UITip : MonoBehaviour
{

    public Text msgTxt;
    public Canvas canvas;
    public Camera uiCamera;
    private RectTransform recTran;
    private VerticalLayoutGroup group;
    private float width;//物体宽度
    private float height;//物体长度
    private float x;//x坐标
    private float y;//y坐标
    private int PivotX;//轴点x坐标
    private int PivotY;//轴点y坐标

    void Start()
    {
        recTran = transform as RectTransform;
        group = transform.GetComponent<VerticalLayoutGroup>();
    }

    public void Show(string msg)
    {
        PivotX = 0;
        PivotY = 1;
        msgTxt.text = msg;
        //设置弹窗最外面渲染
        transform.SetAsLastSibling();
        //计算recttransform的width与height,根据子物体文本长度和vertical layout group 的padding计算,直接获取的话获取的不对,延时获取才是对的,但子物体文本的直接获取是对的

        width = msgTxt.preferredWidth + group.padding.left + group.padding.right;
        height = msgTxt.preferredHeight + group.padding.top + group.padding.bottom;
        Debug.Log(width + "---" + height);
        #region
        Vector2 pos2D_01;
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Input.mousePosition, uiCamera, out pos2D_01))
        {
            Debug.Log(pos2D_01);
        }
        x = pos2D_01.x;
        y = pos2D_01.y;
        //当到达边界时候的处理,根据不同的情况设置不同的轴点
        //先处理左右边界,默认轴点为(0,1),左边界可以不用处理,上边界不用处理
        Debug.Log(Screen.width + "**"+Screen.height);
        if (x + width > (1080/2) )//右边界,设置轴点x为1
        {
            Debug.Log("超右边界");
            PivotX = 1;
        }
        if (Mathf.Abs(-y + height) > (1920/2))//下边界,设置轴点y为0
        {
            Debug.Log("超下边界");
            PivotY = 0;
        }

        recTran.pivot = new Vector2(PivotX, PivotY);
        transform.localPosition = pos2D_01;
        return;
        #endregion
    }
}
    总感觉上面有些地方的解释不正确,只是自己看到的,所以希望发现问题的不吝赐教,在此谢过。
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UGUIUnity中的用户界面系统,常用于制作游戏中的UI界面。如果要实现可以翻页和输入页面的显示信息栏,可以参考以下步骤: 1. 创建一个ScrollView对象,用于实现翻页功能。在Hierarchy视图中,右键点击Canvas对象,选择UI -> Scroll View,创建一个ScrollView对象。 2. 在ScrollView对象中创建一个Content对象,用于存放需要显示信息。在ScrollView对象中,选择Content子对象,将它的RectTransform组件的锚点设置为左上角(0,1),并将它的宽度和高度分别设置为ScrollView对象的宽度和高度。 3. 在Content对象中创建多个子对象,用于显示不同的信息。可以使用Text或Image等组件来显示信息。在每个子对象的RectTransform组件中,将锚点设置为左上角(0,1),并将每个子对象的位置和大小调整好。 4. 在ScrollView对象中添加Scrollbar组件,用于实现滚动条功能。在ScrollView对象中,选择Scrollbar子对象,调整它的位置和大小,使它与ScrollView对象的右侧对齐。然后将Scrollbar的方向设置为Vertical(垂直方向)。 5. 编写代码来控制信息显示和翻页功能。可以通过代码来动态地创建子对象,向子对象中添加Text或Image组件,并设置它们的内容和位置等属性。同时,也可以通过代码来控制翻页功能,使得用户可以通过滚动条或按钮来切换不同的信息页面。 以下是一个简单的示例代码,用于实现上述功能: ```csharp using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class InfoPanel : MonoBehaviour { public int numPages = 3; // 总共有多少页 public float pageHeight = 100f; // 每页的高度 public GameObject contentPrefab; // 用于创建子对象的预制体 public ScrollRect scrollRect; // ScrollView组件 public Button prevButton; // 上一页按钮 public Button nextButton; // 下一页按钮 private List<GameObject> pages = new List<GameObject>(); // 存储所有的信息页 private int currentPage = 0; // 当前显示信息页 void Start() { // 创建所有的信息页 for (int i = 0; i < numPages; i++) { GameObject page = Instantiate(contentPrefab, scrollRect.content); page.transform.localPosition = new Vector3(0, -i * pageHeight, 0); pages.Add(page); } // 隐藏不需要显示信息页 for (int i = numPages; i < pages.Count; i++) { pages[i].SetActive(false); } // 绑定上一页和下一页按钮的事件 prevButton.onClick.AddListener(() => { ShowPage(currentPage - 1); }); nextButton.onClick.AddListener(() => { ShowPage(currentPage + 1); }); // 显示第一页 ShowPage(0); } // 显示指定的信息页 void ShowPage(int index) { index = Mathf.Clamp(index, 0, numPages - 1); currentPage = index; // 更新滚动条的位置 float posY = index * pageHeight / scrollRect.content.rect.height; scrollRect.verticalNormalizedPosition = 1 - posY; // 显示需要显示信息页 for (int i = 0; i < numPages; i++) { if (i == index) { pages[i].SetActive(true); } else { pages[i].SetActive(false); } } // 更新上一页和下一页按钮的状态 prevButton.interactable = (index > 0); nextButton.interactable = (index < numPages - 1); } } ``` 在上述代码中,我们首先在Start函数中创建了所有的信息页,然后将它们添加到ScrollView对象的Content子对象中。接着,我们绑定了上一页和下一页按钮的事件,并在ShowPage函数中实现了翻页功能。具体来说,当用户点击上一页或下一页按钮时,我们会调用ShowPage函数来显示对应的信息页。在ShowPage函数中,我们使用了scrollRect.verticalNormalizedPosition属性来控制滚动条的位置,使用SetActive函数来控制哪些信息页需要显示。最后,我们也更新了上一页和下一页按钮的状态,使得用户无法继续翻页或返回到第一页。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值