嗚嗚喔學習筆記

搜尋此網誌

2022年4月7日 星期四

C# Array or List Safe

Array & List 常常要做 null check & out of range check 寫起來麻煩 把這段寫成擴充函式方便些


public static class ArraySafe

{

    public static bool IsSafe<T>(this T[] array, int index)

    {

        return array != null && index >= 0 && index < array.Length;

    }


    public static bool IsSafe<T>(this System.Collections.Generic.List<T> array, int index)

    {

        return array != null && index >= 0 && index < array.Count;

    }


    public static bool TryGetElement<T>(this T[] array, int index, out T element)

    {

        if (array == null || index < 0 || index >= array.Length)

        {

            element = default(T);

            return false;

        }


        element = array[index];

        return true;

    }


    public static bool TryGetElement<T>(this System.Collections.Generic.List<T> list, int index, out T element)

    {

        if (list == null || index < 0 || index >= list.Count)

        {

            element = default(T);

            return false;

        }


        element = list[index];

        return true;

    }

}


2022年1月17日 星期一

Bit Flag C#

連續的bool array 可以改成連續的 bit 來儲存 可以壓縮 8倍 記憶體


public static class BitHelper

{

    public static byte[] BoolAryToBits(bool[] bools)

    {

        if (bools == null)

        {

            Debug.LogError("get null? ");

            return null;

        }


        int len = bools.Length >> 3;

        if (bools.Length % 8 != 0)

        {

            len++;

        }

        byte[] result = new byte[len];


        for (int i = 0; i < bools.Length; i++)

        {

            bool setTrue = bools[i];

            if (((i >> 3) < result.Length) && setTrue)

            {

                result[i >> 3] |= (byte)(1 << (i & 7));//一個bit可以為0或1足夠判斷了

            }

        }


        return result;

    }

}

2021年10月7日 星期四

Poisson Disk Sampling - Unity - C#

Poisson Disk Sampling

創造一個自然 而且每個點之間距離均衡的演算法:


演算結果: 長寬 105x105 圓圈半徑:10

Unity C# 版本:


using UnityEngine;

using System.Collections.Generic;

[System.Serializable]

public class PoissonDiscHelper

{

    int _w;

    int _h;

    float _r;

    [SerializeField]

    public List<Vector2> Points = new List<Vector2>();

    [SerializeField]

    List<Vector2> CandidatePoints = new List<Vector2>();


    public List<Vector2> Gen(int w, int h, float r)

    {

        _w = w;

        _h = h;

        _r = r;

        Points.Clear();

        CandidatePoints.Clear();

        AddPointToMax();

        return Points;

    }

    private void AddPointToMax()

    {

        int limit = 9999;

        int idx = 0;

        while ((Points.Count == 0 || CandidatePoints.Count > 0) && idx < limit)

        {

            AddPoint();

            idx++;

        }

    }

    private void AddPoint()

    {

        Vector2 newP;

        if (Points.Count == 0)

        {

            System.Random r = new System.Random();

            newP = new Vector2(r.Next(0, _w), r.Next(0, _h));

        }

        else

        {

            if (CandidatePoints.Count <= 0)

            {

                Debug.LogFormat("Total Done.  Point Count: {0}", Points.Count);

                return;

            }


            int rand = Random.Range(0, CandidatePoints.Count - 1);

            newP = CandidatePoints[rand];

            CandidatePoints.RemoveAt(rand);

        }


        Points.Add(newP);


        int rayCount = 64;

        for (int rayI = 0; rayI < rayCount; rayI++)

        {

            Vector3 dir3 = Rotate(new Vector3(0, 0, 1), Vector3.up, rayI * 360f / rayCount);

            Vector2 dir = new Vector2(dir3.x, dir3.z);

            Vector2 candiate = newP + (dir.normalized * _r);

            if (candiate.x <= _r / 4 || candiate.y <= _r / 4)

            {

                continue;

            }

            if (candiate.x > _w - _r / 4 || candiate.y > _h - _r / 4)

            {

                continue;

            }


            CandidatePoints.Add(candiate);

        }


        foreach (Vector2 p in Points)

        {

            for (int i = CandidatePoints.Count - 1; i >= 0; i--)

            {

                Vector2 cp = CandidatePoints[i];

                if (Vector2.Distance(cp, p) < _r - 0.001f)

                {

                    CandidatePoints.RemoveAt(i);

                }

            }

        }

    }

    private Vector3 Rotate(Vector3 source, Vector3 axis, float angle)

    {

        Quaternion q = Quaternion.AngleAxis(angle, axis);// 旋转系数

        return q * source;// 返回目标点

    }

}



其他可參考演算法:
Best Candidate Sampling
Lloyd's algorithm


參考資料:

https://chih-sheng-huang821.medium.com/%E6%A9%9F%E5%99%A8%E5%AD%B8%E7%BF%92-%E9%9B%86%E7%BE%A4%E5%88%86%E6%9E%90-k-means-clustering-e608a7fe1b43


2021年9月26日 星期日

Unity 向量沿著某個軸向旋轉

/// <summary>

/// 计算一个Vector3绕指定轴旋转指定角度后所得到的向量。

/// </summary>

/// <param name="source">旋转前的源Vector3</param>

/// <param name="axis">旋转轴</param>

/// <param name="angle">旋转角度</param>

/// <returns>旋转后得到的新Vector3</returns>

public Vector3 Rotate(Vector3 source, Vector3 axis, float angle)

{

    Quaternion q = Quaternion.AngleAxis(angle, axis);// 旋转系数

    return q * source;// 返回目标点

}

2021年9月1日 星期三

Design pattern - 命令模式

在物件導向程式設計的範疇中,命令模式 是一種設計模式,它嘗試以物件來代表實際行動。命令物件可以把行動(action) 及其參數封裝起來,於是這些行動可以被:
  • 重複多次
  • 取消(如果該物件有實作的話)
  • 取消後又再重做

這裡實現一個可以做 上一步 & 下一步 的命令模式 如果要新增指令 只要繼承 ICommand 並實作 Execute() && Undo() 就行了 使用時只要呼叫 _cmd = new XXXCmd(); _cmd.Execute(); _cmdHistoryMgr.StoreCommand(_cmd);


上一步只需要呼叫 _cmdHistoryMgr.Undo(); 下一步則是呼叫 _cmdHistoryMgr.Redo(); 不需要管Command的細節 達到擴充性。

using System.Collections.Generic;


public interface ICommand

{

void Execute();

void Undo();

}


public interface ICommandManager

{

void StoreCommand(ICommand cmd);

void ClearAllCommand();

void Undo();

void Redo();

}


public class CommandHistoryManager : ICommandManager

{

Stack<ICommand> _undoStack = new Stack<ICommand>();

Stack<ICommand> _redoStack = new Stack<ICommand>();


public void ClearAllCommand()

{

_undoStack.Clear();

_redoStack.Clear();

}

public void StoreCommand(ICommand cmd)

{

_undoStack.Push(cmd);

}


public void Redo()

{

if (_redoStack.Count <= 0)

{

return;

}


ICommand cmd = _redoStack.Pop();

cmd.Execute();


_undoStack.Push(cmd);

}


public void Undo()

{

if(_undoStack.Count <= 0)

{

return;

}


ICommand cmd =_undoStack.Pop();

cmd.Undo();


_redoStack.Push(cmd);

}

}