天天看點

遊戲程式設計精粹學習 - 位數組/位鎖

在《遊戲程式設計精粹1》裡的1.10處講解了位數組的應用,原文意把位操作變為類似數組的形式,進而更加直覺。

(注意C#中已經内置這種資料結構,叫做BitArray)

不過我聯想到是否可以設計一個位鎖的概念。

我遇到過這樣一個問題,遊戲中有一些子產品的參數必須在所有使用者都結束後才可以被釋放。

例如時間系統的暫停,當你在進行某些特殊任務時需要調用時間暫停,而在這個特殊任務的過程中播放了過場動畫也會調用暫停。

是以當他們都結束使用了,暫停這個參數才會變為false。

類似的情況還有AI的開啟和關閉等等。

2021/12/3補充:

後來學習到了德布萊英序列,可以不需要周遊就能知道mask裡最近的那個1在哪,

于是對BitLock代碼進行了修改。

下面是支援32個使用者的位鎖代碼:

namespace Hont
{
    using System;
    using UnityEngine;

    public sealed class BitLock
    {
        public const uint kInvalidLock = 0u;

        private static readonly int[] kDeBruijnSequence =
        {
            0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
            31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
        };

        private const int kLockedMAXValue = 32;

        private uint mBitMask;

        public bool IsLocked => mBitMask != 0;


        public BitLock()
        {
            mBitMask = 0;
        }

        /// <summary>
        /// 這個鎖的句柄是否未配置設定
        /// </summary>
        public bool IsUnassignLock(uint handle)
        {
            return (mBitMask & handle) != handle;
        }

        /// <summary>
        /// 配置設定一個鎖,并傳回鎖的句柄。若傳回0則傳回失敗。
        /// </summary>
        public uint AssignLock()
        {
            uint invMask = ~mBitMask;
            if (invMask == 0) return 0;
            const uint kMagicNumber = 0x077CB531u;
            uint result = (uint) 1 << kDeBruijnSequence[((invMask & ((uint) -invMask)) * kMagicNumber) >> 27];
            mBitMask |= result;

            return result;
        }

        /// <summary>
        /// 通過句柄放回一個鎖。
        /// </summary>
        public void UnassignLock(uint handle)
        {
            mBitMask = mBitMask & (~ handle);
        }

        public void ReleasedAllLock()
        {
            mBitMask = 0;
        }
    }
}