Unity3D相关知识点笔记汇总

这篇文章将作为一些平时的小知识点笔记来记录,如果有错误望指出来,也欢迎大家在评论底下分享你们的笔记。

1.检测点击或者触摸到UI。


    public static bool CheckClickUI()
    {
        bool isClickUI = false;
        if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
        {
            if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
            {
                isClickUI = true;
            }
        }
        else if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.OSXEditor)
        {
            if (EventSystem.current.IsPointerOverGameObject())
            {
                isClickUI = true;
            }
        }
        return isClickUI;
    }

2.发现content size fitter使用后,宽和高没有实时刷新,而是下一帧刷新,不知道是不是bug,这里要调用 ForceUpdateCanvases() 来强制刷新Canvas。

Canvas.ForceUpdateCanvases();

3.获取文本的绘制长度,不同于text的rectTransform.sizeDelta。


        public static int GetFontlen(string str)
        {
            int len = 0;
            Font font;
            font = Font.CreateDynamicFontFromOSFont("Arial", 25);
            font.RequestCharactersInTexture(str);
            for (int i = 0; i < str.Length; i++)
            {
                CharacterInfo ch;
                font.GetCharacterInfo(str[i], out ch);
                len += ch.advance;
            }
            return len;
        }

4.游戏的FPS,不知道为什么很多人都还要自己去计算?

        Time.smoothDeltaTime;

5.简便方法随机 Vector2 和 Vector3。

        UnityEngine.Random.insideUnitCircle;
        UnityEngine.Random.insideUnitSphere;

6.利用富文本可以改变OnGUI上面的按钮和文字的大小。

    void OnGUI()
    {
        GUILayout.Label("<color=green><size=80>我是大文字</size></color>");
        GUILayout.Button("<color=green><size=80>我是大按钮</size></color>");
    }

7.计算代码执行用时。

        System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
        stopwatch.Start();
        //TODO...
        stopwatch.Stop();
        TimeSpan timeSpan = stopwatch.Elapsed;

8.获取自己,子对象和子对象的子对象……

        Transform[] ts = Obj.GetComponentsInChildren<Transform>();
        for (int i = 0; i < ts.Length; i++)
        {
            ts[i].gameObject.layer = LayerMask.NameToLayer("UI");
        }

9.windows控制台窗口显示手机Debug信息

创建一个批处理文件,输入以下代码,保存为.bat格式,连接手机后双击该文件即可显示Debug信息了,电脑必须安装adb,手机必须打开开发者模式。

@echo off    
start cmd /k "adb logcat -s Unity"

10.判断对象为空不要用以下的代码,是因为gameobject是unity的对象,它除了在托管内存区域有一份对象外,还在native内存区域存在一份对象,这两份对象通过一个我们可以称为内存桥(Memory Bridge)来交流,这个多少会造成一些overhead。

        if (gameObject == null)
        {

        }

要尽量用以下的代码来判空操作

        if (System.Object.ReferenceEquals(gameObject, null))
        {

        }

11.用CompareTag()代替gameobject.tag,不然会造成上面的Memory Bridge的overhead,以及会产生gc。

12.使用static batch时,要将这些物体最开始就放入场景中,并勾选static才能使static batch生效。在运行时自动生成的物体,就算勾选了static batch,也不会和场景中默认的static batch 的物体合并到一起。

13.对文件比较大,且使用比较频繁的音频文件,勾选音频文件的loadType 设置为compressed in memory来压缩。

14.勾选了mip map会导致贴图的内存占用增高原来的30%。因此,那些可以确保不会随着游戏的进行,与摄像机的距离发生明显变化的物体,例如UI,天空盒,主角等不需要勾选mip map。其他的物体,比如3D世界中的场景物件可以按需勾选mip map。

15.使用GPU Instancing功能,减少Draw Call,这种一般用于绘制大量简单的相似的物体,比如成片的草地,树木等。

16.使用LOD是用一定的内存和CPU时间,来换取Draw call,填充率和现存带宽。但这项技术建议不要一开始就使用,只有当我们意识到性能问题再GPU端时,且有一定的内存和CPU预算时,才考虑使用LOD功能。可以看到的是:如果游戏有很丰富的场景,摄像机可以看到很远时,就可以在早期就使用LOD,若是俯视角,固定视角,室内等场景时,或许并不需要使用LOD。

17.对于不交互的UI元素,要禁用Ratcast Target选项,我们可以重写这些创建接口,默认禁用Ratcast Target。

18.UI动静分离,将Canvas分为三个,Static,Incidental Dynamic,和Continuous Dynamic。Static的Canvas下主要放一些背景图片的元素,基本上从来都不会动的。Incidental Dynamic的canvas下主要放一些响应事件的UI,比如提示框,包含UI按钮的UI等。Continuous Dynamic下放一些频繁变化的UI,比如进度条或者有动画的UI元素。

