首次提交
This commit is contained in:
53
Assets/Scripts/Common/Utils/AlgorithmUtils.cs
Normal file
53
Assets/Scripts/Common/Utils/AlgorithmUtils.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class AlgorithmUtils
|
||||
{
|
||||
#region 快速排序
|
||||
|
||||
public static void QuickSortRecursive<T>(IList<T> dataList, int left, int right, Comparison<T> comparer)
|
||||
{
|
||||
if (dataList == null)
|
||||
throw new ArgumentNullException();
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
if (left >= right)
|
||||
return;
|
||||
int mid = QuickSortPatition(dataList, left, right, comparer);
|
||||
QuickSortRecursive(dataList, left, mid - 1, comparer);
|
||||
QuickSortRecursive(dataList, mid + 1, right, comparer);
|
||||
}
|
||||
|
||||
private static int QuickSortPatition<T>(IList<T> dataList, int left, int right, Comparison<T> comparer)
|
||||
{
|
||||
int j = left;
|
||||
int i = j - 1;
|
||||
T equipKey = dataList[right]; //基准元素
|
||||
for (; j < right; ++j)
|
||||
{
|
||||
if (comparer(dataList[j], equipKey) < 0)
|
||||
{
|
||||
SwapData(dataList, j, ++i);
|
||||
}
|
||||
}
|
||||
|
||||
//把right交换到中间
|
||||
T t = dataList[right];
|
||||
dataList[right] = dataList[++i];
|
||||
dataList[i] = t;
|
||||
return i;
|
||||
}
|
||||
|
||||
private static void SwapData<T>(IList<T> dataList, int x, int y)
|
||||
{
|
||||
T tmp = dataList[x];
|
||||
dataList[x] = dataList[y];
|
||||
dataList[y] = tmp;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/AlgorithmUtils.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/AlgorithmUtils.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7bd3eec7be141b188178d45a3c3c0c6
|
||||
timeCreated: 1742309728
|
||||
164
Assets/Scripts/Common/Utils/ButtonNavigate.cs
Normal file
164
Assets/Scripts/Common/Utils/ButtonNavigate.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System.Collections.Generic;
|
||||
using FairyGUI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public class ButtonNavigate
|
||||
{
|
||||
private readonly List<GButton> _buttons = new List<GButton>();
|
||||
private int _currentIndex = 0;
|
||||
|
||||
public ButtonNavigate(List<GButton> buttons, bool selectFist = true)
|
||||
{
|
||||
_buttons.AddRange(buttons);
|
||||
if (selectFist && _buttons.Count > 0)
|
||||
{
|
||||
SetButtonSelect(buttons[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Up()
|
||||
{
|
||||
Navigate(Vector2.up);
|
||||
}
|
||||
|
||||
public void Down()
|
||||
{
|
||||
Navigate(Vector2.down);
|
||||
}
|
||||
|
||||
public void Left()
|
||||
{
|
||||
Navigate(Vector2.left);
|
||||
}
|
||||
|
||||
public void Right()
|
||||
{
|
||||
Navigate(Vector2.right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点击选中按钮
|
||||
/// </summary>
|
||||
public void Click()
|
||||
{
|
||||
foreach (var button in _buttons)
|
||||
{
|
||||
if (button.selected)
|
||||
{
|
||||
button.Click();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Navigate(Vector2 direction)
|
||||
{
|
||||
GButton current = _buttons[_currentIndex];
|
||||
Rect currentRect = GetGlobalRect(current);
|
||||
|
||||
GButton bestCandidate = null;
|
||||
float bestDistance = float.MaxValue;
|
||||
|
||||
foreach (var candidate in _buttons)
|
||||
{
|
||||
if (candidate == current) continue;
|
||||
|
||||
Rect candidateRect = GetGlobalRect(candidate);
|
||||
|
||||
// 检查方向匹配性
|
||||
bool isDirectionMatch = false;
|
||||
|
||||
if (direction == Vector2.up) // 上方向
|
||||
{
|
||||
isDirectionMatch = candidateRect.yMax <= currentRect.yMin &&
|
||||
RectOverlapX(currentRect, candidateRect);
|
||||
}
|
||||
else if (direction == Vector2.down) // 下方向
|
||||
{
|
||||
isDirectionMatch = candidateRect.yMin >= currentRect.yMax &&
|
||||
RectOverlapX(currentRect, candidateRect);
|
||||
}
|
||||
else if (direction == Vector2.left) // 左方向
|
||||
{
|
||||
isDirectionMatch = candidateRect.xMax <= currentRect.xMin &&
|
||||
RectOverlapY(currentRect, candidateRect);
|
||||
}
|
||||
else if (direction == Vector2.right) // 右方向
|
||||
{
|
||||
isDirectionMatch = candidateRect.xMin >= currentRect.xMax &&
|
||||
RectOverlapY(currentRect, candidateRect);
|
||||
}
|
||||
|
||||
if (isDirectionMatch)
|
||||
{
|
||||
float distance = Vector2.Distance(currentRect.center, candidateRect.center);
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
bestCandidate = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestCandidate != null)
|
||||
{
|
||||
_currentIndex = _buttons.IndexOf(bestCandidate);
|
||||
SetButtonSelect(bestCandidate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取按钮在根坐标空间中的矩形区域
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
private Rect GetGlobalRect(GObject obj)
|
||||
{
|
||||
// 获取对象在根组件中的全局位置
|
||||
Vector2 globalPos = obj.LocalToGlobal(Vector2.zero);
|
||||
Vector2 rootPos = GRoot.inst.GlobalToLocal(globalPos);
|
||||
|
||||
// 考虑对象的缩放
|
||||
float width = obj.width * obj.scaleX;
|
||||
float height = obj.height * obj.scaleY;
|
||||
|
||||
return new Rect(rootPos.x, rootPos.y, width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查两个矩形在X轴上的重叠
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
private bool RectOverlapX(Rect a, Rect b)
|
||||
{
|
||||
return !(a.xMin >= b.xMax || a.xMax <= b.xMin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查两个矩形在Y轴上的重叠
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
private bool RectOverlapY(Rect a, Rect b)
|
||||
{
|
||||
return !(a.yMin >= b.yMax || a.yMax <= b.yMin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置按钮选中
|
||||
/// </summary>
|
||||
/// <param name="button"></param>
|
||||
private void SetButtonSelect(GButton button)
|
||||
{
|
||||
foreach (var btn in _buttons)
|
||||
{
|
||||
btn.selected = btn == button;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/ButtonNavigate.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/ButtonNavigate.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83f029b458614e419234114c3d90f1fc
|
||||
timeCreated: 1749009573
|
||||
38
Assets/Scripts/Common/Utils/CameraUtils.cs
Normal file
38
Assets/Scripts/Common/Utils/CameraUtils.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class CameraUtils
|
||||
{
|
||||
public static void SetOverlay(Camera camera, CameraRenderType cameraRenderType = CameraRenderType.Overlay)
|
||||
{
|
||||
var stageCameraData = camera.GetUniversalAdditionalCameraData();
|
||||
if (stageCameraData != null)
|
||||
{
|
||||
stageCameraData.renderType = cameraRenderType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void AddStack(this Camera camera, Camera overlayCamera)
|
||||
{
|
||||
var MainCameraData = camera.GetUniversalAdditionalCameraData();
|
||||
if (!MainCameraData.cameraStack.Contains(overlayCamera)) // 防止重复添加
|
||||
{
|
||||
SetOverlay(overlayCamera);
|
||||
MainCameraData.cameraStack.Add(overlayCamera);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveStack(this Camera camera, Camera overlayCamera)
|
||||
{
|
||||
var MainCameraData = camera.GetUniversalAdditionalCameraData();
|
||||
if (MainCameraData.cameraStack.Contains(overlayCamera))
|
||||
{
|
||||
MainCameraData.cameraStack.Remove(overlayCamera);
|
||||
SetOverlay(overlayCamera, CameraRenderType.Base);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/CameraUtils.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/CameraUtils.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa3a85fdab024387a20ac51e8e79a438
|
||||
timeCreated: 1742642984
|
||||
75
Assets/Scripts/Common/Utils/Cryptor.cs
Normal file
75
Assets/Scripts/Common/Utils/Cryptor.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
namespace NBF
|
||||
{
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
public class Cryptor
|
||||
{
|
||||
private const string ENCRYPTION_KEY = "721e51s73er2i3ds50a41exe123abce9";
|
||||
|
||||
public static string Hash(string value)
|
||||
{
|
||||
return GetSha1Hash(value);
|
||||
}
|
||||
|
||||
public static string Encrypt(string value)
|
||||
{
|
||||
if (value == null || value.Length < 1)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return GetEncryptedString(value);
|
||||
}
|
||||
|
||||
public static string Decrypt(string value)
|
||||
{
|
||||
if (value == null || value.Length < 1)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return GetDecryptedString(value);
|
||||
}
|
||||
|
||||
private static string GetSha1Hash(string strToEncrypt)
|
||||
{
|
||||
byte[] bytes = new UTF8Encoding().GetBytes(strToEncrypt);
|
||||
byte[] array = new SHA1CryptoServiceProvider().ComputeHash(bytes);
|
||||
string text = "";
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
text += Convert.ToString(array[i], 16).PadLeft(2, '0');
|
||||
}
|
||||
|
||||
return text.PadLeft(32, '0');
|
||||
}
|
||||
|
||||
private static string GetEncryptedString(string toEncrypt)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes("721e51s73er2i3ds50a41exe123abce9");
|
||||
byte[] bytes2 = Encoding.UTF8.GetBytes(toEncrypt);
|
||||
byte[] array = new RijndaelManaged
|
||||
{
|
||||
Key = bytes,
|
||||
Mode = CipherMode.ECB,
|
||||
Padding = PaddingMode.PKCS7
|
||||
}.CreateEncryptor().TransformFinalBlock(bytes2, 0, bytes2.Length);
|
||||
return Convert.ToBase64String(array, 0, array.Length);
|
||||
}
|
||||
|
||||
private static string GetDecryptedString(string toDecrypt)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes("721e51s73er2i3ds50a41exe123abce9");
|
||||
byte[] array = Convert.FromBase64String(toDecrypt);
|
||||
byte[] bytes2 = new RijndaelManaged
|
||||
{
|
||||
Key = bytes,
|
||||
Mode = CipherMode.ECB,
|
||||
Padding = PaddingMode.PKCS7
|
||||
}.CreateDecryptor().TransformFinalBlock(array, 0, array.Length);
|
||||
return Encoding.UTF8.GetString(bytes2);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/Cryptor.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/Cryptor.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d727e52faea74ce8a969e90353dc1bec
|
||||
timeCreated: 1742311501
|
||||
142
Assets/Scripts/Common/Utils/DataArrayExtends.cs
Normal file
142
Assets/Scripts/Common/Utils/DataArrayExtends.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class DataArrayExtends
|
||||
{
|
||||
public static T Find<T>(this IList<T> obj, Predicate<T> match)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException();
|
||||
if (match == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
for (int i = 0; i < obj.Count; i++)
|
||||
{
|
||||
if (match(obj[i]))
|
||||
{
|
||||
return obj[i];
|
||||
}
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public static int FindIndex<T>(this IList<T> obj, Predicate<T> match)
|
||||
{
|
||||
return FindIndex(obj, 0, obj.Count, match);
|
||||
}
|
||||
|
||||
public static int FindIndex<T>(this IList<T> obj, int startIndex, int count, Predicate<T> match)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException();
|
||||
if (startIndex < 0 || startIndex > count)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
if (count < 0 || startIndex > obj.Count - count)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
int num = startIndex + count;
|
||||
for (int i = startIndex; i < num; i++)
|
||||
{
|
||||
if (match(obj[i]))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static void ForEach<T>(this IList<T> obj, Action<T> action)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException();
|
||||
if (action == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
for (int i = 0; i < obj.Count; i++)
|
||||
{
|
||||
action(obj[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<T> FindAll<T>(this IList<T> obj, Predicate<T> match)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException();
|
||||
if (match == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
List<T> list = new List<T>();
|
||||
for (int i = 0; i < obj.Count; i++)
|
||||
{
|
||||
if (match(obj[i]))
|
||||
list.Add(obj[i]);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static int RemoveAll<T>(this IList<T> obj, Predicate<T> match)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException();
|
||||
if (match == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
int originalSize = obj.Count;
|
||||
int index = 0;
|
||||
while (index < obj.Count)
|
||||
{
|
||||
if (match(obj[index]))
|
||||
{
|
||||
obj.RemoveAt(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return originalSize - obj.Count;
|
||||
}
|
||||
|
||||
public static void Sort<T>(this IList<T> obj, Comparison<T> comparison)
|
||||
{
|
||||
Sort(obj, 0, obj.Count, comparison);
|
||||
}
|
||||
|
||||
public static void Sort<T>(this IList<T> obj, int index, int count, Comparison<T> comparison)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException();
|
||||
if (index < 0 || count < 0)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
if (obj.Count - index < count)
|
||||
throw new ArgumentException();
|
||||
|
||||
AlgorithmUtils.QuickSortRecursive(obj, index, count - 1, comparison);
|
||||
}
|
||||
|
||||
public static void RemoveRange<T>(this IList<T> obj, int index, int count)
|
||||
{
|
||||
var list = new List<T>(obj);
|
||||
list.RemoveRange(index, count);
|
||||
obj.Clear();
|
||||
obj.AddRange(list);
|
||||
}
|
||||
|
||||
public static void Overlay<T>(this IList<T> obj, IEnumerable<T> collection)
|
||||
{
|
||||
if (obj != collection)
|
||||
{
|
||||
obj.Clear();
|
||||
obj.AddRange(collection);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/DataArrayExtends.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/DataArrayExtends.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 207d2029894f4e3595d3b1a244cae68d
|
||||
timeCreated: 1742309720
|
||||
9
Assets/Scripts/Common/Utils/DontDestroy.cs
Normal file
9
Assets/Scripts/Common/Utils/DontDestroy.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class DontDestroy : MonoBehaviour
|
||||
{
|
||||
void Awake()
|
||||
{
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/DontDestroy.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/DontDestroy.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b189222c6db8433db1239d314f92cf9f
|
||||
timeCreated: 1742309743
|
||||
3
Assets/Scripts/Common/Utils/Extends.meta
Normal file
3
Assets/Scripts/Common/Utils/Extends.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14814175084d45b39b84b2fccc0a5dc1
|
||||
timeCreated: 1744904278
|
||||
263
Assets/Scripts/Common/Utils/Extends/Extensions.cs
Normal file
263
Assets/Scripts/Common/Utils/Extends/Extensions.cs
Normal file
@@ -0,0 +1,263 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the square of the given value.
|
||||
/// </summary>
|
||||
|
||||
public static int square(this int value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the square of the given value.
|
||||
/// </summary>
|
||||
|
||||
public static float square(this float value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether value is near to zero within a tolerance.
|
||||
/// </summary>
|
||||
|
||||
public static bool isZero(this float value)
|
||||
{
|
||||
const float kTolerance = 0.0000000001f;
|
||||
|
||||
return Mathf.Abs(value) < kTolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only X component of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyX(this Vector3 vector3)
|
||||
{
|
||||
vector3.y = 0.0f;
|
||||
vector3.z = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only Y component of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyY(this Vector3 vector3)
|
||||
{
|
||||
vector3.x = 0.0f;
|
||||
vector3.z = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only Z component of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyZ(this Vector3 vector3)
|
||||
{
|
||||
vector3.x = 0.0f;
|
||||
vector3.y = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only X and Y components of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyXY(this Vector3 vector3)
|
||||
{
|
||||
vector3.z = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with only X and Z components of the vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 onlyXZ(this Vector3 vector3)
|
||||
{
|
||||
vector3.y = 0.0f;
|
||||
|
||||
return vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether vector is near to zero within a tolerance.
|
||||
/// </summary>
|
||||
|
||||
public static bool isZero(this Vector2 vector2)
|
||||
{
|
||||
return vector2.sqrMagnitude < 9.99999943962493E-11;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether vector is near to zero within a tolerance.
|
||||
/// </summary>
|
||||
|
||||
public static bool isZero(this Vector3 vector3)
|
||||
{
|
||||
return vector3.sqrMagnitude < 9.99999943962493E-11;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether vector is exceeding the magnitude within a small error tolerance.
|
||||
/// </summary>
|
||||
|
||||
public static bool isExceeding(this Vector3 vector3, float magnitude)
|
||||
{
|
||||
// Allow 1% error tolerance, to account for numeric imprecision.
|
||||
|
||||
const float kErrorTolerance = 1.01f;
|
||||
|
||||
return vector3.sqrMagnitude > magnitude * magnitude * kErrorTolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with a magnitude of 1,
|
||||
/// and outs its magnitude before normalization.
|
||||
///
|
||||
/// If the vector is too small to be normalized a zero vector will be returned.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 normalized(this Vector3 vector3, out float magnitude)
|
||||
{
|
||||
magnitude = vector3.magnitude;
|
||||
if (magnitude > 9.99999974737875E-06)
|
||||
return vector3 / magnitude;
|
||||
|
||||
magnitude = 0.0f;
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dot product of two vectors.
|
||||
/// </summary>
|
||||
|
||||
public static float dot(this Vector3 vector3, Vector3 otherVector3)
|
||||
{
|
||||
return Vector3.Dot(vector3, otherVector3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector projected onto normal vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 projectedOn(this Vector3 thisVector, Vector3 normal)
|
||||
{
|
||||
return Vector3.Project(thisVector, normal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector projected onto a plane defined by a normal orthogonal to the plane.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 projectedOnPlane(this Vector3 thisVector, Vector3 planeNormal)
|
||||
{
|
||||
return Vector3.ProjectOnPlane(thisVector, planeNormal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector with its magnitude clamped to maxLength.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 clampedTo(this Vector3 vector3, float maxLength)
|
||||
{
|
||||
return Vector3.ClampMagnitude(vector3, maxLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector perpendicular to other vector.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 perpendicularTo(this Vector3 thisVector, Vector3 otherVector)
|
||||
{
|
||||
return Vector3.Cross(thisVector, otherVector).normalized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of given vector adjusted to be tangent to a specified surface normal relatively to given up axis.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 tangentTo(this Vector3 thisVector, Vector3 normal, Vector3 up)
|
||||
{
|
||||
Vector3 r = thisVector.perpendicularTo(up);
|
||||
Vector3 t = normal.perpendicularTo(r);
|
||||
|
||||
return t * thisVector.magnitude;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a vector to be relative to given transform.
|
||||
/// If isPlanar == true, the transform will be applied on the plane defined by world up axis.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 relativeTo(this Vector3 vector3, Transform relativeToThis, bool isPlanar = true)
|
||||
{
|
||||
Vector3 forward = relativeToThis.forward;
|
||||
|
||||
if (isPlanar)
|
||||
{
|
||||
Vector3 upAxis = Vector3.up;
|
||||
forward = forward.projectedOnPlane(upAxis);
|
||||
|
||||
if (forward.isZero())
|
||||
forward = Vector3.ProjectOnPlane(relativeToThis.up, upAxis);
|
||||
}
|
||||
|
||||
Quaternion q = Quaternion.LookRotation(forward);
|
||||
|
||||
return q * vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a vector to be relative to given transform.
|
||||
/// If isPlanar == true, the transform will be applied on the plane defined by upAxis.
|
||||
/// </summary>
|
||||
|
||||
public static Vector3 relativeTo(this Vector3 vector3, Transform relativeToThis, Vector3 upAxis, bool isPlanar = true)
|
||||
{
|
||||
Vector3 forward = relativeToThis.forward;
|
||||
|
||||
if (isPlanar)
|
||||
{
|
||||
forward = Vector3.ProjectOnPlane(forward, upAxis);
|
||||
|
||||
if (forward.isZero())
|
||||
forward = Vector3.ProjectOnPlane(relativeToThis.up, upAxis);
|
||||
}
|
||||
|
||||
Quaternion q = Quaternion.LookRotation(forward, upAxis);
|
||||
|
||||
return q * vector3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the given quaternion pitch rotation between the given minPitchAngle and maxPitchAngle.
|
||||
/// </summary>
|
||||
|
||||
public static Quaternion clampPitch(this Quaternion quaternion, float minPitchAngle, float maxPitchAngle)
|
||||
{
|
||||
quaternion.x /= quaternion.w;
|
||||
quaternion.y /= quaternion.w;
|
||||
quaternion.z /= quaternion.w;
|
||||
quaternion.w = 1.0f;
|
||||
|
||||
float pitch = Mathf.Clamp(2.0f * Mathf.Rad2Deg * Mathf.Atan(quaternion.x), minPitchAngle, maxPitchAngle);
|
||||
|
||||
quaternion.x = Mathf.Tan(pitch * 0.5f * Mathf.Deg2Rad);
|
||||
|
||||
return quaternion;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/Extends/Extensions.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/Extends/Extensions.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3308733cfb774828bac0a5740bedd645
|
||||
timeCreated: 1748527846
|
||||
13
Assets/Scripts/Common/Utils/Extends/FairyGUIExtensions.cs
Normal file
13
Assets/Scripts/Common/Utils/Extends/FairyGUIExtensions.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using FairyGUI;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class FairyGUIExtensions
|
||||
{
|
||||
public static void Click(this GButton button)
|
||||
{
|
||||
button.FireClick(true);
|
||||
button.onClick.Call();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f9d43645df348cc8e3e498a7570cd2f
|
||||
timeCreated: 1749008303
|
||||
34
Assets/Scripts/Common/Utils/Extends/ListExtends.cs
Normal file
34
Assets/Scripts/Common/Utils/Extends/ListExtends.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using NBF.Setting;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class ListExtends
|
||||
{
|
||||
public static void SortBySortAttribute(this List<OptionBase> list)
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
|
||||
list.Sort((a, b) =>
|
||||
{
|
||||
var orderA = GetSortOrder(a);
|
||||
var orderB = GetSortOrder(b);
|
||||
return orderA.CompareTo(orderB);
|
||||
});
|
||||
}
|
||||
|
||||
private static int GetSortOrder(OptionBase option)
|
||||
{
|
||||
if (option == null)
|
||||
return 999;
|
||||
|
||||
var sortAttribute = option.GetType()
|
||||
.GetCustomAttribute<SortAttribute>(inherit: false);
|
||||
|
||||
return sortAttribute?.Sort ?? 999;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/Extends/ListExtends.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/Extends/ListExtends.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87135425aeb24b8ead2f411dceabb955
|
||||
timeCreated: 1748591603
|
||||
11
Assets/Scripts/Common/Utils/Extends/StringExtends.cs
Normal file
11
Assets/Scripts/Common/Utils/Extends/StringExtends.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace NBF
|
||||
{
|
||||
public static class StringExtends
|
||||
{
|
||||
public static string GetLastString(this string str)
|
||||
{
|
||||
int lastIndex = str.LastIndexOf('_');
|
||||
return str.Substring(lastIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c73591289b8944f6b5a2c50787cba505
|
||||
timeCreated: 1744904286
|
||||
94
Assets/Scripts/Common/Utils/FishWeightToLength.cs
Normal file
94
Assets/Scripts/Common/Utils/FishWeightToLength.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public class FishWeightToLength : MonoBehaviour
|
||||
{
|
||||
[Serializable]
|
||||
public class FishWeightData
|
||||
{
|
||||
[SerializeField] private string FishName = "Nazwa rybki";
|
||||
|
||||
public FishSpecies species;
|
||||
|
||||
[Tooltip("X - waga ryby, Y - centymetry")]
|
||||
public Vector2[] weightLenghtValues;
|
||||
|
||||
public AnimationCurve weightLengthCurve;
|
||||
|
||||
public void SetupCurvesWeight()
|
||||
{
|
||||
weightLengthCurve.keys = null;
|
||||
for (int i = 0; i < weightLenghtValues.Length; i++)
|
||||
{
|
||||
weightLengthCurve.AddKey(weightLenghtValues[i].x, weightLenghtValues[i].y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static FishWeightToLength instance;
|
||||
|
||||
[SerializeField] [Tooltip("Uzywac tylko w edytorze")]
|
||||
private bool isEditMode = true;
|
||||
|
||||
public FishWeightData[] fishWeightData;
|
||||
|
||||
[Tooltip("Testowanie poprawnosci konwersji waga/centymetry")] [SerializeField]
|
||||
private FishSpecies TestSpecies;
|
||||
|
||||
[SerializeField] private float TestWeight;
|
||||
|
||||
[SerializeField] private float TestWynikCentymetry;
|
||||
|
||||
public static FishWeightToLength Instance => instance;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
DontDestroyOnLoad(this);
|
||||
if (instance == null)
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
else if (instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!isEditMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < fishWeightData.Length; i++)
|
||||
{
|
||||
fishWeightData[i].SetupCurvesWeight();
|
||||
if (TestSpecies == fishWeightData[i].species)
|
||||
{
|
||||
TestWynikCentymetry = fishWeightData[i].weightLengthCurve.Evaluate(TestWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float ConvertWeightFishToLength(FishSpecies species, float weight)
|
||||
{
|
||||
for (int i = 0; i < fishWeightData.Length; i++)
|
||||
{
|
||||
if (fishWeightData[i].species == species)
|
||||
{
|
||||
fishWeightData[i].SetupCurvesWeight();
|
||||
return fishWeightData[i].weightLengthCurve.Evaluate(weight);
|
||||
}
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/FishWeightToLength.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/FishWeightToLength.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89c60af8c5b54404b9a45748e3e3f9b9
|
||||
timeCreated: 1742311706
|
||||
257
Assets/Scripts/Common/Utils/FishingTrajectoryCalculator.cs
Normal file
257
Assets/Scripts/Common/Utils/FishingTrajectoryCalculator.cs
Normal file
@@ -0,0 +1,257 @@
|
||||
// using UnityEngine;
|
||||
// using System.Collections.Generic;
|
||||
//
|
||||
// namespace NBF
|
||||
// {
|
||||
// public class LureTrajectorySimulator : MonoBehaviour
|
||||
// {
|
||||
// [Header("基础参数")] public int maxSimulationSteps = 1000;
|
||||
// public float simulationDuration = 10f;
|
||||
// public float timeStep = 0.02f;
|
||||
//
|
||||
// [Header("抛投力度")] public float minThrowPower = 15f;
|
||||
// public float maxThrowPower = 45f;
|
||||
//
|
||||
// [Header("空气阻力参数")] [Tooltip("阻力系数,非流线钓饵建议 0.8~1.2")]
|
||||
// public float dragCoefficient = 0.2f;
|
||||
//
|
||||
// [Tooltip("迎风面积,0.005m² ≈ 5cm²")] public float lureArea = 0.001f;
|
||||
//
|
||||
//
|
||||
// public List<Vector3> CalculateTrajectory(FishingLureCastInput input, Vector3 startPosition,
|
||||
// Vector3 castDirection)
|
||||
// {
|
||||
// List<Vector3> trajectory = new List<Vector3>();
|
||||
//
|
||||
// Vector3 position = startPosition;
|
||||
// Vector3 direction = castDirection.normalized;
|
||||
// float throwPower = Mathf.Lerp(minThrowPower, maxThrowPower, input.power);
|
||||
// Vector3 velocity = direction * throwPower;
|
||||
//
|
||||
// float lureMass = input.lureWeight / 1000f; // 转 kg
|
||||
// Vector3 windDir = input.windDirection.normalized;
|
||||
// float windStrength = input.windStrength;
|
||||
//
|
||||
// float currentTime = 0f;
|
||||
// int steps = 0;
|
||||
//
|
||||
// while (currentTime < simulationDuration && steps < maxSimulationSteps)
|
||||
// {
|
||||
// if (position.y <= 0f) break;
|
||||
//
|
||||
// // 模拟风力逐渐生效
|
||||
// float windInfluenceFactor = Mathf.Clamp01(currentTime / 1.5f); // 1.5秒内增长
|
||||
// Vector3 windVelocity = windDir * windStrength * windInfluenceFactor;
|
||||
//
|
||||
// // 真实空气阻力模型
|
||||
// Vector3 relVelocity = velocity - windVelocity;
|
||||
//
|
||||
// // 空气阻力
|
||||
// float dragMag = 0.5f * PhysicsHelper.AirDensity *
|
||||
// relVelocity.sqrMagnitude *
|
||||
// dragCoefficient * lureArea;
|
||||
//
|
||||
//
|
||||
// // --- 钓线空气阻力模拟 ---
|
||||
// // 假设飞行中展开的线长度近似为当前位置的XZ平面长度
|
||||
// float lineLength = Vector3.Distance(new Vector3(position.x, 0, position.z),
|
||||
// new Vector3(startPosition.x, 0, startPosition.z));
|
||||
// float lineRadius = input.lineDiameter / 2000f; // mm转m再除以2得到半径
|
||||
//
|
||||
// // 钓线的迎风面积估算:长度 * 直径
|
||||
// float lineArea = lineLength * (lineRadius * 2f); // 近似为圆柱体侧面积的一部分
|
||||
//
|
||||
// // 简化模型:线的附加空气阻力方向与当前速度方向相反
|
||||
// float lineDragMag = 0.5f * PhysicsHelper.AirDensity * velocity.sqrMagnitude * dragCoefficient *
|
||||
// lineArea;
|
||||
// Vector3 lineDragForce = -velocity.normalized * lineDragMag;
|
||||
//
|
||||
//
|
||||
// Vector3 dragForce = -relVelocity.normalized * dragMag;
|
||||
//
|
||||
// // 合力 = 重力 + 空气阻力
|
||||
// // Vector3 acceleration = (Physics.gravity + dragForce / lureMass);
|
||||
// Vector3 totalForce = dragForce + lineDragForce;
|
||||
// // 合力 = 重力 + 空气阻力 + 线阻力
|
||||
// Vector3 acceleration = (Physics.gravity + totalForce / lureMass);
|
||||
//
|
||||
//
|
||||
// velocity += acceleration * timeStep;
|
||||
// position += velocity * timeStep;
|
||||
//
|
||||
// trajectory.Add(position);
|
||||
// currentTime += timeStep;
|
||||
// steps++;
|
||||
// }
|
||||
//
|
||||
// return trajectory;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // 示例调用
|
||||
// private List<Vector3> _trajectory;
|
||||
//
|
||||
// private int _windIndex;
|
||||
//
|
||||
// public List<Vector3> Test(Transform cameraTransform)
|
||||
// {
|
||||
// Vector3[] directions =
|
||||
// {
|
||||
// Vector3.forward,
|
||||
// Vector3.back,
|
||||
// Vector3.right,
|
||||
// Vector3.left,
|
||||
// (Vector3.forward + Vector3.right).normalized,
|
||||
// (Vector3.forward + Vector3.left).normalized,
|
||||
// (Vector3.back + Vector3.right).normalized,
|
||||
// (Vector3.back + Vector3.left).normalized
|
||||
// };
|
||||
//
|
||||
// FishingLureCastInput baseInput = new FishingLureCastInput
|
||||
// {
|
||||
// power = 1f,
|
||||
// lureWeight = 2.2f,
|
||||
// windStrength = 0f, // 风力大小
|
||||
// lineDiameter = 0.14f
|
||||
// };
|
||||
//
|
||||
//
|
||||
// Vector3 startPos = cameraTransform.position;
|
||||
// Vector3 throwDir = cameraTransform.forward.normalized;
|
||||
//
|
||||
// baseInput.windDirection = directions[_windIndex];
|
||||
// _trajectory = CalculateTrajectory(baseInput, startPos, throwDir);
|
||||
// _trajectory = SimplifyTrajectoryRDP(_trajectory, 0.1f);
|
||||
// _windIndex++;
|
||||
// if (_windIndex >= directions.Length)
|
||||
// {
|
||||
// _windIndex = 0;
|
||||
// }
|
||||
//
|
||||
// var length = CalculateTrajectoryLength(_trajectory);
|
||||
// var distance = CalculateHorizontalDistance(_trajectory);
|
||||
// Debug.LogError($"轨迹点位数={_trajectory.Count} length={length} distance={distance}");
|
||||
// SceneSettings.Instance.LineRenderer.startWidth = 0.1f;
|
||||
// SceneSettings.Instance.LineRenderer.endWidth = 0.1f;
|
||||
// SceneSettings.Instance.LineRenderer.positionCount = _trajectory.Count;
|
||||
// SceneSettings.Instance.LineRenderer.useWorldSpace = true;
|
||||
// SceneSettings.Instance.LineRenderer.SetPositions(_trajectory.ToArray());
|
||||
// return _trajectory;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 计算轨迹总长度(线的实际长度)
|
||||
// /// </summary>
|
||||
// public float CalculateTrajectoryLength(List<Vector3> trajectory)
|
||||
// {
|
||||
// if (trajectory == null || trajectory.Count < 2)
|
||||
// return 0f;
|
||||
//
|
||||
// float totalLength = 0f;
|
||||
// for (int i = 1; i < trajectory.Count; i++)
|
||||
// {
|
||||
// totalLength += Vector3.Distance(trajectory[i - 1], trajectory[i]);
|
||||
// }
|
||||
//
|
||||
// return totalLength;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 计算水平距离(XZ平面上的直线距离)
|
||||
// /// </summary>
|
||||
// public float CalculateHorizontalDistance(List<Vector3> trajectory)
|
||||
// {
|
||||
// if (trajectory == null || trajectory.Count == 0)
|
||||
// return 0f;
|
||||
//
|
||||
// Vector3 startPoint = trajectory[0];
|
||||
// Vector3 endPoint = trajectory[trajectory.Count - 1];
|
||||
//
|
||||
// // 将Y坐标设为相同(地面高度)
|
||||
// startPoint.y = 0;
|
||||
// endPoint.y = 0;
|
||||
//
|
||||
// return Vector3.Distance(startPoint, endPoint);
|
||||
// }
|
||||
//
|
||||
// private void OnDrawGizmos()
|
||||
// {
|
||||
// if (_trajectory == null) return;
|
||||
//
|
||||
// Gizmos.color = Color.cyan;
|
||||
// for (int i = 0; i < _trajectory.Count - 1; i++)
|
||||
// {
|
||||
// Gizmos.DrawLine(_trajectory[i], _trajectory[i + 1]);
|
||||
// }
|
||||
//
|
||||
// if (_trajectory.Count > 0)
|
||||
// {
|
||||
// Gizmos.color = Color.green;
|
||||
// Gizmos.DrawSphere(_trajectory[0], 0.1f);
|
||||
//
|
||||
// Gizmos.color = Color.red;
|
||||
// Gizmos.DrawSphere(_trajectory[_trajectory.Count - 1], 0.1f);
|
||||
//
|
||||
// for (int i = 1; i < _trajectory.Count; i += 10)
|
||||
// {
|
||||
// Gizmos.color = Color.Lerp(Color.green, Color.red, i / (float)_trajectory.Count);
|
||||
// Gizmos.DrawSphere(_trajectory[i], 0.05f);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public static List<Vector3> SimplifyTrajectoryRDP(List<Vector3> points, float tolerance)
|
||||
// {
|
||||
// if (points == null || points.Count < 3)
|
||||
// return new List<Vector3>(points);
|
||||
//
|
||||
// List<Vector3> result = new List<Vector3>();
|
||||
// SimplifySection(points, 0, points.Count - 1, tolerance, result);
|
||||
// result.Add(points[points.Count - 1]);
|
||||
// return result;
|
||||
// }
|
||||
//
|
||||
// private static void SimplifySection(List<Vector3> points, int start, int end, float tolerance,
|
||||
// List<Vector3> result)
|
||||
// {
|
||||
// if (end <= start + 1)
|
||||
// return;
|
||||
//
|
||||
// float maxDistance = -1f;
|
||||
// int index = -1;
|
||||
// Vector3 startPoint = points[start];
|
||||
// Vector3 endPoint = points[end];
|
||||
//
|
||||
// for (int i = start + 1; i < end; i++)
|
||||
// {
|
||||
// float distance = PerpendicularDistance(points[i], startPoint, endPoint);
|
||||
// if (distance > maxDistance)
|
||||
// {
|
||||
// maxDistance = distance;
|
||||
// index = i;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (maxDistance > tolerance)
|
||||
// {
|
||||
// SimplifySection(points, start, index, tolerance, result);
|
||||
// result.Add(points[index]);
|
||||
// SimplifySection(points, index, end, tolerance, result);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static float PerpendicularDistance(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
|
||||
// {
|
||||
// if (lineStart == lineEnd) return Vector3.Distance(point, lineStart);
|
||||
// Vector3 projected = Vector3.Project(point - lineStart, lineEnd - lineStart);
|
||||
// Vector3 closest = lineStart + projected;
|
||||
// return Vector3.Distance(point, closest);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static class PhysicsHelper
|
||||
// {
|
||||
// public const float AirDensity = 1.225f;
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0f1ba19f0fa4b8f988837e9f240c009
|
||||
timeCreated: 1746284483
|
||||
49
Assets/Scripts/Common/Utils/GameUtils.cs
Normal file
49
Assets/Scripts/Common/Utils/GameUtils.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class GameUtils
|
||||
{
|
||||
public static float GetVerticalAngle(Transform character, Transform target)
|
||||
{
|
||||
// 计算从角色指向目标的向量
|
||||
Vector3 direction = target.position - character.position;
|
||||
|
||||
// 计算水平距离(XZ平面)
|
||||
float horizontalDistance = new Vector3(direction.x, 0, direction.z).magnitude;
|
||||
|
||||
// 计算垂直高度差(Y轴)
|
||||
float verticalDistance = direction.y;
|
||||
|
||||
// 使用反正切计算垂直角度
|
||||
// Atan2(垂直距离, 水平距离)
|
||||
float angle = Mathf.Atan2(verticalDistance, horizontalDistance) * Mathf.Rad2Deg;
|
||||
|
||||
return angle; // 正值为上方,负值为下方
|
||||
}
|
||||
|
||||
public static float GetHorizontalAngle(Transform obj1, Transform obj2)
|
||||
{
|
||||
// 获取两个物体在水平面上的位置向量
|
||||
Vector3 pos1 = obj1.position;
|
||||
Vector3 pos2 = obj2.position;
|
||||
|
||||
// 创建水平方向向量(忽略Y轴)
|
||||
Vector3 direction = new Vector3(
|
||||
pos2.x - pos1.x,
|
||||
0, // Y轴设为0,只在XZ平面计算
|
||||
pos2.z - pos1.z
|
||||
);
|
||||
|
||||
// 计算相对于世界坐标系前向向量的角度
|
||||
float angle = Vector3.SignedAngle(
|
||||
Vector3.forward, // 参考方向(世界坐标系前向)
|
||||
direction.normalized,
|
||||
Vector3.up // 旋转轴(Y轴)
|
||||
);
|
||||
|
||||
// 返回角度范围:-180° 到 180°
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/GameUtils.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/GameUtils.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1e142c7ecd747eaa6971907ecae2c44
|
||||
timeCreated: 1768661644
|
||||
76
Assets/Scripts/Common/Utils/ItemHelper.cs
Normal file
76
Assets/Scripts/Common/Utils/ItemHelper.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System.IO;
|
||||
using FairyGUI;
|
||||
using NBC;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF.Utils
|
||||
{
|
||||
public static class ItemHelper
|
||||
{
|
||||
public static string GetFullModelPath(this cfg.Item config)
|
||||
{
|
||||
//Assets/ResRaw/gfx/hooks/alliance/c_hook_20789_20794/c_hook_20789.prefab
|
||||
return $"Assets/ResRaw/gfx/{config.Model}.prefab"; //Path.Combine("Assets/ResRaw/gfx", config.Model);
|
||||
}
|
||||
|
||||
public static ItemType GetItemType(this int id)
|
||||
{
|
||||
return (ItemType)(id / 10000);
|
||||
}
|
||||
public static ItemType GetItemType(this uint id)
|
||||
{
|
||||
return (ItemType)(id / 10000);
|
||||
}
|
||||
|
||||
public static string GetName(this int id)
|
||||
{
|
||||
return Lan.Get($"Name_{id}");
|
||||
}
|
||||
|
||||
public static string GetDesc(this int id)
|
||||
{
|
||||
return Lan.Get($"Desc_{id}");
|
||||
}
|
||||
|
||||
public static string GetIcon(this int id)
|
||||
{
|
||||
//Assets/Resources/Icons/baitfrog.png
|
||||
////$"Icons/{id}";
|
||||
return UIPackage.GetItemURL(UIDef.Pack.CommonIcon, id.ToString());
|
||||
}
|
||||
|
||||
public static void SetIcon(this GLoader loader, int id)
|
||||
{
|
||||
loader.url = id.GetIcon();
|
||||
}
|
||||
|
||||
public static void SetIcon(this GComponent loader, int id)
|
||||
{
|
||||
loader.icon = id.GetIcon();
|
||||
}
|
||||
|
||||
#region 品质相关
|
||||
|
||||
public static Color QualityColor0 = new Color(1, 1, 1, 0);
|
||||
public static Color QualityColor1 = new Color(96 / 255f, 160 / 255f, 224 / 255f, 1);
|
||||
public static Color QualityColor2 = new Color(0 / 255f, 138 / 255f, 255 / 255f, 1);
|
||||
public static Color QualityColor3 = new Color(126 / 255f, 0 / 255f, 255 / 255f, 1);
|
||||
public static Color QualityColor4 = new Color(220 / 255f, 50 / 255f, 50 / 255f, 1);
|
||||
public static Color QualityColor5 = new Color(255 / 255f, 108 / 255f, 0 / 255f, 1);
|
||||
|
||||
public static Color GetItemQualityColor(int quality)
|
||||
{
|
||||
return quality switch
|
||||
{
|
||||
1 => QualityColor1,
|
||||
2 => QualityColor2,
|
||||
3 => QualityColor3,
|
||||
4 => QualityColor4,
|
||||
5 => QualityColor5,
|
||||
_ => QualityColor0
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/ItemHelper.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/ItemHelper.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bbfb10d099a14e8b9b8aea80b789bdf3
|
||||
timeCreated: 1760429516
|
||||
109
Assets/Scripts/Common/Utils/JsonHelper.cs
Normal file
109
Assets/Scripts/Common/Utils/JsonHelper.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class JsonHelper
|
||||
{
|
||||
[Serializable]
|
||||
private class Wrapper<T>
|
||||
{
|
||||
public T[] Items;
|
||||
}
|
||||
|
||||
public static T[] FromJson<T>(string json)
|
||||
{
|
||||
return JsonUtility.FromJson<Wrapper<T>>(json).Items;
|
||||
}
|
||||
|
||||
public static string ToJson<T>(T[] array)
|
||||
{
|
||||
return JsonUtility.ToJson(new Wrapper<T>
|
||||
{
|
||||
Items = array
|
||||
});
|
||||
}
|
||||
|
||||
public static string ToJson<T>(T[] array, bool prettyPrint)
|
||||
{
|
||||
return JsonUtility.ToJson(new Wrapper<T>
|
||||
{
|
||||
Items = array
|
||||
}, prettyPrint);
|
||||
}
|
||||
|
||||
public static string fixJson(string value)
|
||||
{
|
||||
value = "{\"Items\":" + value + "}";
|
||||
return value;
|
||||
}
|
||||
|
||||
public static string fixArrayJson(string value)
|
||||
{
|
||||
value = "{\"Items\":[" + value + "]}";
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int JsonToInt(string target, string s)
|
||||
{
|
||||
return int.Parse(Regex.Split(target, s)[1]);
|
||||
}
|
||||
|
||||
public static float JsonToFloat(string target, string s)
|
||||
{
|
||||
return float.Parse(Regex.Split(target, s)[1]);
|
||||
}
|
||||
|
||||
public static string JsonToString(string target, string s)
|
||||
{
|
||||
return Regex.Split(target, s)[1];
|
||||
}
|
||||
|
||||
public static Vector3 JsonToVecter3(string target)
|
||||
{
|
||||
string[] array = Regex.Split(target, ",");
|
||||
return new Vector3(float.Parse(array[0]), float.Parse(array[1]), float.Parse(array[2]));
|
||||
}
|
||||
|
||||
public static Vector2 JsonToVecter2(string target)
|
||||
{
|
||||
string[] array = Regex.Split(target, ",");
|
||||
return new Vector2(float.Parse(array[0]), float.Parse(array[1]));
|
||||
}
|
||||
|
||||
public static string filterTheOutgoingString(string text)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (text[i] == '\\')
|
||||
{
|
||||
stringBuilder.Append('\\');
|
||||
}
|
||||
else if (text[i] == '"')
|
||||
{
|
||||
stringBuilder.Append("\\\"");
|
||||
continue;
|
||||
}
|
||||
|
||||
stringBuilder.Append(text[i]);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public static string fixJsonString(string text)
|
||||
{
|
||||
if (text.Length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
text = text.Remove(0, 1);
|
||||
text = text.Remove(text.Length - 1, 1);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/JsonHelper.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/JsonHelper.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e074774e14e54eaa8ebd7816cb050d4c
|
||||
timeCreated: 1742311517
|
||||
27
Assets/Scripts/Common/Utils/ModelUtils.cs
Normal file
27
Assets/Scripts/Common/Utils/ModelUtils.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class ModelUtils
|
||||
{
|
||||
public static void AlignmentParentCenter(this Transform transform)
|
||||
{
|
||||
// 获取子物体所有渲染器的边界
|
||||
Renderer[] renderers = transform.GetComponentsInChildren<Renderer>();
|
||||
Bounds bounds = new Bounds(transform.transform.position, Vector3.zero);
|
||||
foreach (Renderer renderer in renderers)
|
||||
{
|
||||
bounds.Encapsulate(renderer.bounds);
|
||||
}
|
||||
|
||||
Vector3 center = bounds.center;
|
||||
|
||||
// 将子物体设为父对象的子物体
|
||||
transform.transform.SetParent(transform.parent.transform);
|
||||
|
||||
// 计算偏移量并调整位置
|
||||
Vector3 offset = center - transform.parent.transform.position;
|
||||
transform.transform.localPosition = -offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/ModelUtils.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/ModelUtils.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11d82f3861f442f3a96a5cdee975bc04
|
||||
timeCreated: 1750947982
|
||||
223
Assets/Scripts/Common/Utils/NewFile1.txt
Normal file
223
Assets/Scripts/Common/Utils/NewFile1.txt
Normal file
@@ -0,0 +1,223 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
[System.Serializable]
|
||||
public struct FishingCastInput
|
||||
{
|
||||
[Range(0, 1)] public float power;
|
||||
[Range(0, 50)] public float windStrength; // 单位 m/s
|
||||
public Vector3 windDirection;
|
||||
[Range(5, 30)] public float lureWeight; // 单位 g
|
||||
[Range(0, 30)] public float spiralAngularSpeed;
|
||||
}
|
||||
|
||||
public class LureTrajectorySimulator : MonoBehaviour
|
||||
{
|
||||
[Header("基础参数")] public int maxSimulationSteps = 1000;
|
||||
public float simulationDuration = 10f;
|
||||
public float timeStep = 0.02f;
|
||||
|
||||
[Header("抛投力度")] public float minThrowPower = 15f;
|
||||
public float maxThrowPower = 45f;
|
||||
|
||||
[Header("空气阻力参数")] [Tooltip("阻力系数,非流线钓饵建议 0.8~1.2")]
|
||||
public float dragCoefficient = 0.1f;
|
||||
|
||||
[Tooltip("迎风面积,0.005m² ≈ 5cm²")] public float lureArea = 0.001f;
|
||||
|
||||
|
||||
[Header("螺旋轨迹参数")] public float spiralRadius = 0.3f; // 初始螺旋半径(单位:米)
|
||||
[Range(0f, 2f)] public float spiralDecay = 1f; // 螺旋逐渐减弱的衰减因子
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 加入了风
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="startPosition"></param>
|
||||
/// <param name="castDirection"></param>
|
||||
/// <returns></returns>
|
||||
public List<Vector3> CalculateTrajectory(FishingCastInput input, Vector3 startPosition, Vector3 castDirection)
|
||||
{
|
||||
List<Vector3> trajectory = new List<Vector3>();
|
||||
|
||||
Vector3 position = startPosition;
|
||||
Vector3 direction = castDirection.normalized;
|
||||
float throwPower = Mathf.Lerp(minThrowPower, maxThrowPower, input.power);
|
||||
Vector3 velocity = direction * throwPower;
|
||||
|
||||
float lureMass = input.lureWeight / 1000f; // 转 kg
|
||||
Vector3 windDir = input.windDirection.normalized;
|
||||
float windStrength = input.windStrength;
|
||||
|
||||
float currentTime = 0f;
|
||||
int steps = 0;
|
||||
|
||||
while (currentTime < simulationDuration && steps < maxSimulationSteps)
|
||||
{
|
||||
if (position.y <= 0f) break;
|
||||
|
||||
// 模拟风力逐渐生效
|
||||
float windInfluenceFactor = Mathf.Clamp01(currentTime / 1.5f); // 1.5秒内增长
|
||||
Vector3 windVelocity = windDir * windStrength * windInfluenceFactor;
|
||||
|
||||
// 真实空气阻力模型
|
||||
Vector3 relVelocity = velocity - windVelocity;
|
||||
|
||||
// 空气阻力
|
||||
float dragMag = 0.5f * PhysicsHelper.AirDensity *
|
||||
relVelocity.sqrMagnitude *
|
||||
dragCoefficient * lureArea;
|
||||
|
||||
Vector3 dragForce = -relVelocity.normalized * dragMag;
|
||||
|
||||
// 合力 = 重力 + 空气阻力
|
||||
Vector3 acceleration = (Physics.gravity + dragForce / lureMass);
|
||||
|
||||
velocity += acceleration * timeStep;
|
||||
position += velocity * timeStep;
|
||||
|
||||
|
||||
// // 当前角度(代替原来的 spiralAngle 计算)
|
||||
// float spiralAngle = currentTime * input.spiralAngularSpeed;
|
||||
//
|
||||
// float spiralFalloff = Mathf.Exp(-spiralDecay * currentTime);
|
||||
// float radiusNow = spiralRadius * spiralFalloff;
|
||||
//
|
||||
// // 构造绕投掷方向垂直平面上的偏移
|
||||
// Vector3 right = Vector3.Cross(direction, Vector3.up).normalized;
|
||||
// if (right.sqrMagnitude < 0.001f)
|
||||
// right = Vector3.Cross(direction, Vector3.forward).normalized; // 防止方向与up重合
|
||||
// Vector3 up = Vector3.Cross(direction, right).normalized;
|
||||
// Vector3 spiralOffset = (right * Mathf.Cos(spiralAngle) + up * Mathf.Sin(spiralAngle)) * radiusNow;
|
||||
//
|
||||
// position += spiralOffset;
|
||||
|
||||
trajectory.Add(position);
|
||||
currentTime += timeStep;
|
||||
steps++;
|
||||
}
|
||||
|
||||
return trajectory;
|
||||
}
|
||||
|
||||
|
||||
// 示例调用
|
||||
private List<Vector3> _trajectory;
|
||||
|
||||
private int _windIndex;
|
||||
|
||||
public List<Vector3> Test(Transform cameraTransform)
|
||||
{
|
||||
Vector3[] directions =
|
||||
{
|
||||
Vector3.forward,
|
||||
Vector3.back,
|
||||
Vector3.right,
|
||||
Vector3.left,
|
||||
(Vector3.forward + Vector3.right).normalized,
|
||||
(Vector3.forward + Vector3.left).normalized,
|
||||
(Vector3.back + Vector3.right).normalized,
|
||||
(Vector3.back + Vector3.left).normalized
|
||||
};
|
||||
|
||||
FishingCastInput baseInput = new FishingCastInput
|
||||
{
|
||||
power = 1f,
|
||||
lureWeight = 2f,
|
||||
windStrength = 6f, // 风力大小
|
||||
spiralAngularSpeed = 15
|
||||
};
|
||||
|
||||
|
||||
Vector3 startPos = cameraTransform.position;
|
||||
Vector3 throwDir = cameraTransform.forward.normalized;
|
||||
|
||||
baseInput.windDirection = directions[_windIndex]; //directions[_windIndex]; (-transform.forward).normalized;
|
||||
_trajectory = CalculateTrajectory(baseInput, startPos, throwDir);
|
||||
_windIndex++;
|
||||
if (_windIndex >= directions.Length)
|
||||
{
|
||||
_windIndex = 0;
|
||||
}
|
||||
|
||||
var length = CalculateTrajectoryLength(_trajectory);
|
||||
var distance = CalculateHorizontalDistance(_trajectory);
|
||||
Debug.LogError($"轨迹点位数={_trajectory.Count} length={length} distance={distance}");
|
||||
SceneSettings.Instance.LineRenderer.startWidth = 0.1f;
|
||||
SceneSettings.Instance.LineRenderer.endWidth = 0.1f;
|
||||
SceneSettings.Instance.LineRenderer.positionCount = _trajectory.Count;
|
||||
SceneSettings.Instance.LineRenderer.useWorldSpace = true;
|
||||
SceneSettings.Instance.LineRenderer.SetPositions(_trajectory.ToArray());
|
||||
return _trajectory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算轨迹总长度(线的实际长度)
|
||||
/// </summary>
|
||||
public float CalculateTrajectoryLength(List<Vector3> trajectory)
|
||||
{
|
||||
if (trajectory == null || trajectory.Count < 2)
|
||||
return 0f;
|
||||
|
||||
float totalLength = 0f;
|
||||
for (int i = 1; i < trajectory.Count; i++)
|
||||
{
|
||||
totalLength += Vector3.Distance(trajectory[i - 1], trajectory[i]);
|
||||
}
|
||||
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算水平距离(XZ平面上的直线距离)
|
||||
/// </summary>
|
||||
public float CalculateHorizontalDistance(List<Vector3> trajectory)
|
||||
{
|
||||
if (trajectory == null || trajectory.Count == 0)
|
||||
return 0f;
|
||||
|
||||
Vector3 startPoint = trajectory[0];
|
||||
Vector3 endPoint = trajectory[trajectory.Count - 1];
|
||||
|
||||
// 将Y坐标设为相同(地面高度)
|
||||
startPoint.y = 0;
|
||||
endPoint.y = 0;
|
||||
|
||||
return Vector3.Distance(startPoint, endPoint);
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (_trajectory == null) return;
|
||||
|
||||
Gizmos.color = Color.cyan;
|
||||
for (int i = 0; i < _trajectory.Count - 1; i++)
|
||||
{
|
||||
Gizmos.DrawLine(_trajectory[i], _trajectory[i + 1]);
|
||||
}
|
||||
|
||||
if (_trajectory.Count > 0)
|
||||
{
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawSphere(_trajectory[0], 0.1f);
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawSphere(_trajectory[_trajectory.Count - 1], 0.1f);
|
||||
|
||||
for (int i = 1; i < _trajectory.Count; i += 10)
|
||||
{
|
||||
Gizmos.color = Color.Lerp(Color.green, Color.red, i / (float)_trajectory.Count);
|
||||
Gizmos.DrawSphere(_trajectory[i], 0.05f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class PhysicsHelper
|
||||
{
|
||||
public const float AirDensity = 1.225f;
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/NewFile1.txt.meta
Normal file
3
Assets/Scripts/Common/Utils/NewFile1.txt.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3151cd53ac046ba9c8335d772c4a596
|
||||
timeCreated: 1746334873
|
||||
184
Assets/Scripts/Common/Utils/PrefabCreator.cs
Normal file
184
Assets/Scripts/Common/Utils/PrefabCreator.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class PrefabCreator
|
||||
{
|
||||
// #region 轮
|
||||
//
|
||||
// public static ReelAsset Create(this GameReels self, Transform parent, bool isPreview = false)
|
||||
// {
|
||||
// ReelAsset ret = null;
|
||||
// var prefab = self.Instantiate(parent);
|
||||
//
|
||||
// prefab.transform.localPosition = Vector3.zero;
|
||||
// prefab.transform.localRotation = Quaternion.identity;
|
||||
//
|
||||
// if (prefab.TryGetComponent<ReelAsset>(out var reel))
|
||||
// {
|
||||
// ret = reel;
|
||||
//
|
||||
// if (!isPreview)
|
||||
// {
|
||||
// prefab.AddComponent<FReel>();
|
||||
// // prefab.AddJoint();
|
||||
// // var dis = prefab.AddComponent<FWaterDisplacement>();
|
||||
// // dis.moveCharacteristic = FWaterDisplacement.MoveCharacteristic.Custom;
|
||||
// // dis.objectDisplacement = 2;
|
||||
// // dis.outwaterMass = 0.1f;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// #endregion
|
||||
|
||||
// #region 浮漂
|
||||
//
|
||||
// public static BobberAsset Create(this GameFloats self, Transform parent, bool isPreview = false)
|
||||
// {
|
||||
// BobberAsset ret = null;
|
||||
// var prefab = self.Instantiate(parent);
|
||||
//
|
||||
// prefab.transform.localPosition = Vector3.zero;
|
||||
// prefab.transform.localRotation = Quaternion.identity;
|
||||
//
|
||||
// if (prefab.TryGetComponent<BobberAsset>(out var lure))
|
||||
// {
|
||||
// ret = lure;
|
||||
//
|
||||
// if (!isPreview)
|
||||
// {
|
||||
// prefab.AddComponent<FFloat>();
|
||||
// prefab.AddJoint();
|
||||
// var dis = prefab.AddComponent<FWaterDisplacement>();
|
||||
// dis.moveCharacteristic = FWaterDisplacement.MoveCharacteristic.Custom;
|
||||
// dis.objectDisplacement = 2;
|
||||
// dis.outwaterMass = 0.1f;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// #endregion
|
||||
//
|
||||
//
|
||||
// #region 饵
|
||||
//
|
||||
// public static LureAsset Create(this GameLures self, Transform parent, bool isPreview = false)
|
||||
// {
|
||||
// LureAsset ret = null;
|
||||
// var prefab = self.Instantiate(parent);
|
||||
//
|
||||
//
|
||||
// if (prefab.TryGetComponent<LureAsset>(out var lure))
|
||||
// {
|
||||
// ret = lure;
|
||||
//
|
||||
// if (!isPreview)
|
||||
// {
|
||||
// prefab.AddComponent<FLure>();
|
||||
// prefab.AddJoint();
|
||||
// var rigidbody = prefab.GetComponent<Rigidbody>();
|
||||
// rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
|
||||
// rigidbody.linearDamping = 0.5f;
|
||||
// var dis = prefab.AddComponent<FWaterDisplacement>();
|
||||
// // dis.moveCharacteristic = FWaterDisplacement.MoveCharacteristic.Custom;
|
||||
// // dis.objectDisplacement = 2;
|
||||
// dis.outwaterMass = 0.5f;
|
||||
// }
|
||||
//
|
||||
// if (lure.hookPoints != null && lure.hookPoints.Length > 0)
|
||||
// {
|
||||
// for (int i = 0; i < lure.hookPoints.Length; i++)
|
||||
// {
|
||||
// var hookPoint = lure.hookPoints[i];
|
||||
// var hook = self.hook[0];
|
||||
// var hookConfig = GameHooks.Get(hook);
|
||||
// var hookAsset = hookConfig.Create(hookPoint, true);
|
||||
// hookAsset.transform.localPosition = Vector3.zero;
|
||||
// hookAsset.transform.localRotation = Quaternion.identity;
|
||||
// var configurableJoint = hookAsset.GetComponent<ConfigurableJoint>();
|
||||
// configurableJoint.connectedBody = prefab.GetComponent<Rigidbody>();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// public static BaitAsset Create(this GameBaits self, Transform parent, bool isPreview = false)
|
||||
// {
|
||||
// var prefab = self.Instantiate(parent);
|
||||
// BaitAsset ret = null;
|
||||
// if (prefab.TryGetComponent<BaitAsset>(out var hook))
|
||||
// {
|
||||
// ret = hook;
|
||||
// }
|
||||
//
|
||||
// prefab.transform.localPosition = Vector3.zero;
|
||||
// prefab.transform.localRotation = Quaternion.identity;
|
||||
// if (!isPreview)
|
||||
// {
|
||||
// prefab.AddComponent<FBait>();
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// #endregion
|
||||
//
|
||||
// #region 钩
|
||||
//
|
||||
// public static HookAsset Create(this GameHooks self, Transform parent, bool isPreview = false)
|
||||
// {
|
||||
// var prefab = self.Instantiate(parent);
|
||||
// HookAsset ret = null;
|
||||
// if (prefab.TryGetComponent<HookAsset>(out var hook))
|
||||
// {
|
||||
// ret = hook;
|
||||
// }
|
||||
//
|
||||
// prefab.transform.localPosition = Vector3.zero;
|
||||
// prefab.transform.localRotation = Quaternion.identity;
|
||||
// if (!isPreview)
|
||||
// {
|
||||
// prefab.AddComponent<FHook>();
|
||||
// var d = prefab.AddComponent<FWaterDisplacement>();
|
||||
// d.outwaterMass = 0.1f;
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
// }
|
||||
//
|
||||
// #endregion
|
||||
|
||||
#region 工具
|
||||
|
||||
public static void AddJoint(this GameObject prefab)
|
||||
{
|
||||
var rigidbody = prefab.GetComponent<Rigidbody>();
|
||||
if (rigidbody == null)
|
||||
{
|
||||
rigidbody = prefab.AddComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
var configurableJoint = prefab.GetComponent<ConfigurableJoint>();
|
||||
if (configurableJoint == null)
|
||||
{
|
||||
configurableJoint = prefab.AddComponent<ConfigurableJoint>();
|
||||
}
|
||||
|
||||
configurableJoint.xMotion = ConfigurableJointMotion.Locked;
|
||||
configurableJoint.yMotion = ConfigurableJointMotion.Locked;
|
||||
configurableJoint.zMotion = ConfigurableJointMotion.Locked;
|
||||
// configurableJoint.angularXMotion = ConfigurableJointMotion.Limited;
|
||||
// configurableJoint.angularYMotion = ConfigurableJointMotion.Limited;
|
||||
// configurableJoint.angularZMotion = ConfigurableJointMotion.Limited;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/PrefabCreator.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/PrefabCreator.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9b6385ae5124f949c2bb3d3513b3b5a
|
||||
timeCreated: 1745497829
|
||||
101
Assets/Scripts/Common/Utils/Reflection.cs
Normal file
101
Assets/Scripts/Common/Utils/Reflection.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class Reflection
|
||||
{
|
||||
private static readonly Assembly _assembly;
|
||||
|
||||
public static Assembly Assembly => _assembly;
|
||||
|
||||
static Reflection()
|
||||
{
|
||||
_assembly = AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Assembly-CSharp");
|
||||
Types = _assembly.GetTypes();
|
||||
}
|
||||
|
||||
public static Type[] Types { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有非抽象的派生类
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static List<Type> GetAllNonAbstractDerivedTypes<T>()
|
||||
{
|
||||
Type baseType = typeof(T);
|
||||
Assembly assembly = Assembly.GetAssembly(baseType);
|
||||
|
||||
List<Type> derivedTypes = assembly.GetTypes()
|
||||
.Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(baseType))
|
||||
.ToList();
|
||||
|
||||
return derivedTypes;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有标记指定特性的方法
|
||||
/// </summary>
|
||||
/// <param name="classType">查找类</param>
|
||||
/// <param name="attributeType">特性类</param>
|
||||
/// <returns></returns>
|
||||
public static List<MethodInfo> GetMethodsWithUIInputAttribute(Type classType, Type attributeType)
|
||||
{
|
||||
List<MethodInfo> methodsWithAttribute = new List<MethodInfo>();
|
||||
|
||||
if (classType == null)
|
||||
{
|
||||
return methodsWithAttribute;
|
||||
}
|
||||
|
||||
// 获取所有方法,包括公共、非公共、实例和静态方法
|
||||
MethodInfo[] allMethods =
|
||||
classType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
foreach (MethodInfo method in allMethods)
|
||||
{
|
||||
if (method.GetCustomAttributes(attributeType, false).Length > 0)
|
||||
{
|
||||
methodsWithAttribute.Add(method);
|
||||
}
|
||||
}
|
||||
|
||||
return methodsWithAttribute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有标记指定特性的方法
|
||||
/// </summary>
|
||||
/// <param name="classType">查找类</param>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<MethodInfo, T> GetMethodsAttribute<T>(Type classType) where T : Attribute
|
||||
{
|
||||
Dictionary<MethodInfo, T> methodsWithAttribute = new Dictionary<MethodInfo, T>();
|
||||
|
||||
if (classType == null)
|
||||
{
|
||||
return methodsWithAttribute;
|
||||
}
|
||||
|
||||
// 获取所有方法,包括公共、非公共、实例和静态方法
|
||||
MethodInfo[] allMethods =
|
||||
classType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
foreach (MethodInfo method in allMethods)
|
||||
{
|
||||
var attribute = method.GetCustomAttribute<T>();
|
||||
if (attribute != null)
|
||||
{
|
||||
methodsWithAttribute[method] = attribute;
|
||||
}
|
||||
}
|
||||
|
||||
return methodsWithAttribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/Reflection.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/Reflection.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 305dfb5221954b80a64083de46a27a46
|
||||
timeCreated: 1742309671
|
||||
179
Assets/Scripts/Common/Utils/SPrefs.cs
Normal file
179
Assets/Scripts/Common/Utils/SPrefs.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public class SPrefs
|
||||
{
|
||||
public const string STRING_SALT = "7Snc1Lso";
|
||||
|
||||
public const string INT_SALT = "t5HqItbY";
|
||||
|
||||
public const string FLOAT_SALT = "ZieZO5cM";
|
||||
|
||||
public const string BOOL_SALT = "E9LvW12n";
|
||||
|
||||
public static void SetString(string key, string value)
|
||||
{
|
||||
SecureSetString("7Snc1Lso" + key, value);
|
||||
}
|
||||
|
||||
public static string GetString(string key)
|
||||
{
|
||||
return GetString(key, "");
|
||||
}
|
||||
|
||||
public static string GetString(string key, string defaultValue)
|
||||
{
|
||||
if (!SecureHasKey("7Snc1Lso" + key))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return SecureGetString("7Snc1Lso" + key);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetInt(string key, int value)
|
||||
{
|
||||
SecureSetString("t5HqItbY" + key, value.ToString());
|
||||
}
|
||||
|
||||
public static int GetInt(string key)
|
||||
{
|
||||
return GetInt(key, 0);
|
||||
}
|
||||
|
||||
public static int GetInt(string key, int defaultValue)
|
||||
{
|
||||
return GetIntCustomSalt("t5HqItbY", key, defaultValue);
|
||||
}
|
||||
|
||||
private static int GetIntCustomSalt(string salt, string key, int defaultValue)
|
||||
{
|
||||
if (!SecureHasKey(salt + key))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
string text = "";
|
||||
try
|
||||
{
|
||||
text = SecureGetString(salt + key);
|
||||
if (text.Length < 1)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return int.Parse(text);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetFloat(string key, float value)
|
||||
{
|
||||
SecureSetString("ZieZO5cM" + key, value.ToString());
|
||||
}
|
||||
|
||||
public static float GetFloat(string key)
|
||||
{
|
||||
return GetFloat(key, 0f);
|
||||
}
|
||||
|
||||
public static float GetFloat(string key, float defaultValue)
|
||||
{
|
||||
if (!SecureHasKey("ZieZO5cM" + key))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
string text = "";
|
||||
try
|
||||
{
|
||||
text = SecureGetString("ZieZO5cM" + key);
|
||||
if (text.Length < 1)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return float.Parse(text);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetBool(string key)
|
||||
{
|
||||
return GetBool(key, defaultValue: false);
|
||||
}
|
||||
|
||||
public static bool GetBool(string key, bool defaultValue)
|
||||
{
|
||||
return Convert.ToBoolean(GetIntCustomSalt("E9LvW12n", key, Convert.ToInt32(defaultValue)));
|
||||
}
|
||||
|
||||
public static void SetBool(string key, bool value)
|
||||
{
|
||||
SecureSetString("E9LvW12n" + key, Convert.ToInt32(value).ToString());
|
||||
}
|
||||
|
||||
public static void DeleteAll()
|
||||
{
|
||||
PlayerPrefs.DeleteAll();
|
||||
}
|
||||
|
||||
public static void DeleteKey(string key)
|
||||
{
|
||||
SecureDeleteKey("7Snc1Lso" + key);
|
||||
SecureDeleteKey("t5HqItbY" + key);
|
||||
SecureDeleteKey("ZieZO5cM" + key);
|
||||
SecureDeleteKey("E9LvW12n" + key);
|
||||
}
|
||||
|
||||
public static void Save()
|
||||
{
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
public static bool HasKey(string key)
|
||||
{
|
||||
if (!SecureHasKey("7Snc1Lso" + key) && !SecureHasKey("t5HqItbY" + key) && !SecureHasKey("ZieZO5cM" + key))
|
||||
{
|
||||
return SecureHasKey("E9LvW12n" + key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void SecureSetString(string key, string value)
|
||||
{
|
||||
PlayerPrefs.SetString(Cryptor.Hash(key), Cryptor.Encrypt(value));
|
||||
}
|
||||
|
||||
private static string SecureGetString(string key)
|
||||
{
|
||||
return Cryptor.Decrypt(PlayerPrefs.GetString(Cryptor.Hash(key)));
|
||||
}
|
||||
|
||||
private static bool SecureHasKey(string key)
|
||||
{
|
||||
return PlayerPrefs.HasKey(Cryptor.Hash(key));
|
||||
}
|
||||
|
||||
private static void SecureDeleteKey(string key)
|
||||
{
|
||||
PlayerPrefs.DeleteKey(Cryptor.Hash(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/SPrefs.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/SPrefs.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a700947f50c4adeac1df790c3b6d7af
|
||||
timeCreated: 1742311480
|
||||
53
Assets/Scripts/Common/Utils/ScreenshotCapturer.cs
Normal file
53
Assets/Scripts/Common/Utils/ScreenshotCapturer.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
|
||||
public class ScreenshotCapturer : MonoBehaviour
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
private void Update()
|
||||
{
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.F12))
|
||||
{
|
||||
CaptureScreenshot();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void CaptureScreenshot()
|
||||
{
|
||||
// 获取Assets上一级目录路径
|
||||
string projectPath = Directory.GetParent(Application.dataPath).FullName;
|
||||
string screenshotDir = Path.Combine(projectPath, "Screenshots");
|
||||
|
||||
// 如果目录不存在则创建
|
||||
if (!Directory.Exists(screenshotDir))
|
||||
{
|
||||
Directory.CreateDirectory(screenshotDir);
|
||||
}
|
||||
|
||||
// 生成基于时间的文件名
|
||||
string timestamp = System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||
string filename = $"Screenshot_{timestamp}.png";
|
||||
string fullPath = Path.Combine(screenshotDir, filename);
|
||||
|
||||
// 确保文件名唯一
|
||||
int counter = 1;
|
||||
while (File.Exists(fullPath))
|
||||
{
|
||||
filename = $"Screenshot_{timestamp}_{counter}.png";
|
||||
fullPath = Path.Combine(screenshotDir, filename);
|
||||
counter++;
|
||||
}
|
||||
|
||||
// 截取屏幕
|
||||
ScreenCapture.CaptureScreenshot(fullPath);
|
||||
Debug.Log($"Screenshot saved to: {fullPath}");
|
||||
|
||||
// 刷新资源数据库(如果需要)
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
2
Assets/Scripts/Common/Utils/ScreenshotCapturer.cs.meta
Normal file
2
Assets/Scripts/Common/Utils/ScreenshotCapturer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c9b2dc114877d3479675dbe81e42739
|
||||
163
Assets/Scripts/Common/Utils/VectorUtil.cs
Normal file
163
Assets/Scripts/Common/Utils/VectorUtil.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBF
|
||||
{
|
||||
public static class VectorUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns Value mapped from one range into another.
|
||||
/// </summary>
|
||||
public static float Remap(float inA, float inB, float outA, float outB, float value)
|
||||
{
|
||||
float t = Mathf.InverseLerp(inA, inB, value);
|
||||
|
||||
return Mathf.Lerp(outA, outB, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the square of the given value.
|
||||
/// </summary>
|
||||
public static float Square(float value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the direction adjusted to be tangent to a specified surface normal relatively to given up axis.
|
||||
/// </summary>
|
||||
public static Vector3 GetTangent(Vector3 direction, Vector3 normal, Vector3 up)
|
||||
{
|
||||
Vector3 right = direction.perpendicularTo(up);
|
||||
|
||||
return normal.perpendicularTo(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Projects a given point onto the plane defined by plane origin and plane normal.
|
||||
/// </summary>
|
||||
public static Vector3 ProjectPointOnPlane(Vector3 point, Vector3 planeOrigin, Vector3 planeNormal)
|
||||
{
|
||||
Vector3 toPoint = point - planeOrigin;
|
||||
Vector3 toPointProjected = Vector3.Project(toPoint, planeNormal);
|
||||
|
||||
return point - toPointProjected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps given angle within min - max range.
|
||||
/// </summary>
|
||||
public static float ClampAngle(float a, float min, float max)
|
||||
{
|
||||
while (max < min)
|
||||
max += 360.0f;
|
||||
|
||||
while (a > max)
|
||||
a -= 360.0f;
|
||||
|
||||
while (a < min)
|
||||
a += 360.0f;
|
||||
|
||||
return a > max ? a - (max + min) * 0.5f < 180.0f ? max : min : a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Angle in the range (0, 360)
|
||||
/// </summary>
|
||||
public static float ClampAngle(float angle)
|
||||
{
|
||||
// returns angle in the range (-360, 360)
|
||||
|
||||
angle = angle % 360.0f;
|
||||
|
||||
if (angle < 0.0f)
|
||||
{
|
||||
// shift to (0, 360) range
|
||||
|
||||
angle += 360.0f;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return angle in range -180 to 180
|
||||
/// </summary>
|
||||
public static float NormalizeAngle(float angle)
|
||||
{
|
||||
// returns angle in the range (0, 360)
|
||||
|
||||
angle = ClampAngle(angle);
|
||||
|
||||
if (angle > 180.0f)
|
||||
{
|
||||
// shift to (-180,180)
|
||||
|
||||
angle -= 360.0f;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clamps the given angle into 0 - 360 degrees range.
|
||||
/// </summary>
|
||||
private static float Clamp0360(float eulerAngles)
|
||||
{
|
||||
float result = eulerAngles - Mathf.CeilToInt(eulerAngles / 360f) * 360f;
|
||||
if (result < 0) result += 360f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new rotation angle (interpolated) clamped in the range (0.0f , 360.0f)
|
||||
/// </summary>
|
||||
public static float FixedTurn(float current, float target, float maxDegreesDelta)
|
||||
{
|
||||
if (maxDegreesDelta == 0.0f)
|
||||
return Clamp0360(current);
|
||||
|
||||
if (maxDegreesDelta >= 360.0f)
|
||||
return Clamp0360(target);
|
||||
|
||||
float result = Clamp0360(current);
|
||||
current = result;
|
||||
target = Clamp0360(target);
|
||||
|
||||
if (current > target)
|
||||
{
|
||||
if (current - target < 180.0f)
|
||||
result -= Mathf.Min(current - target, Mathf.Abs(maxDegreesDelta));
|
||||
else
|
||||
result += Mathf.Min(target + 360.0f - current, Mathf.Abs(maxDegreesDelta));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target - current < 180.0f)
|
||||
result += Mathf.Min(target - current, Mathf.Abs(maxDegreesDelta));
|
||||
else
|
||||
result -= Mathf.Min(current + 360.0f - target, Mathf.Abs(maxDegreesDelta));
|
||||
}
|
||||
|
||||
return Clamp0360(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frame Rate Independent Damping.
|
||||
/// Source: https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
|
||||
/// </summary>
|
||||
public static float Damp(float a, float b, float lambda, float dt)
|
||||
{
|
||||
return Mathf.Lerp(a, b, 1.0f - Mathf.Exp(-lambda * dt));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frame Rate Independent Damping.
|
||||
/// Source: https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
|
||||
/// </summary>
|
||||
public static Vector3 Damp(Vector3 a, Vector3 b, float lambda, float dt)
|
||||
{
|
||||
return Vector3.Lerp(a, b, 1.0f - Mathf.Exp(-lambda * dt));
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Scripts/Common/Utils/VectorUtil.cs.meta
Normal file
3
Assets/Scripts/Common/Utils/VectorUtil.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f5057ebeaca4f7bbcf90011280b93b8
|
||||
timeCreated: 1748528114
|
||||
Reference in New Issue
Block a user