读一读

库:FlameskyDexive/FastBugly: 快速接入新版Bugly到unity,支持unity2021 (github.com)


启动时,初始化Bugly就可以了

public static void Boot()
{
    //BUGLY初始化
#if UNITY_IPHONE || UNITY_IOS
    BuglyAgent.InitWithAppId("xxxxxxxx");
#elif UNITY_ANDROID
    BuglyAgent.InitWithAppId("xxxxxxxx");
#endif
    if (FrameConfig.Debug)
    {
        BuglyAgent.ConfigDebugMode(true);
    }
    BuglyAgent.EnableExceptionHandler();
}



带CanvasUI是不在剪裁的目标之内的,所以需要一个中介自身是剪裁目标的,然后获取到这些带Canvas的UI通知它们也做相应处理。

image.png


Proxy的脚本为:

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

[DisallowMultipleComponent]
public class RectMask2DClipProxy : MaskableGraphic
{
    private List<MaskableGraphic> _maskableList;

    protected RectMask2DClipProxy()
    {
        useLegacyMeshGeneration = false;
    }
     protected override void OnPopulateMesh(VertexHelper toFill)
     {
         toFill.Clear();
     }

    protected override void Awake()
    {
        base.Awake();
        _maskableList = new List<MaskableGraphic>();
    }

    public override void Cull(Rect clipRect, bool validRect)
    {
        base.Cull(clipRect, validRect);
        GetComponentsInChildren(false, _maskableList);
        if (null != _maskableList)
        {
            for (int i = 0; i < _maskableList.Count; i++)
            { 
                var maskable = _maskableList[i];
                if (maskable != null && maskable != this)
                { 
                    maskable.Cull(clipRect, validRect);
                }
            }
        }
    }

    public override void SetClipRect(Rect clipRect, bool validRect)
    {
        base.SetClipRect(clipRect, validRect);
        GetComponentsInChildren(false, _maskableList);
        if (null != _maskableList)
        {
            for (int i = 0; i < _maskableList.Count; i++)
            {
                var maskable = _maskableList[i];
                if (maskable != null && maskable != this)
                {
                    maskable.SetClipRect(clipRect, validRect);
                }
            }
        }
    }
}


如果元素不是动态变化的,就缓存起来。


inline float SoftUnityGet2DClipping (in float2 position, in float4 clipRect)
{
    float2 xy = (position.xy-clipRect.xy)/float2(_ClipSoftX,_ClipSoftY)*step(clipRect.xy, position.xy);
    float2 zw = (clipRect.zw-position.xy)/float2(_ClipSoftX,_ClipSoftY)*step(position.xy,clipRect.zw);
    float2 factor = clamp(0, zw, xy);
    return saturate(min(factor.x,factor.y));
}

c.a *= SoftUnityGet2DClipping(i.wpos.xy, float4(_MinX, _MinY, _MaxX, _MaxY));



需要将RectMask2D的区域传给3d物品显示的Shader,然后控制3d物体的透明度,不在范围内的设置为0


首先修改使用到的Shader,添加(使用到的参数声明忽略):

//Properties
_MinX ("Min X", Float) = -10
_MaxX ("Max X", Float) = 10
_MinY ("Min Y", Float) = -10
_MaxY ("Max Y", Float) = 10

//顶点函数
o.wpos = mul(unity_ObjectToWorld, v.vertex).xyz;

//片元函数
c.a *= (i.wpos.x >= _MinX );
c.a *= (i.wpos.x <= _MaxX);
c.a *= (i.wpos.y >= _MinY);
c.a *= (i.wpos.y <= _MaxY);
c.rgb *= c.a;



  获取RectMask2D的区域,传递需要裁剪的3D物体

Vector3[] corners = new Vector3[4];
RectTransform rectTransform = GetComponent<RectTransform>("UIGirdlView/Viewport");
rectTransform.GetWorldCorners(corners);
m_GridCorners[0] = corners[0].x;
m_GridCorners[1] = corners[0].y;
m_GridCorners[2] = corners[2].x;
m_GridCorners[3] = corners[2].y;


var items = transform.GetComponentsInChildren<Renderer>();
foreach (var item in items)
{
    var m = item.material;
    m.SetFloat("_MinX", m_GridCorners[0]);
    m.SetFloat("_MinY", m_GridCorners[1]);
    m.SetFloat("_MaxX", m_GridCorners[2]);
    m.SetFloat("_MaxY", m_GridCorners[3]);
}



对于在Shader使用Toggle标志的参数

[Toggle(_KEYWORD0_ON)] _Keyword0("Keyword 0", Float) = 0

#ifdef _KEYWORD0_ON

#else

#endif


可以通过以下C#控制开关

m_Image.material.EnableKeyword("_KEYWORD0_ON");
m_Image.material.DisableKeyword("_KEYWORD0_ON");



可以直接用对ui的输入操作来对3d物体进行交互,例如添加父物体是ui的一个空image,勾选Raycast target,对他做IpointClickHandler等,后影响3D物体即可


public static void RestartOrKillApp()
{
    if (Application.isEditor) return;
    if (Application.platform == RuntimePlatform.Android)
    {
        using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        {
            const int kIntent_FLAG_ACTIVITY_CLEAR_TASK = 0x00008000;
            const int kIntent_FLAG_ACTIVITY_NEW_TASK = 0x10000000;

            var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
            var pm = currentActivity.Call<AndroidJavaObject>("getPackageManager");
            var intent = pm.Call<AndroidJavaObject>("getLaunchIntentForPackage", Application.identifier);

            intent.Call<AndroidJavaObject>("setFlags", kIntent_FLAG_ACTIVITY_NEW_TASK | kIntent_FLAG_ACTIVITY_CLEAR_TASK);
            currentActivity.Call("startActivity", intent);
            currentActivity.Call("finish");
            var process = new AndroidJavaClass("android.os.Process");
            int pid = process.CallStatic<int>("myPid");
            process.CallStatic("killProcess", pid);
        }

    }
    else if (Application.platform == RuntimePlatform.IPhonePlayer)
    {
        //测试只有下面俩种类型好用,FatalError几率卡界面
        UnityEngine.Diagnostics.Utils.ForceCrash(UnityEngine.Diagnostics.ForcedCrashCategory.FatalError);
        //UnityEngine.Diagnostics.Utils.ForceCrash(UnityEngine.Diagnostics.ForcedCrashCategory.PureVirtualFunction);
    }
    else
    {
        Application.Quit();
    }

}



需要扩展一下UnityWebRequestAsyncOperation

using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using UnityEngine.Networking;

public static class ExtensionMethods
{
    public static TaskAwaiter<object> GetAwaiter(this UnityWebRequestAsyncOperation op)
    {
        var tcs = new TaskCompletionSource<object>();
        op.completed += (obj) =>
        {
            tcs.SetResult(null);
        };
        return tcs.Task.GetAwaiter();
    }
}



可以用uniwebview插件。


可能都有试过,给文本组件的物体添加了ContentSizeFiter组件然后去获取PreferredHeight的情况,发现PreferredHeight并不是设置文本后的,此时可以留意设置文本和获取长度时物体是不是被隐藏了,ContentSizeFiter在被激活的时候才会生效。