19.为World  Canvas指定一个Camera,否则它会每帧都执行Camera.Main,而Camera.Main会持续调用Gameobj.Find方法。

20.在性能不敏感的时期主动出发垃圾回收(System.GC.Collect方法),比如在切换场景,暂停游戏时等。

21.在使用string时,若有多字符的链接操作(即+操作符),若要处理较多字符串的连接,则使用stringBuilder类;若较少,则使用String.Format方法或者String.Concact方法。多字符连接数大于2时,就避免使用+操作符了。

StringBuilder sb = new StringBuilder(5); //当指定分配大小之后,性能就会得到提升。在达到容量之前,它不会为其自己重新分配空间。如果超过指定大小系统会当前大小倍增,也就10,15,20。建议指定大小
sb.Append('china');

StringBuilder sb = new StringBuilder("Hello World!");  
sb.Append(" What a beautiful day."); 

int MyInt = 25;    
StringBuilder sb = new StringBuilder("Your total is ");  
sb.AppendFormat("{0:C}   ",   MyInt);  

22.在调用UnityAPI时,那些会返回一个数组的API都会在堆上分配新的内存空间,比如GetComponents<T>(),mesh.vertices等。每次调用都会创建一个全新的数据,因此,我们必须谨慎的调用这些方法,并缓存结果。

23.GetComponent()会有一定的GC产生,应该避免频繁调用并缓存,禁止在Update里面调用GetComponent()相关的方法。

24.必要情况下,在Dictionary的键中,我们要缓存Unity对象时,使用UnityObject的InstanceID来作为字典的键,而不是直接使用Object的引用。

25.

==它是比较的栈里面的值是否相等(值比较)

Equals它比较的是堆里面的值是否相等(引用地址值比较)

Object.ReferenceEquals(obj1,obj2)它是比较的是内存地址是否相等

26.目前在一些典型的3D游戏的制作中,全屏不超过10万个顶点和200个draw call左右,不然对中端机器会有一定压力。

27.颜色编码和Color之间的转换。

Color color = ColorUtility.ToHtmlStringRGB(strcolor);
    private Color GetColor(string colorcode)
    {
        Color color;
        StringBuilder sb = new StringBuilder("#");
        sb.Append(colorcode);
        ColorUtility.TryParseHtmlString(sb.ToString(), out color);
        return color;
    }

28.获取渐变色。

    private Gradient GetGradient(Color color)
    {
        Gradient gradient = new Gradient();
        gradient.SetKeys(
            new GradientColorKey[] { new GradientColorKey(color, 0.0f), new GradientColorKey(color, 1.0f) },
            new GradientAlphaKey[] { new GradientAlphaKey(1, 0.0f), new GradientAlphaKey(1, 1.0f) }
            );
        return gradient;
    }

29.获取渐变色。需要主要的是Gradient的key长度最大为8。

    /// <summary>
    /// 获取渐变色,Gradient的key长度最大为8
    /// </summary>
    /// <param name="strColors">颜色编码集合</param>
    /// <returns>渐变色</returns>
    public static Gradient GetGradient(List<string> strColors)
    {
        Gradient gradient = new Gradient();
        GradientColorKey[] colorKey = new GradientColorKey[strColors.Count];
        GradientAlphaKey[] alphaKey = new GradientAlphaKey[strColors.Count];
        float offset = 1.0f / (strColors.Count - 1);
        for (int i = 0; i < strColors.Count; i++)
        {
            Color color = GetColor(strColors[i]);
            float time = i * offset;

            XMDebug.Log(time);

            colorKey[i].color = color;
            colorKey[i].time = time;

            alphaKey[i].alpha = 1.0f;
            alphaKey[i].time = time;
        }
        gradient.SetKeys(colorKey, alphaKey);
        return gradient;
    }

30.设置随机种子

    private float[] noiseValues;
    void Start()
    {
        Random.InitState(42);
        noiseValues = new float[10];
        for (int i = 0; i < noiseValues.Length; i++)
        {
            noiseValues[i] = Random.value;
            Debug.Log(noiseValues[i]);
        }
    }

31.时间戳的转换

    /// <summary>
    /// 时间戳转换为时间
    /// </summary>
    /// <param name="timeStamp"></param>
    /// <returns></returns>
    public static DateTime StampToDateTime(string timeStamp)
    {
        DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
        long mTime = long.Parse(timeStamp + "0000");
        TimeSpan toNow = new TimeSpan(mTime);
        return startTime.Add(toNow);
    }

    /// <summary>
    /// 时间转时间戳
    /// </summary>
    /// <param name="now"></param>
    /// <returns></returns>
    public static string DateTimeToStamp(DateTime now)
    {
        DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
        long timeStamp = (long)(now - startTime).TotalMilliseconds; // 相差毫秒数
        return timeStamp.ToString();
    }

