Unity UI 适配问题汇总

Unity UI 适配问题汇总

Canvas组件下的RenderMode选择

总共有三种模式

  • Overlay UI的渲染总是在画面的最上层,缺陷:不能渲染粒子特效
  • WorldSpace UI在空间总渲染
  • Camera 优势:可以渲染粒子

最终选择Camera模式。RenderCamera选择另建一个UICamera专门渲染UI。UICamera设置 ClearFlags选择DepthOnly,CullingMask选择只渲染UI层,这里注意既然UICamera已经渲染了UI了,主摄像机就不要再选择渲染UI层了。

UI Camera 渲染模式下渲染粒子特效

粒子特效layer要设为UI层,要不UICamera渲染不到。
粒子和其他UI的层级关系:

  • 粒子设置层级 particlesystem/Renderer/Order in Layer
  • UI的层级。如果UI元素都在同一个Canvas下,那么每个UI元素的层级都是这个canvas的层级。canvas的层级设置 Canvas/Order in Layer。
  • 因为粒子之间不能像UI之间一样以顺序排列渲染顺序,粒子之间的遮挡关系是靠order in layer的设置来实现的层级越高越后渲染。
  • 如果要实现粒子在两个UI的中间,即先渲染UI A,再渲染粒子,再渲染UI B。我们知道同一个canvas下,所有UI元素层级一样。但是可以给UI元素添加组件canvas,在新添加的canvas组件上修改order in layer这样就可以给单个元素设置新的层级,因为UI元素的父级已经有一个canvas了,所以新添加的canvas是继承自父级的。
UI Camera 渲染模式下的分辨率适配

CanvasScaler/UI Scale Mode UI缩放模式 选择Scale With Screen Size
CanvasScaler/Reference Resolution 参考分辨率 选择美术提供的参考分辨率
CanvasScaler/Match 根据实际手机的分辨率选择0或者1

public class CanvasManager : MonoBehaviour
{
    private void Awake()
    {
        if((float)Camera.main.pixelWidth / (float)Camera.main.pixelHeight >= 
            this.GetComponent().referenceResolution.x / this.GetComponent().referenceResolution.y)
        {
            this.GetComponent().matchWidthOrHeight = 0;
        }
        else
        {
            this.GetComponent().matchWidthOrHeight = 1;
        }
    }
}

这里主要是对UI的背景板做适配。要实现的效果是如果手机实际分辨率宽高比比参考分辨率高,即更宽,则以宽适配UI大小,这样UI背景板会在高度上切去一部分。如果手机实际分辨率宽高比比参考分辨率低,即更高,则以高适配UI大小,这样UI背景板会在宽度上切去一部分。即背景板始终会被切去一部分,而不会以黑边填充。

  • UI Camera 渲染模式下的背景板锚点选择,一定不要选择拉伸锚点,选择锚点聚在一块(也就是非拉伸模式)居中。这样才能和上面配合实现始终裁剪背景
  • 正常UI元素(即按钮等等)的锚点选择,注意选择适合的锚点,比如距离底部一定距离的按钮,锚点一定要定在底部,因为这里宽高适配是不固定的。如果一定是以高适配UI大小的话,距离底部一定距离的按钮,锚点也可以定在顶部,换算一下距离即可,因为这时UI高度是一定的。
UI Camera 渲染模式下屏幕坐标转换为anchorposition

有两种屏幕坐标。
第一种input.mouseposition或者pointerhandler的eventdata.position等的输入坐标。这些坐标都是根据手机的实际分辨率给出的和参考分辨率无关,比如参考分辨率2048 * 1024,实际分辨率 1024 * 1024,输入点在屏幕中间,则mouseposition为 (512, 512)。
第二种Camera.main.WorldToScreenPoint(vecter3 position),将空间中的物体位置转换为屏幕坐标。如果是main camera的话,这里返回的坐标点是和第一种一样的。即看到场景中的物体,在屏幕上的坐标位置就是相当于输入坐标的位置。比如场景正中央的物体转化为屏幕坐标时,实际分辨率1024 * 1024 参考分辨率为 2048 * 1024,则屏幕坐标为(512, 512)。什么情况下不一样呢,就是camera有rendertexture,并且rendertexture的分辨率不是手机的实际分辨率。经常是参考分辨率。这时返回的坐标是参考分辨率的坐标。比如场景正中央的物体转化为屏幕坐标时,实际分辨率1024 * 1024 参考分辨率为 2048 * 1024,相机上的rendertexture分辨率为2048 * 1024,则屏幕坐标为(1024, 512)。

