使用 Unity 做游戏开发的一些记录

sunls24 于 2020-12-16 发布

Unity 检测是否点击到UI元素

public static bool IfTouchUI() {
    if (EventSystem.current == null) {
        return false;
    }
    var eventData = new PointerEventData(EventSystem.current);
    var mousePos = Input.mousePosition;
    eventData.position = new Vector2(mousePos.x, mousePos.y);
    var results = new List<RaycastResult>();
    EventSystem.current.RaycastAll(eventData, results);
    return results.Count > 0;
}

常用于判断点击到UI还是游戏场景内的物体

Unity 使用 Dotween 实现数字插值变化

/// <summary>
/// 数字渐变
/// </summary>
public static void GradientNumber(this Text target, int start, int end, float duration = 0.5f,
    Func<int, string> func = null) {
    if (start == -1) {
        int.TryParse(target.text, out start);
    }
    if (func == null) {
        func = val => val.ToString();
    }
    DOTween.To(() => start, value => target.text = func(value), end, duration);
}

C# 根据权重随机列表

public interface IRandomItem {
    /// <summary>
    /// 随机权重
    /// </summary>
    int Weight { get; }
}

/// <summary>
/// 根据权重随机列表, 返回命中项数组 (值越大抽中概率越大)
/// </summary>
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
public static T[] RandomList<T>(IEnumerable<T> list, int count = 1) where T : IRandomItem {
    var result = new T[count];
    var totalWeight = list.Select(item => item.Weight).Sum();
    for (var i = 0; i < count; ++i) {
        var random = Random.Range(0, totalWeight);
        int min = 0, max = 0;
        foreach (var item in list) {
            max += item.Weight;
            if (random >= min && random < max) {
                result[i] = item;
                break;
            }
            min = max;
        }
    }
    return result;
}

public static T RandomOne<T>(IEnumerable<T> list) where T : IRandomItem => RandomList(list)[0];

SuppressMessage 特性只是为了隐藏 Rider 关于可能多次迭代的警告,事实上不会有这个问题。
随机的数据类需继承 IRandomItem 接口,并实现 Weight 属性。
常用于英雄抽卡,幸运转盘,抽奖池等;需要根据权重随机抽取元素的模块。

C# String分割为数组

/// <summary>
/// string分割为数组, 默认以','分割
/// </summary>
public static T[] Str2Arr<T>(this string str, Func<string, T> func, char splitChar = ',') {
    return str.Split(splitChar).Select(func).ToArray();
}

可传入 func 指定分割后的数组类型,转换为 int 则需传入 int.Parse

BigInteger 类型的格式化显示

/// <summary>
/// 数字格式化
/// </summary>
public static string FormatBigInt(this BigInteger bigInt) {
    var bigIntStr = bigInt.ToString();
    var digit = bigIntStr.Length; // 位数
    if (digit <= 3) {
        return bigIntStr;
    }

    digit -= 1;
    var prefix = digit % 3 + 1; // 小数点前的位数
    return bigIntStr.Substring(0, prefix + 2).Insert(prefix, ".") + GetA_Z(digit / 3);
}

/// <summary>
/// 获取26个字母
/// </summary>
/// <param name="num">1是a, 2是b</param>
private static char GetA_Z(int num) => (char) (96 + num);

当游戏数值非常大,超出 decimal 和 double 的精度时,需要使用 BigInteger 类型。
进行乘除运算时使用 double,加减以及存储时使用 BigInteger。
格式化以千进位,例如:1234=1.23a。

C# 删除栈中的指定元素

/// <summary>
/// 删除栈中的指定元素
/// </summary>
/// <returns>返回是否删除, false代表栈中无此元素</returns>
public static bool RemoveAny<T>(this Stack<T> stack, T element) {
    var topElement = stack.Pop();
    if (Equals(topElement, element)) {
        return true;
    }

    var remove = false;
    if (stack.Count != 0) {
        remove = stack.RemoveAny(element);
    }

    stack.Push(topElement);
    return remove;
}

C# 简单树结构实现

using System;
using System.Collections.Generic;

/// <summary>
/// 简单的树结构
/// </summary>
public class TreeNode<T> {
    public readonly T Value;
    public TreeNode<T> Parent { get; private set; }
    public List<TreeNode<T>> Children { get; private set; }

    /// <summary>
    /// 是否是根节点
    /// </summary>
    public bool IsRoot => Parent == null;

    /// <summary>
    /// 是否是叶子节点
    /// </summary>
    public bool IsLeaf => Children == null;

    public TreeNode(T value) {
        Value = value;
    }

    public TreeNode<T> SetParent(T value) {
        var node = new TreeNode<T>(value);
        SetParent(node);
        return node;
    }

    public void SetParent(TreeNode<T> node) {
        if (!IsRoot) {
            Parent.RemoveChildren(this);
        }
        node.AddChildren(this);
    }

    public TreeNode<T> AddChildren(T value) {
        var node = new TreeNode<T>(value);
        AddChildren(node);
        return node;
    }

    public void AddChildren(TreeNode<T> node) {
        if (IsLeaf) {
            Children = new List<TreeNode<T>>();
        }
        Children.Add(node);
        node.Parent = this;
    }

    public void RemoveChildren(TreeNode<T> node) {
        if (IsLeaf) {
            return;
        }
        Children.Remove(node);
        if (Children.Count == 0) {
            Children = null;
        }
    }

    /// <summary>
    /// 递归查询孩子节点
    /// </summary>
    private static TreeNode<T> FindChildren(TreeNode<T> node, Func<T, bool> func) {
        if (func(node.Value)) {
            return node;
        }
        if (node.IsLeaf) {
            return null;
        }
        foreach (var child in node.Children) {
            var find = FindChildren(child, func);
            if (find != null) {
                return find;
            }
        }
        return null;
    }

    public TreeNode<T> FindChildren(Func<T, bool> func) => FindChildren(this, func);

    public TreeNode<T> FindParent(Func<T, bool> func) {
        var result = this;
        while (!result.IsRoot) {
            result = result.Parent;
            if (func(result.Value)) {
                return result;
            }
        }
        return null;
    }
}