32.复制粘贴

GUIUtility.systemCopyBuffer

33.UGUI在使用DOTween的时候其实有一些坑的,比如移动,自己封装一个拓展方法。

    public static void Move(this RectTransform rect, Vector2 endValue, float duration, Action callback = null)
    {
        DOTween.To(() => { return rect.anchoredPosition; }, v => { rect.anchoredPosition = v; }, endValue, duration).OnComplete(() =>
        {
            callback?.Invoke();
        });
    }

34.从文本中删除富文本标签(HTML标签),需求比如要统计文字个数。

using UnityEngine;
using System.Text.RegularExpressions;     
public class TestDeleteTag : MonoBehaviour
{
    private const string TEXT = "请<size=12>关注</size><color=red> Twitter </color>。";
    void Start()
    {
        //请<size=12>关注</size><color=red> Twitter </color>。
        Debug.Log(TEXT);
 
        //删除<O>或<O>
        string text = Regex.Replace(TEXT, "<[^>]*?>", string.Empty);
 
        //请关注Twitter。
        Debug.Log(text);
    }
}

35.修改预设模式场景

using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using UnityEditor.Experimental.SceneManagement;
using UnityEditor.SceneManagement;

public class EditorTools
{
    private static MaskableGraphic[] graphics;

    [MenuItem("GameObject/RemoveUIRaycastTarget", false, 20)]
    static void RemoveUIRaycastTarget()
    {
        GameObject selectionObj = Selection.activeGameObject;
        if (selectionObj)
        {
            //graphics = GameObject.FindObjectsOfType<MaskableGraphic>();
            graphics = selectionObj.GetComponentsInChildren<MaskableGraphic>();
            for (int i = 0; i < graphics.Length; i++)
            {
                MaskableGraphic graphic = graphics[i];
                graphic.raycastTarget = false;
                XMDebug.Log(graphic);
            }
            PrefabStage prefabStage = PrefabStageUtility.GetPrefabStage(selectionObj);
            if (prefabStage != null)
            {
                EditorSceneManager.MarkSceneDirty(prefabStage.scene);
            }
        }
    }
}

36.UnityWebRequest网络请求,代替弃用的www

    /// <summary>
    /// HttpGet请求
    /// </summary>
    /// <param name="url"></param>
    /// <param name="callback"></param>
    /// <returns></returns>
    private IEnumerator HttpGetRequest(string url, Action<string> callback)
    {
        using (UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequest.Get(url))
        {
            yield return www.SendWebRequest();
            if (www.isHttpError || www.isNetworkError)
            {
                Debug.LogError(www.error);
            }
            else
            {
                callback(www.downloadHandler.text);
            }
        }
    }

    /// <summary>
    /// HttpPost请求
    /// </summary>
    /// <param name="url"></param>
    /// <param name="form">WWWForm form = new WWWForm();form.AddField("key", "value");</param>
    /// <param name="callback"></param>
    /// <returns></returns>
    private IEnumerator HttpPostRequest(string url, WWWForm form, Action<string> callback)
    {
        using (UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequest.Post(url, form))
        {
            yield return www.SendWebRequest();
            if (www.isHttpError || www.isNetworkError)
            {
                Debug.LogError(www.error);
            }
            else
            {
                callback(www.downloadHandler.text);
            }
        }
    }

37.文本显示不下的时候后面的文本会转换为 ... 省略号 。

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;


/// <summary>
/// Textを範囲内に収まるように省略して表示する
/// 省略表示させたいTextと一緒にアタッチする
/// </summary>
public class TextEllipsis : UIBehaviour
{
    private Text m_Text;

    protected override void Awake()
    {
        if (m_Text != null)
        {
            return;
        }

        m_Text = GetComponent<Text>();
        m_Text.RegisterDirtyLayoutCallback(DoEllipsis);
    }

    protected override void OnDestroy()
    {
        m_Text.UnregisterDirtyLayoutCallback(DoEllipsis);
    }


    /// <summary>
    /// 省略記号
    /// </summary>
    private static readonly string ELLIPSIS = "...";