将屏幕坐标转化为anchorposition代码如下:

 /// 
/// 必须是无拉伸的才可以调用这个方法,并且获取的是世界anchorPosition
/// 
/// 
/// 
public static Vector2 GetAnchorpositionByScreenPoint(Vector2 anchorMin, Vector2 anchorMax, Vector2 screenPoint, bool needScale = true)
{
    Vector2 point = Vector2.zero;
    float width = 0, height = 0;
    float widthScale = (float)GameManager.Instance.referrenceWidth / (float)Camera.main.pixelWidth;
    float heightScale = (float)GameManager.Instance.referrenceHeight / (float)Camera.main.pixelHeight;
    float screenPointScale = widthScale <= heightScale ? widthScale : heightScale;
    if (needScale)
    {
        screenPoint = screenPoint * screenPointScale;
    }
    if (widthScale <= heightScale)
    {
        width = GameManager.Instance.referrenceWidth;
        height = Camera.main.pixelHeight * widthScale;
    }
    else
    {
        width = Camera.main.pixelWidth * heightScale;
        height = GameManager.Instance.referrenceHeight;
    }
    //
    if (anchorMin == Vector2.zero && anchorMax == Vector2.zero)
    {
        point = screenPoint;
    }
    if (anchorMin == Vector2.right * 0.5f && anchorMax == Vector2.right * 0.5f)
    {
        point = screenPoint - Vector2.right * width / 2;
    }
    if (anchorMin == Vector2.right && anchorMax == Vector2.right)
    {
        point = screenPoint - Vector2.right * width;
    }
    if (anchorMin == Vector2.up * 0.5f + Vector2.zero && anchorMax == Vector2.up * 0.5f + Vector2.zero)
    {
        point = screenPoint - Vector2.up * height / 2;
    }
    if (anchorMin == Vector2.up * 0.5f + Vector2.right * 0.5f && anchorMax == Vector2.up * 0.5f + Vector2.right * 0.5f)
    {
        point = screenPoint - Vector2.right * width / 2 - Vector2.up * height / 2;
    }
    if (anchorMin == Vector2.up * 0.5f + Vector2.right && anchorMax == Vector2.up * 0.5f + Vector2.right)
    {
        point = screenPoint - Vector2.right * width - Vector2.up * height / 2;
    }
    if (anchorMin == Vector2.up + Vector2.zero && anchorMax == Vector2.up + Vector2.zero)
    {
        point = screenPoint - Vector2.up * height;
    }
    if (anchorMin == Vector2.up + Vector2.right * 0.5f && anchorMax == Vector2.up + Vector2.right * 0.5f)
    {
        point = screenPoint - Vector2.right * width / 2 - Vector2.up * height;
    }
    if (anchorMin == Vector2.up + Vector2.right && anchorMax == Vector2.up + Vector2.right)
    {
        point = screenPoint - Vector2.right * width - Vector2.up * height;
    }
    return point;
}

这个函数可以将屏幕坐标转化为anchorposition,也就是在UI缩放适配的情况下,可以让UI元素比如button跟着鼠标走。前两个参数是recttransform 下的 anchorMax anchorMin,后者是输入坐标,最后一个参数是是否需要对输入的坐标缩放,默认是需要。除非你用相机的函数WorldToScreenPoint获取的屏幕坐标,并且这个相机还得设置了rendertexture,并且rendertexture的分辨率不是手机实际的分辨率。也就是说如果相机带有rendertexture慎用这个函数。

代码动态设置UI的锚点

代码如下:

using System;
using UnityEngine;

public enum AnchorPresets
{
    TopLeft,
    TopCenter,
    TopRight,

    MiddleLeft,
    MiddleCenter,
    MiddleRight,

    BottomLeft,
    BottonCenter,
    BottomRight,
    BottomStretch,

    VertStretchLeft,
    VertStretchRight,
    VertStretchCenter,

    HorStretchTop,
    HorStretchMiddle,
    HorStretchBottom,

    StretchAll
}
public enum NoStrechAnchorPresets
{
    TopLeft,
    TopCenter,
    TopRight,

    MiddleLeft,
    MiddleCenter,
    MiddleRight,

    BottomLeft,
    BottonCenter,
    BottomRight,
    BottomStretch,
}
public enum StrechAnchorPresets
{
    VertStretchLeft,
    VertStretchRight,
    VertStretchCenter,

