首次提交

This commit is contained in:
Bob.Song
2026-03-05 18:07:55 +08:00
commit e125bb869e
4534 changed files with 563920 additions and 0 deletions

View 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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b7bd3eec7be141b188178d45a3c3c0c6
timeCreated: 1742309728

View 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;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 83f029b458614e419234114c3d90f1fc
timeCreated: 1749009573

View 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);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fa3a85fdab024387a20ac51e8e79a438
timeCreated: 1742642984

View 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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d727e52faea74ce8a969e90353dc1bec
timeCreated: 1742311501

View 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);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 207d2029894f4e3595d3b1a244cae68d
timeCreated: 1742309720

View File

@@ -0,0 +1,9 @@
using UnityEngine;
public class DontDestroy : MonoBehaviour
{
void Awake()
{
DontDestroyOnLoad(gameObject);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b189222c6db8433db1239d314f92cf9f
timeCreated: 1742309743

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14814175084d45b39b84b2fccc0a5dc1
timeCreated: 1744904278

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3308733cfb774828bac0a5740bedd645
timeCreated: 1748527846

View 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();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6f9d43645df348cc8e3e498a7570cd2f
timeCreated: 1749008303

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 87135425aeb24b8ead2f411dceabb955
timeCreated: 1748591603

View 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);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c73591289b8944f6b5a2c50787cba505
timeCreated: 1744904286

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 89c60af8c5b54404b9a45748e3e3f9b9
timeCreated: 1742311706

View 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;
// }
// }

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a0f1ba19f0fa4b8f988837e9f240c009
timeCreated: 1746284483

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a1e142c7ecd747eaa6971907ecae2c44
timeCreated: 1768661644

View 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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bbfb10d099a14e8b9b8aea80b789bdf3
timeCreated: 1760429516

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e074774e14e54eaa8ebd7816cb050d4c
timeCreated: 1742311517

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 11d82f3861f442f3a96a5cdee975bc04
timeCreated: 1750947982

View 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;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b3151cd53ac046ba9c8335d772c4a596
timeCreated: 1746334873

View 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
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9b6385ae5124f949c2bb3d3513b3b5a
timeCreated: 1745497829

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 305dfb5221954b80a64083de46a27a46
timeCreated: 1742309671

View 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));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9a700947f50c4adeac1df790c3b6d7af
timeCreated: 1742311480

View 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
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0c9b2dc114877d3479675dbe81e42739

View 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));
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8f5057ebeaca4f7bbcf90011280b93b8
timeCreated: 1748528114