    private void DoEllipsis()
    {
        if (!IsActive() || m_Text == null)
        {
            return;
        }

        if (!NeedsEllipsis(m_Text))
        {
            return;
        }

        TextGenerator generator = m_Text.cachedTextGenerator;
        TextGenerationSettings settings = m_Text.GetGenerationSettings(m_Text.rectTransform.rect.size);
        generator.Populate(m_Text.text, settings);
        string result = string.Empty;
        for (int i = 0; i < generator.characterCount; ++i)
        {
            string current = m_Text.text.Substring(i, 1);
            string next = string.Empty;
            if (i + 1 <= generator.characterCount)
            {
                next = m_Text.text.Substring(i + 1, 1);
            }

            var preferredWidth = GetPreferredWidth(result + current + next);
            if (IsOverSize(m_Text.rectTransform.rect.size.x, preferredWidth))
            {
                result += ELLIPSIS;
                break;
            }

            result += current;
        }

        m_Text.text = result;
    }

    private bool NeedsEllipsis(Text text)
    {
        return IsOverSize(text.rectTransform.rect.size.x, text.preferredWidth);
    }

    private bool IsOverSize(float textBoxWidth, float preferredWidth)
    {
        return textBoxWidth < preferredWidth;
    }

    private float GetPreferredWidth(string str)
    {
        var settings = m_Text.GetGenerationSettings(Vector2.zero);
        return m_Text.cachedTextGeneratorForLayout.GetPreferredWidth(str, settings) / m_Text.pixelsPerUnit;
    }
}

38.

还有到顶请求网络刷新的实现。

https://github.com/kiepng/Unity-PullToRefresh

39.关于高分辨率触摸设备上ScrollRect内的Item的点击事件容易触发滑动事件的解决办法。

EventSystem.current.pixelDragThreshold = Screen.height / 50;

40.手机端的安全区域UI适配。

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

public class SafeArea : MonoBehaviour
{
    public enum SimDevice { None, iPhoneX }

    public SimDevice Sim = SimDevice.iPhoneX;

    Rect[] NSA_iPhoneX = new Rect[]
    {
        new Rect (0f, 102f / 2436f, 1f, 2202f / 2436f),  // Portrait
        new Rect (132f / 2436f, 63f / 1125f, 2172f / 2436f, 1062f / 1125f)  // Landscape
    };

    RectTransform Panel;
    Rect LastSafeArea = new Rect(0, 0, 0, 0);

    void Awake()
    {
        Panel = GetComponent<RectTransform>();
        Refresh();
    }

    void Update()
    {
        Refresh();
    }

    void Refresh()
    {
        Rect safeArea = GetSafeArea();

        if (safeArea != LastSafeArea)
            ApplySafeArea(safeArea);
    }

    Rect GetSafeArea()
    {
#if UNITY_EDITOR
        Rect safeArea = Screen.safeArea;

        if (Application.isEditor && Sim != SimDevice.None)
        {
            Rect nsa = new Rect(0, 0, Screen.width, Screen.height);

            switch (Sim)
            {
                case SimDevice.iPhoneX:
                    if (Screen.height > Screen.width)  // Portrait
                        nsa = NSA_iPhoneX[0];
                    else  // Landscape
                        nsa = NSA_iPhoneX[1];
                    break;
                default:
                    break;
            }

            safeArea = new Rect(Screen.width * nsa.x, Screen.height * nsa.y, Screen.width * nsa.width, Screen.height * nsa.height);
        }

        return safeArea;
#else
        Screen.safeArea;
#endif

    }

    void ApplySafeArea(Rect r)
    {
        LastSafeArea = r;

        // Convert safe area rectangle from absolute pixels to normalised anchor coordinates
        Vector2 anchorMin = r.position;
        Vector2 anchorMax = r.position + r.size;
        anchorMin.x /= Screen.width;
        anchorMin.y /= Screen.height;
        anchorMax.x /= Screen.width;
        anchorMax.y /= Screen.height;
        Panel.anchorMin = anchorMin;
        Panel.anchorMax = anchorMax;

        Debug.LogFormat("New safe area applied to {0}: x={1}, y={2}, w={3}, h={4} on full extents w={5}, h={6}",
            name, r.x, r.y, r.width, r.height, Screen.width, Screen.height);
    }
}

41.List<T>比较器Distinct

ware.Distinct(new Compare<T>((x, y) => (x != null && y != null) && (x.XXX!= y.XXX))).ToList();
    public delegate bool CompareDelegate<T>(T x, T y);
    public class Compare<T> : IEqualityComparer<T>
    {
        private CompareDelegate<T> _compare;
        public Compare(CompareDelegate<T> d)
        {
            this._compare = d;
        }

        public bool Equals(T x, T y)
        {
            if (_compare != null)
            {
                return this._compare(x, y);
            }
            else
            {
                return false;
            }
        }

        public int GetHashCode(T obj)
        {
            return obj.ToString().GetHashCode();
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值