    HorStretchTop,
    HorStretchMiddle,
    HorStretchBottom,

    StretchAll
}

public enum PivotPresets
{
    TopLeft,
    TopCenter,
    TopRight,

    MiddleLeft,
    MiddleCenter,
    MiddleRight,

    BottomLeft,
    BottomCenter,
    BottomRight,
}

public static class RectTransformExtensions
{      
    public static void SetStrechAnchor(this RectTransform source, PivotPresets pivot, StrechAnchorPresets allign, Vector2 anchorPosition, Vector2 sizeDelta,Vector2 leftbottom, Vector2 rightup)
    {
        SetPivot(source,pivot);
        string sna = Enum.GetName(typeof(StrechAnchorPresets), allign);
        SetAnchor(source,(AnchorPresets)Enum.Parse(typeof(AnchorPresets),sna), anchorPosition.x, anchorPosition.y);

        switch (allign)
        {
            case StrechAnchorPresets.VertStretchLeft:
                source.offsetMin = Vector2.up * leftbottom.y;
                source.offsetMax = Vector2.up * -rightup.y;
                source.sizeDelta = new Vector2(sizeDelta.x, -leftbottom.y - rightup.y);
                break;
            case StrechAnchorPresets.VertStretchCenter:
                source.offsetMin = Vector2.up * leftbottom.y;
                source.offsetMax = Vector2.up * -rightup.y;
                source.sizeDelta = new Vector2(sizeDelta.x, -leftbottom.y - rightup.y);
                break;
            case StrechAnchorPresets.VertStretchRight:
                source.offsetMin = Vector2.up * leftbottom.y;
                source.offsetMax = Vector2.up * -rightup.y;
                source.sizeDelta = new Vector2(sizeDelta.x, -leftbottom.y - rightup.y);
                break;
            case StrechAnchorPresets.HorStretchBottom:
                source.offsetMin = Vector2.right * leftbottom.x;
                source.offsetMax = Vector2.right * -rightup.x;
                source.sizeDelta = new Vector2(-leftbottom.x - rightup.x, sizeDelta.y);
                break;
            case StrechAnchorPresets.HorStretchMiddle:
                source.offsetMin = Vector2.right * leftbottom.x;
                source.offsetMax = Vector2.right * -rightup.x;
                source.sizeDelta = new Vector2(-leftbottom.x - rightup.x, sizeDelta.y);
                break;
            case StrechAnchorPresets.HorStretchTop:
                source.offsetMin = Vector2.right * leftbottom.x;
                source.offsetMax = Vector2.right * -rightup.x;
                source.sizeDelta = new Vector2(-leftbottom.x - rightup.x, sizeDelta.y);
                break;
            case StrechAnchorPresets.StretchAll:
                source.offsetMin = leftbottom;
                source.offsetMax = -rightup;
                break;
        }
    }
    public static void SetNoStrechAnchor(this RectTransform source, PivotPresets pivot,NoStrechAnchorPresets allign, Vector2 anchorPosition, Vector2 sizeDelta)
    {
        SetPivot(source, pivot);
        string sna = Enum.GetName(typeof(StrechAnchorPresets), allign);
        SetAnchor(source, (AnchorPresets)Enum.Parse(typeof(AnchorPresets), sna), anchorPosition.x, anchorPosition.y);
        source.sizeDelta = sizeDelta;
    }
    private static void SetAnchor(this RectTransform source, AnchorPresets allign, float offsetX = 0, float offsetY = 0)
    {
        source.anchoredPosition = new Vector3(offsetX, offsetY, 0);

        switch (allign)
        {
            case (AnchorPresets.TopLeft):
            {
                source.anchorMin = new Vector2(0, 1);
                source.anchorMax = new Vector2(0, 1);
                break;
            }
            case (AnchorPresets.TopCenter):
            {
                source.anchorMin = new Vector2(0.5f, 1);
                source.anchorMax = new Vector2(0.5f, 1);
                break;
            }
            case (AnchorPresets.TopRight):
            {
                source.anchorMin = new Vector2(1, 1);
                source.anchorMax = new Vector2(1, 1);
                break;
            }

            case (AnchorPresets.MiddleLeft):
            {
                source.anchorMin = new Vector2(0, 0.5f);
                source.anchorMax = new Vector2(0, 0.5f);
                break;
            }
            case (AnchorPresets.MiddleCenter):
            {
                source.anchorMin = new Vector2(0.5f, 0.5f);
                source.anchorMax = new Vector2(0.5f, 0.5f);
                break;
            }
            case (AnchorPresets.MiddleRight):
            {
                source.anchorMin = new Vector2(1, 0.5f);
                source.anchorMax = new Vector2(1, 0.5f);
                break;
            }

            case (AnchorPresets.BottomLeft):
            {
                source.anchorMin = new Vector2(0, 0);
                source.anchorMax = new Vector2(0, 0);
                break;
            }
            case (AnchorPresets.BottonCenter):
            {
                source.anchorMin = new Vector2(0.5f, 0);
                source.anchorMax = new Vector2(0.5f, 0);
                break;
            }
            case (AnchorPresets.BottomRight):
            {
                source.anchorMin = new Vector2(1, 0);
                source.anchorMax = new Vector2(1, 0);
                break;
            }

            case (AnchorPresets.HorStretchTop):
            {
                source.anchorMin = new Vector2(0, 1);
                source.anchorMax = new Vector2(1, 1);
                break;
            }
            case (AnchorPresets.HorStretchMiddle):
            {
                source.anchorMin = new Vector2(0, 0.5f);
                source.anchorMax = new Vector2(1, 0.5f);
                break;
            }
            case (AnchorPresets.HorStretchBottom):
            {
                source.anchorMin = new Vector2(0, 0);
                source.anchorMax = new Vector2(1, 0);
                break;
            }

            case (AnchorPresets.VertStretchLeft):
            {
                source.anchorMin = new Vector2(0, 0);
                source.anchorMax = new Vector2(0, 1);
                break;
            }
            case (AnchorPresets.VertStretchCenter):
            {
                source.anchorMin = new Vector2(0.5f, 0);
                source.anchorMax = new Vector2(0.5f, 1);
                break;
            }
            case (AnchorPresets.VertStretchRight):
            {
                source.anchorMin = new Vector2(1, 0);
                source.anchorMax = new Vector2(1, 1);
                break;
            }

            case (AnchorPresets.StretchAll):
            {
                source.anchorMin = new Vector2(0, 0);
                source.anchorMax = new Vector2(1, 1);
                break;
            }
        }
    }

