Ver.0.3.0.33
This commit is contained in:
@@ -19,18 +19,24 @@ namespace AibisDream.Framework
|
||||
|
||||
#region 相机组件
|
||||
|
||||
private CinemachineBrain _cameraBrain;
|
||||
private static Camera _mainCam;
|
||||
public static Camera UICamera => UIManager.Instance.UICamera;
|
||||
|
||||
private CinemachineBrain _orthoCameraBrain;
|
||||
private CinemachineBrain _perspectiveCameraBrain;
|
||||
private readonly Dictionary<CameraEnum, ICinemachineCamera> _virtualCameraDict = new();
|
||||
private readonly Dictionary<CameraEnum, CamState> _camInitStateDict = new();
|
||||
|
||||
private CameraEnum _curCameraEnum;
|
||||
private CamState _curInitState;
|
||||
public Camera uiCamera;
|
||||
private Camera _orthoMainCamera;
|
||||
private Camera _perspectiveMainCamera;
|
||||
|
||||
private struct CamState
|
||||
{
|
||||
public Vector3 pos;
|
||||
public float initOrthoSize;
|
||||
public bool isPerspective;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -41,65 +47,82 @@ namespace AibisDream.Framework
|
||||
|
||||
public override void OnSingletonInit()
|
||||
{
|
||||
_cameraBrain = Camera.main?.GetComponent<CinemachineBrain>();
|
||||
_curCameraEnum = CameraEnum.Default;
|
||||
|
||||
// 获取UICamera
|
||||
if (Camera.main && Camera.main.TryGetComponent<UniversalAdditionalCameraData>(out var cameraData))
|
||||
{
|
||||
// 获取Stack Cameras
|
||||
var stackCameras = cameraData.cameraStack;
|
||||
// 获取正交相机
|
||||
_orthoMainCamera = Camera.main;
|
||||
_orthoCameraBrain = _orthoMainCamera?.GetComponent<CinemachineBrain>();
|
||||
|
||||
// 获取UICamera
|
||||
uiCamera = stackCameras.Find(cam => cam && cam.name == "UI Camera");
|
||||
// 获取透视相机
|
||||
_perspectiveMainCamera = GameObject.Find("PerspectiveMainCamera")?.GetComponent<Camera>();
|
||||
_perspectiveCameraBrain = _perspectiveMainCamera?.GetComponent<CinemachineBrain>();
|
||||
|
||||
_curCameraEnum = CameraEnum.Default;
|
||||
|
||||
if (_orthoMainCamera && _perspectiveMainCamera)
|
||||
{
|
||||
// 默认激活正交相机,禁用透视相机
|
||||
_orthoMainCamera.gameObject.SetActive(true);
|
||||
_perspectiveMainCamera.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
#region 相机信息注册
|
||||
|
||||
/// <summary>
|
||||
/// 添加UI相机
|
||||
/// </summary>
|
||||
public void AddUICamera(Camera camera)
|
||||
public void RegisterUICamera(Camera overlayCamera)
|
||||
{
|
||||
if (Camera.main && Camera.main.TryGetComponent<UniversalAdditionalCameraData>(out var cameraData))
|
||||
// 获取 Main Camera 的 UniversalAdditionalCameraData
|
||||
var mainCameraData = _orthoMainCamera.GetUniversalAdditionalCameraData();
|
||||
|
||||
if (mainCameraData != null)
|
||||
{
|
||||
cameraData.cameraStack.Add(camera);
|
||||
// 检查 Overlay Camera 是否已经在 Stack 中
|
||||
if (!mainCameraData.cameraStack.Contains(overlayCamera))
|
||||
{
|
||||
// 将 Overlay Camera 添加到 Stack
|
||||
mainCameraData.cameraStack.Insert(0, overlayCamera);
|
||||
Debug.Log("Overlay Camera added to Main Camera stack.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Main Camera does not have UniversalAdditionalCameraData.");
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveUICamera(Camera camera)
|
||||
public void UnRegisterUICamera(Camera overlayCamera)
|
||||
{
|
||||
if (Camera.main && Camera.main.TryGetComponent<UniversalAdditionalCameraData>(out var cameraData))
|
||||
// 获取 Main Camera 的 UniversalAdditionalCameraData
|
||||
var mainCameraData = _orthoMainCamera.GetUniversalAdditionalCameraData();
|
||||
|
||||
if (mainCameraData != null)
|
||||
{
|
||||
cameraData.cameraStack.Remove(camera);
|
||||
// 检查 Overlay Camera 是否已经在 Stack 中
|
||||
if (!mainCameraData.cameraStack.Contains(overlayCamera))
|
||||
{
|
||||
// 将 Overlay Camera 添加到 Stack
|
||||
mainCameraData.cameraStack.Remove(overlayCamera);
|
||||
Debug.Log("Overlay Camera added to Main Camera stack.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Main Camera does not have UniversalAdditionalCameraData.");
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterCamera(CameraEnum camType, ICinemachineCamera virtualCamera)
|
||||
|
||||
public void RegisterCamera(CameraEnum camType, ICinemachineCamera virtualCamera, bool isPerspective = false)
|
||||
{
|
||||
_virtualCameraDict[camType] = virtualCamera;
|
||||
|
||||
|
||||
var initState = new CamState
|
||||
{
|
||||
pos = virtualCamera.VirtualCameraGameObject.transform.localPosition,
|
||||
initOrthoSize = virtualCamera.VirtualCameraGameObject.gameObject
|
||||
.GetComponent<CinemachineVirtualCamera>().m_Lens.OrthographicSize
|
||||
.GetComponent<CinemachineVirtualCamera>().m_Lens.OrthographicSize,
|
||||
isPerspective = isPerspective
|
||||
};
|
||||
_camInitStateDict[camType] = initState;
|
||||
}
|
||||
|
||||
public void UnRegisterCamera(CameraEnum camType, ICinemachineCamera virtualCamera)
|
||||
{
|
||||
if (!_virtualCameraDict.TryGetValue(camType, out var curCam)) return;
|
||||
|
||||
if (virtualCamera == curCam)
|
||||
{
|
||||
_virtualCameraDict.Remove(camType);
|
||||
_camInitStateDict.Remove(camType);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnRegisterCamera(CameraEnum camType)
|
||||
{
|
||||
_virtualCameraDict.Remove(camType);
|
||||
@@ -129,18 +152,31 @@ namespace AibisDream.Framework
|
||||
/// </summary>
|
||||
/// <param name="camType"></param>
|
||||
public IEnumerator SwitchCamera(CameraEnum camType)
|
||||
{
|
||||
var switchDuration = SwitchCameraSync(camType);
|
||||
yield return new WaitForSeconds(switchDuration);
|
||||
}
|
||||
|
||||
public float SwitchCameraSync(CameraEnum camType)
|
||||
{
|
||||
if (_curCameraEnum == camType)
|
||||
{
|
||||
yield break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!_virtualCameraDict.ContainsKey(camType))
|
||||
{
|
||||
Debug.Log($"{camType.ToString()}相机未注册");
|
||||
yield break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 切换主相机
|
||||
bool isPerspective = _camInitStateDict[camType].isPerspective;
|
||||
_orthoMainCamera.gameObject.SetActive(!isPerspective);
|
||||
// _perspectiveMainCamera.gameObject.SetActive(isPerspective);
|
||||
var activeBrain = isPerspective ? _perspectiveCameraBrain : _orthoCameraBrain;
|
||||
|
||||
// 设置虚拟相机优先级
|
||||
foreach (var camPair in _virtualCameraDict)
|
||||
{
|
||||
if (camPair.Key == camType) camPair.Value.Priority = HighPriority;
|
||||
@@ -148,13 +184,44 @@ namespace AibisDream.Framework
|
||||
}
|
||||
|
||||
_curInitState = _camInitStateDict[camType];
|
||||
yield return new WaitForSeconds(_cameraBrain.m_DefaultBlend.BlendTime);
|
||||
var lastCamEnum = _curCameraEnum;
|
||||
_curCameraEnum = camType;
|
||||
|
||||
// 切完之后记得复原
|
||||
ActionKit.Delay(activeBrain.m_DefaultBlend.BlendTime, () => ResetVirCam(lastCamEnum)).StartGlobal();
|
||||
|
||||
return activeBrain.m_DefaultBlend.BlendTime;
|
||||
}
|
||||
|
||||
public void ResetCamera()
|
||||
{
|
||||
_curCameraEnum = CameraEnum.Default;
|
||||
_camInitStateDict.Clear();
|
||||
_camInitStateDict.Clear();
|
||||
}
|
||||
|
||||
private void ResetVirCam(CameraEnum cameraEnum)
|
||||
{
|
||||
if (cameraEnum == CameraEnum.Default)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_virtualCameraDict.TryGetValue(cameraEnum, out var virCam))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var camState = _camInitStateDict[cameraEnum];
|
||||
virCam.VirtualCameraGameObject.transform.localPosition = camState.pos;
|
||||
var camObj = virCam.VirtualCameraGameObject.GetComponent<CinemachineVirtualCamera>();
|
||||
camObj.m_Lens.OrthographicSize = camState.initOrthoSize;
|
||||
}
|
||||
|
||||
public Tween TransCamera(Vector3 targetPos, float targetOrthographicSize, float duration)
|
||||
{
|
||||
// 寻找现在正在激活的相机
|
||||
var curCamera = _cameraBrain.ActiveVirtualCamera;
|
||||
var curCamera = _orthoCameraBrain.ActiveVirtualCamera;
|
||||
|
||||
var sq = DOTween.Sequence();
|
||||
// 移动
|
||||
@@ -165,17 +232,11 @@ namespace AibisDream.Framework
|
||||
return sq;
|
||||
}
|
||||
|
||||
public Tween ReturnInitCamera(float duration)
|
||||
private Tween ReturnInitCamera(float duration)
|
||||
{
|
||||
return TransCamera(_curInitState.pos, _curInitState.initOrthoSize, duration);
|
||||
}
|
||||
|
||||
public IEnumerator TransCameraAsync(Vector3 targetPos, float duration)
|
||||
{
|
||||
var curCamera = _cameraBrain.ActiveVirtualCamera;
|
||||
yield return curCamera.DOLocalMove(targetPos, duration).WaitForCompletion();
|
||||
}
|
||||
|
||||
public IEnumerator TransCameraAsync(Vector3 targetPos, float targetOrthographicSize, float duration)
|
||||
{
|
||||
yield return TransCamera(targetPos, targetOrthographicSize, duration).WaitForCompletion();
|
||||
@@ -190,11 +251,41 @@ namespace AibisDream.Framework
|
||||
[Obsolete]
|
||||
public IEnumerator ShakeCamera(float duration, float strength, int vibrate)
|
||||
{
|
||||
var curCamera = _cameraBrain.ActiveVirtualCamera;
|
||||
var curCamera = _orthoCameraBrain.ActiveVirtualCamera;
|
||||
yield return curCamera.VirtualCameraGameObject.transform
|
||||
.DOShakePosition(duration, strength, vibrate, 90, false, false)
|
||||
.WaitForCompletion();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前激活的相机
|
||||
/// </summary>
|
||||
public Camera GetCurrentCamera()
|
||||
{
|
||||
return _camInitStateDict.TryGetValue(_curCameraEnum, out var state) && state.isPerspective
|
||||
? _perspectiveMainCamera
|
||||
: _orthoMainCamera;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取鼠标在世界中的位置
|
||||
/// </summary>
|
||||
/// <param name="mousePoint">鼠标界面位置</param>
|
||||
/// <returns>世界位置</returns>
|
||||
public static Vector3 GetMouseWorldPos(Vector3 mousePoint)
|
||||
{
|
||||
if (!_mainCam) _mainCam = Camera.main;
|
||||
var pos = _mainCam.ScreenToWorldPoint(mousePoint);
|
||||
return new Vector3(pos.x, pos.y, 0);
|
||||
}
|
||||
|
||||
public static Vector3 GetScreenPos(Vector3 worldPos)
|
||||
{
|
||||
if (!_mainCam) _mainCam = Camera.main;
|
||||
|
||||
worldPos.z = 0;
|
||||
return _mainCam.WorldToScreenPoint(worldPos);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CinemachineExtension
|
||||
@@ -209,7 +300,7 @@ namespace AibisDream.Framework
|
||||
public static TweenerCore<float, float, FloatOptions> DOOrthoSize(this ICinemachineCamera camera,
|
||||
float endValue, float duration)
|
||||
{
|
||||
var virtualCamera = camera.VirtualCameraGameObject.gameObject.GetComponent<CinemachineVirtualCamera>();
|
||||
var virtualCamera = camera.VirtualCameraGameObject.GetComponent<CinemachineVirtualCamera>();
|
||||
var currentOrthoSize = virtualCamera.m_Lens.OrthographicSize;
|
||||
return DOTween.To(() => currentOrthoSize, x => currentOrthoSize = x, endValue, duration)
|
||||
.OnUpdate(() => virtualCamera.m_Lens.OrthographicSize = currentOrthoSize);
|
||||
@@ -231,13 +322,6 @@ namespace AibisDream.Framework
|
||||
return CameraKit.Instance.TransCameraAsync(targetPos, targetOrthographicSize, duration);
|
||||
}
|
||||
|
||||
[YarnCommand("trans_camera_pos")]
|
||||
public static IEnumerator TransCameraAsync(float x, float y, float duration)
|
||||
{
|
||||
Vector3 targetPos = new Vector3(x, y, -10);
|
||||
return CameraKit.Instance.TransCameraAsync(targetPos, duration);
|
||||
}
|
||||
|
||||
[YarnCommand("shake_camera")]
|
||||
public static IEnumerator ShakeCamera(float duration, float strength, int vibrate = 10)
|
||||
{
|
||||
@@ -264,11 +348,28 @@ namespace AibisDream.Framework
|
||||
Clinic,
|
||||
CoolingMachine,
|
||||
BodyModule,
|
||||
BodyModuleCenter,
|
||||
Memory,
|
||||
Logic,
|
||||
Eye,
|
||||
UF,
|
||||
Expression,
|
||||
Engine,
|
||||
Gear,
|
||||
EyeDeep,
|
||||
ExpressionDeep,
|
||||
Emo,
|
||||
EmoDeep,
|
||||
Op,
|
||||
CutEmo,
|
||||
CutMemory,
|
||||
CutLogic,
|
||||
CutEmoDeep,
|
||||
CutMemoryDeep,
|
||||
CutLogicDeep,
|
||||
BreakCamera,
|
||||
DreamCamera,
|
||||
Subway,
|
||||
SubwayTV
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace AibisDream.Framework
|
||||
{
|
||||
public static class FadeKit
|
||||
{
|
||||
public static IEnumerator FadeInAsync(this Image image, float duration)
|
||||
{
|
||||
image.gameObject.SetActive(true);
|
||||
var tweener = image.DOBlendableColor(Color.white, duration);
|
||||
yield return tweener.WaitForCompletion();
|
||||
}
|
||||
|
||||
public static void FadeIn(this Image image, float duration)
|
||||
{
|
||||
image.gameObject.SetActive(true);
|
||||
var tweener = image.DOBlendableColor(Color.white, duration);
|
||||
}
|
||||
|
||||
public static IEnumerator FadeOutAsync(this Image image, float duration)
|
||||
{
|
||||
image.gameObject.SetActive(true);
|
||||
var tweener = image.DOBlendableColor(Color.clear, duration);
|
||||
yield return tweener.WaitForCompletion();
|
||||
image.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
public static void FadeOut(this Image image, float duration)
|
||||
{
|
||||
image.gameObject.SetActive(true);
|
||||
image.DOBlendableColor(Color.clear, duration).onComplete += () =>
|
||||
{
|
||||
image.gameObject.SetActive(false);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09319533608646369f9418994e9c8ae1
|
||||
timeCreated: 1744781879
|
||||
@@ -0,0 +1,141 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization;
|
||||
using UnityEngine.Localization.Settings;
|
||||
using UnityEngine.Localization.Tables;
|
||||
|
||||
namespace AibisDream.Framework
|
||||
{
|
||||
public static class LocalizationKit
|
||||
{
|
||||
private const string LocalizationPrefix = "l10n.";
|
||||
private const string ActorTableName = "ActorName";
|
||||
private const string ParamTableName = "Params";
|
||||
|
||||
private static LocalizationTable _paramTable;
|
||||
|
||||
public static LocalizationTable ActorTable { get; private set; }
|
||||
|
||||
static LocalizationKit()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.playModeStateChanged += OnPlayModeChanged;
|
||||
#else
|
||||
_paramTable = new LocalizationTable(ParamTableName);
|
||||
ActorTable = new LocalizationTable(ActorTableName);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private static void OnPlayModeChanged(PlayModeStateChange playMode)
|
||||
{
|
||||
if (playMode == PlayModeStateChange.EnteredPlayMode)
|
||||
{
|
||||
// 进入游戏模式的时候
|
||||
_paramTable?.Dispose();
|
||||
ActorTable?.Dispose();
|
||||
_paramTable = new LocalizationTable(ParamTableName);
|
||||
ActorTable = new LocalizationTable(ActorTableName);
|
||||
}
|
||||
else if (playMode == PlayModeStateChange.ExitingPlayMode)
|
||||
{
|
||||
_paramTable?.Dispose();
|
||||
ActorTable?.Dispose();
|
||||
_paramTable = null;
|
||||
ActorTable = null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void SwitchLanguage(string localeCode)
|
||||
{
|
||||
var selectedLocale =
|
||||
LocalizationSettings.AvailableLocales.Locales.Find(locale => locale.Identifier.Code == localeCode);
|
||||
|
||||
if (selectedLocale)
|
||||
{
|
||||
LocalizationSettings.SelectedLocale = selectedLocale;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Locale with code {localeCode} not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地化参数
|
||||
/// </summary>
|
||||
/// <param name="param">参数名</param>
|
||||
/// <param name="table">本地化表</param>
|
||||
/// <returns></returns>
|
||||
public static string LocalizeParam(string param, LocalizationTable table)
|
||||
{
|
||||
// 非本地化参数直接返回
|
||||
if (!param.StartsWith(LocalizationPrefix)) return param;
|
||||
|
||||
var localizationKey = param[LocalizationPrefix.Length..];
|
||||
// 获取本地化值
|
||||
if (table.TryGetValue(localizationKey, out var value))
|
||||
{
|
||||
return value.LocalizedValue;
|
||||
}
|
||||
|
||||
// 如果找不到,则使用原始值
|
||||
Debug.LogWarning($"Variable Key '{localizationKey}' not found in localization table.");
|
||||
return localizationKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地化参数
|
||||
/// </summary>
|
||||
/// <param name="param"></param>
|
||||
/// <returns></returns>
|
||||
public static string LocalizeParam(string param)
|
||||
{
|
||||
return LocalizeParam(param, _paramTable);
|
||||
}
|
||||
|
||||
public static string GetL10NParamKey(string key)
|
||||
{
|
||||
// 本地化参数则去除前缀,非本地化参数直接返回
|
||||
return key.StartsWith(LocalizationPrefix) ? key[LocalizationPrefix.Length..] : key;
|
||||
}
|
||||
}
|
||||
|
||||
public class LocalizationTable
|
||||
{
|
||||
public string TableName { get; }
|
||||
|
||||
private StringTable _stringTable;
|
||||
|
||||
public LocalizationTable(string tableName)
|
||||
{
|
||||
TableName = tableName;
|
||||
_stringTable = LocalizationSettings.StringDatabase.GetTable(TableName);
|
||||
LocalizationSettings.SelectedLocaleChanged += OnLocaleChanged;
|
||||
|
||||
Debug.Log($"表格{_stringTable.TableCollectionName}已加载");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LocalizationSettings.SelectedLocaleChanged -= OnLocaleChanged;
|
||||
_stringTable = null;
|
||||
}
|
||||
|
||||
private void OnLocaleChanged(Locale locale)
|
||||
{
|
||||
_stringTable = LocalizationSettings.StringDatabase.GetTable(TableName);
|
||||
}
|
||||
|
||||
public StringTableEntry this[string key] => _stringTable[key];
|
||||
|
||||
public bool TryGetValue(string key, out StringTableEntry value)
|
||||
{
|
||||
value = _stringTable.GetEntry(key);
|
||||
return value != null;
|
||||
}
|
||||
|
||||
public LocaleIdentifier LocaleIdentifier => _stringTable.LocaleIdentifier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd25d4c834624b57984f2157197507e8
|
||||
timeCreated: 1745481747
|
||||
@@ -40,14 +40,16 @@ namespace AibisDream.Framework
|
||||
OnUpdate -= kit.OnUpdate;
|
||||
}
|
||||
|
||||
private void Update() => OnUpdate?.Invoke();
|
||||
private void Update()
|
||||
{
|
||||
OnUpdate?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public class TransTest : IMonoKit
|
||||
{
|
||||
public void OnUpdate()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AibisDream.Framework
|
||||
{
|
||||
public class RandomSelector<T> : ISelector<T>
|
||||
{
|
||||
private readonly List<T> _items;
|
||||
private readonly List<int> _prefixSums;
|
||||
private readonly int _totalWeight;
|
||||
private readonly Random _random;
|
||||
|
||||
public int Count => _items.Count;
|
||||
|
||||
public RandomSelector(IEnumerable<T> items, Func<T, int> weightSelector)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException(nameof(items));
|
||||
if (weightSelector == null) throw new ArgumentNullException(nameof(weightSelector));
|
||||
|
||||
var itemList = items.ToList();
|
||||
_items = new List<T>(itemList.Count);
|
||||
_prefixSums = new List<int>(itemList.Count);
|
||||
|
||||
int sum = 0;
|
||||
foreach (var item in itemList)
|
||||
{
|
||||
int weight = weightSelector(item);
|
||||
if (weight < 0)
|
||||
throw new ArgumentException("Weights cannot be negative", nameof(items));
|
||||
|
||||
if (weight > 0)
|
||||
{
|
||||
sum += weight;
|
||||
_prefixSums.Add(sum);
|
||||
_items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
_totalWeight = sum;
|
||||
_random = new Random();
|
||||
}
|
||||
|
||||
public T Select()
|
||||
{
|
||||
if (_totalWeight == 0)
|
||||
throw new InvalidOperationException("All weights are zero");
|
||||
|
||||
int randomValue = _random.Next(0, _totalWeight);
|
||||
int index = BinarySearch(randomValue);
|
||||
return _items[index];
|
||||
}
|
||||
|
||||
private int BinarySearch(int value)
|
||||
{
|
||||
int left = 0;
|
||||
int right = _prefixSums.Count - 1;
|
||||
|
||||
while (left < right)
|
||||
{
|
||||
int mid = left + (right - left) / 2;
|
||||
if (_prefixSums[mid] <= value)
|
||||
{
|
||||
left = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
right = mid;
|
||||
}
|
||||
}
|
||||
|
||||
return left;
|
||||
}
|
||||
}
|
||||
|
||||
public class BatchSelector<T> : ISelector<T>
|
||||
{
|
||||
private readonly List<T> _items;
|
||||
private readonly List<int> _batchWeight;
|
||||
private readonly int _totalWeight;
|
||||
private readonly Random _random;
|
||||
|
||||
private int _currentWeight;
|
||||
private List<int> _curBatchWeight;
|
||||
|
||||
public int Count => _totalWeight;
|
||||
|
||||
public BatchSelector(IEnumerable<T> items, Func<T, int> weightSelector)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException(nameof(items));
|
||||
if (weightSelector == null) throw new ArgumentNullException(nameof(weightSelector));
|
||||
|
||||
var itemList = items.ToList();
|
||||
_items = new List<T>(itemList.Count);
|
||||
_batchWeight = new List<int>(itemList.Count);
|
||||
|
||||
int sum = 0;
|
||||
foreach (var item in itemList)
|
||||
{
|
||||
int weight = weightSelector(item);
|
||||
if (weight < 0)
|
||||
throw new ArgumentException("Weights cannot be negative", nameof(items));
|
||||
|
||||
if (weight > 0)
|
||||
{
|
||||
sum += weight;
|
||||
_batchWeight.Add(weight);
|
||||
_items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
_curBatchWeight = new List<int>(_batchWeight);
|
||||
|
||||
_totalWeight = sum;
|
||||
_currentWeight = sum;
|
||||
_random = new Random();
|
||||
|
||||
if (_totalWeight == 0)
|
||||
{
|
||||
// 出错了
|
||||
throw new InvalidOperationException("All weights are zero");
|
||||
}
|
||||
}
|
||||
|
||||
public T Select()
|
||||
{
|
||||
// 如果已经抽完就重置
|
||||
if (_currentWeight == 0)
|
||||
{
|
||||
ResetSelector();
|
||||
}
|
||||
|
||||
int randomValue = _random.Next(0, _currentWeight);
|
||||
int index = BinarySearch(randomValue);
|
||||
|
||||
// 选择后减去权重
|
||||
_currentWeight -= 1;
|
||||
_curBatchWeight[index] -= 1;
|
||||
|
||||
return _items[index];
|
||||
}
|
||||
|
||||
private void ResetSelector()
|
||||
{
|
||||
_currentWeight = _totalWeight;
|
||||
_curBatchWeight = new List<int>(_batchWeight);
|
||||
}
|
||||
|
||||
private int BinarySearch(int value)
|
||||
{
|
||||
var sum = 0;
|
||||
for (var i = 0; i < _curBatchWeight.Count; i++)
|
||||
{
|
||||
sum += _curBatchWeight[i];
|
||||
if (sum >= value)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return _curBatchWeight.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public interface ISelector<out T>
|
||||
{
|
||||
T Select();
|
||||
int Count { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70b0a1ccda984fecbcfdc4d196e0453b
|
||||
timeCreated: 1748522566
|
||||
@@ -0,0 +1,17 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace AibisDream.Framework
|
||||
{
|
||||
public class UICamera : MonoBehaviour
|
||||
{
|
||||
private void Awake()
|
||||
{
|
||||
CameraKit.Instance.RegisterUICamera(GetComponent<Camera>());
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
CameraKit.Instance.UnRegisterUICamera(GetComponent<Camera>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 061bb2d8e88b4f7c96c1a1b10e32d6eb
|
||||
timeCreated: 1749649908
|
||||
Reference in New Issue
Block a user