    private static void SetPivot(this RectTransform source, PivotPresets preset)
    {

        switch (preset)
        {
            case (PivotPresets.TopLeft):
            {
                source.pivot = new Vector2(0, 1);
                break;
            }
            case (PivotPresets.TopCenter):
            {
                source.pivot = new Vector2(0.5f, 1);
                break;
            }
            case (PivotPresets.TopRight):
            {
                source.pivot = new Vector2(1, 1);
                break;
            }

            case (PivotPresets.MiddleLeft):
            {
                source.pivot = new Vector2(0, 0.5f);
                break;
            }
            case (PivotPresets.MiddleCenter):
            {
                source.pivot = new Vector2(0.5f, 0.5f);
                break;
            }
            case (PivotPresets.MiddleRight):
            {
                source.pivot = new Vector2(1, 0.5f);
                break;
            }

            case (PivotPresets.BottomLeft):
            {
                source.pivot = new Vector2(0, 0);
                break;
            }
            case (PivotPresets.BottomCenter):
            {
                source.pivot = new Vector2(0.5f, 0);
                break;
            }
            case (PivotPresets.BottomRight):
            {
                source.pivot = new Vector2(1, 0);
                break;
            }
        }
    }
}

总共两个函数一个设置有拉伸(四个锚点没有聚成一个花)的锚点信息: SetStrechAnchor,一个设置无拉伸(四个锚点聚在一块)的锚点信息:SetNoStrechAchor。
pivot, anchors, anchorPosition, sizeDelta,left_buttom_right_up,五个值需要一块填入。
pivot元素的中心点,总共6种。
StrechAnchorPresets 总共有7种拉伸模式,在inspector界面可以看到
anchors在inspector界面可以看到就在pivot设置的上面 有min 和max两个vector2数值。
anchorPosition x, y 两个数值inspector界面有显示就填入数值没显示就填0
sizeDelta x,y 两个数值inspector界面有显示就填入数值没显示就填0
leftbottom x,y 两个数值inspector界面有显示就填入数值没显示就填0 x代表left y代表bottom
rightup x,y 两个数值inspector界面有显示就填入数值没显示就填0 right y代表up

你可能感兴趣的:(unity,unity3d,锚点)