调整目录结构

This commit is contained in:
2025-08-29 09:11:08 +08:00
parent efb64ce7bc
commit 1fff9db9ca
351 changed files with 304 additions and 215 deletions

View File

@@ -0,0 +1,59 @@
using System;
using NBF;
using UnityEngine;
public class ArmTest : MonoBehaviour
{
public Vector3 target = new Vector3(0, 0, 0);
public Transform targetTransform;
// public float Value;
private Quaternion initialCameraRotation;
private Quaternion initialLocalRotation;
private Quaternion initialRelativeRotation;
[SerializeField] private float smoothSpeed = 5f;
private void Start()
{
// target = transform.localEulerAngles;
// 记录初始状态
initialCameraRotation = BaseCamera.Main.transform.rotation;
initialLocalRotation = transform.localRotation;
// 计算物体相对于相机的旋转
initialRelativeRotation = Quaternion.Inverse(initialCameraRotation) * transform.rotation;
}
private void Update()
{
}
private Quaternion lastCameraRotation;
[SerializeField] private float angleThreshold = 0.1f; // 角度阈值(度)
private bool needsUpdate;
// private void LateUpdate()
private void LateUpdate()
{
if (!targetTransform) return;
return;
// Debug.LogError(targetTransform.rotation);
// 计算物体应有的世界旋转
Quaternion targetWorldRotation = BaseCamera.Main.transform.rotation * initialRelativeRotation;
// 转换为本地旋转(如果是子物体)
transform.localRotation = Quaternion.Inverse(transform.parent.rotation) * targetWorldRotation;
// // 使用Lerp平滑过渡
// transform.localRotation = Quaternion.Lerp(transform.localRotation, Quaternion.Euler(target.x, target.y, target.z), smoothSpeed * Time.deltaTime);
// // targetTransform.localRotation = Quaternion.Euler(target.x, target.y, target.z);
}
}

View File

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

View File

@@ -0,0 +1,19 @@
using System;
using UnityEngine;
namespace NBF
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = true)]
public class ShowIconAttribute : PropertyAttribute
{
public readonly string icon;
public ShowIconAttribute(string icon) => this.icon = icon;
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = true)]
public class OpCountAttribute : PropertyAttribute
{
public readonly int count;
public OpCountAttribute(int count) => this.count = count;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 772c563e061446c18c624311c27ba9a3
timeCreated: 1747666608

View File

@@ -0,0 +1,50 @@
using UnityEngine;
public class FPSCounter : MonoBehaviour
{
private float updateInterval = 0.5f;
private float accum = 0f;
private int frames = 0;
private float timeleft;
private float fps = 0f;
private GUIStyle style;
private Color goodColor = Color.green; // >60 FPS
private Color warnColor = Color.yellow; // 30-60 FPS
private Color badColor = Color.red; // <30 FPS
void Start()
{
timeleft = updateInterval;
style = new GUIStyle();
style.alignment = TextAnchor.UpperRight;
style.fontSize = 20;
}
void Update()
{
timeleft -= Time.deltaTime;
accum += Time.timeScale / Time.deltaTime;
frames++;
if (timeleft <= 0f)
{
fps = accum / frames;
timeleft = updateInterval;
accum = 0f;
frames = 0;
}
}
void OnGUI()
{
// 根据FPS值设置颜色
if (fps > 60) style.normal.textColor = goodColor;
else if (fps > 30) style.normal.textColor = warnColor;
else style.normal.textColor = badColor;
GUI.Label(new Rect(Screen.width - 150, 10, 140, 30),
$"FPS: {fps:0.}", style);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 117515d39da46664d994b9132854541c

View File

@@ -0,0 +1,191 @@
using System;
using NBC;
using UnityEngine;
namespace NBF
{
public class GameTimer
{
static GameTimer()
{
SetServerTime(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
}
// 游戏纪元开始时间Unix 时间戳,毫秒)
private const long GameEpoch = 1672531200000; // 2023-01-01 00:00:00 UTC
// 时间加速比例 (1现实秒 = 15游戏秒)
private const int TimeAcceleration = 200;
//一天, 一小时, 一分钟固定时长(毫秒)
public const long DAY_MILLS = 86400000;
public const long HOUR_MILLS = 3600000;
public const long MINUTE_MILLS = 60000;
public const long SECOND_MILLS = 1000;
private static long _serverTime;
/// <summary>
/// 更新服务器时间时的unity运行时间
/// </summary>
private static float _serverTimeLocalTime;
/// <summary>
/// 当前服务器时间ms
/// </summary>
public static long serverTime
{
get
{
var offsetLocalTime = (long)((Time.realtimeSinceStartup - _serverTimeLocalTime) * 1000);
return _serverTime + offsetLocalTime;
}
}
/// <summary>
/// 当前游戏时间
/// </summary>
public static long gameTime => RealToGameTimestamp(serverTime);
public static string gameTimeString
{
get
{
var realTime = DateTimeOffset.FromUnixTimeMilliseconds(gameTime);
return $"{realTime.Hour:D2}:{realTime.Minute:D2}";
}
}
public static DateTime ServerDateTime => ConvertUtcToDateTime(serverTime);
public static void SetServerTime(long time)
{
Log.Info($"设置服务器时间={time}");
_serverTime = time;
_serverTimeLocalTime = Time.realtimeSinceStartup;
}
/// <summary>
/// 获取本地时区
/// </summary>
/// <returns></returns>
public static int GetLocalTimeZoneOffset()
{
TimeZoneInfo localTimeZone = TimeZoneInfo.Local;
int offset = (int)localTimeZone.BaseUtcOffset.TotalHours;
return offset;
}
/// <summary>
/// 现实时间戳转游戏时间戳
/// </summary>
/// <param name="realTimestamp">现实时间戳(毫秒)</param>
/// <returns>游戏时间戳(毫秒)</returns>
public static long RealToGameTimestamp(long realTimestamp)
{
long realElapsed = realTimestamp - GameEpoch;
long gameElapsed = realElapsed * TimeAcceleration;
return GameEpoch + gameElapsed;
}
/// <summary>
/// 游戏时间戳转现实时间戳
/// </summary>
/// <param name="gameTimestamp">游戏时间戳(毫秒)</param>
/// <returns>现实时间戳(毫秒)</returns>
public static long GameToRealTimestamp(long gameTimestamp)
{
long gameElapsed = gameTimestamp - GameEpoch;
long realElapsed = gameElapsed / TimeAcceleration;
return GameEpoch + realElapsed;
}
/// <summary>
/// 13位时间戳转化为时间
/// </summary>
/// <param name="utcTime">时间戳</param>
/// <returns></returns>
public static DateTime ConvertUtcToDateTime(long utcTime)
{
var startTime = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Utc);
var cTime = startTime.AddTicks(utcTime * 10000);
return cTime;
}
/// <summary>
/// 时间转化为10位时间戳
/// </summary>
/// <param name="time">获取的时间</param>
/// <returns></returns>
public static long ConvertDateTimeToUtc_10(DateTime time)
{
TimeSpan timeSpan = time.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(timeSpan.TotalSeconds);
}
/// <summary>
/// 转为毫秒时间戳 13位
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
public static long ConvertDateTimeToUtc(DateTime time)
{
TimeSpan timeSpan = time - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(timeSpan.TotalMilliseconds);
}
/// <summary>
/// 将一个秒数转换成"00:00:00"格式
/// </summary>
/// <param name="second"></param>
/// <returns></returns>
public static string GetTimeStringHMD(long second)
{
int h = Mathf.FloorToInt(second / 3600f);
int m = Mathf.FloorToInt(second / 60f - h * 60f);
int s = Mathf.FloorToInt(second - m * 60f - h * 3600f);
// 直接返回格式化好的小时、分钟、秒
return string.Format("{0:D2}:{1:D2}:{2:D2}", h, m, s);
}
/// <summary>
/// 将一个秒数转换成"00:00"格式
/// </summary>
/// <param name="second"></param>
/// <returns></returns>
public static string GetTimeStringMD(int second)
{
if (second <= 0)
{
return "00:00";
}
float m = Mathf.FloorToInt(second / 60f);
float s = Mathf.FloorToInt(second % 60);
// 使用ZString进行高效的字符串拼接
return string.Format("{0:D2}:{1:D2}", (int)m, (int)s);
}
/// <summary>
/// 获取游戏时间在当天的进度0到1之间的值
/// 0表示当天0点1表示当天24点
/// </summary>
public static float GetGameDayProgress()
{
// 获取当天的总毫秒数
double totalMillisecondsInDay = 24 * 60 * 60 * 1000; // 86400000ms
// 计算当天已过去的毫秒数
double elapsedMilliseconds = ConvertUtcToDateTime(gameTime).TimeOfDay.TotalMilliseconds;
// 计算进度0-1
return (float)(elapsedMilliseconds / totalMillisecondsInDay);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aad89a93b28d44678bdb28f4a9ac6e81
timeCreated: 1746417216

246
Assets/Scripts/Demo/Rope.cs Normal file
View File

@@ -0,0 +1,246 @@
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class Rope : MonoBehaviour
{
[Header("Attachment Points")] [SerializeField]
public Transform startAttachment; // 绳子起点绑定的Transform
[SerializeField] public Transform endAttachment; // 绳子终点绑定的Transform
[Header("Verlet Parameters")] [SerializeField]
float nodeDistance = 0.35f;
[SerializeField] float nodeColliderRadius = 0.2f;
[SerializeField] float gravityStrength = 2;
[SerializeField] float totalLength = 10f;
[SerializeField, Range(0, 1)] float velocityDampen = 0.95f;
[SerializeField, Range(0, 0.99f)] float stiffness = 0.8f;
[SerializeField] int iterateCollisionsEvery = 1;
[SerializeField] int iterations = 10;
[SerializeField] int colliderBufferSize = 1;
[Header("Line Renderer")] [SerializeField]
float ropeWidth = 0.1f;
// 私有变量
Vector3 gravity;
// 数组和缓存
Vector3[] currentNodePositions;
Vector3[] previousNodePositions;
Collider[] colliderHitBuffer;
LineRenderer lineRenderer;
GameObject nodeTester;
SphereCollider nodeCollider;
int totalNodes;
float lastTotalLength;
void Awake()
{
// 获取组件引用
lineRenderer = GetComponent<LineRenderer>();
gravity = new Vector3(0, -gravityStrength, 0);
// 初始化节点测试器
nodeTester = new GameObject("Node Tester");
nodeTester.layer = 8;
nodeCollider = nodeTester.AddComponent<SphereCollider>();
nodeCollider.radius = nodeColliderRadius;
// 初始化长度跟踪
lastTotalLength = totalLength;
InitializeRope();
}
void InitializeRope()
{
// 计算节点数量
totalNodes = Mathf.FloorToInt(totalLength / nodeDistance) + 1;
float remainingLength = totalLength % nodeDistance;
if (remainingLength > 0 && totalLength > nodeDistance)
{
totalNodes++;
}
// 初始化或调整数组大小
System.Array.Resize(ref currentNodePositions, totalNodes);
System.Array.Resize(ref previousNodePositions, totalNodes);
colliderHitBuffer = new Collider[colliderBufferSize];
// 初始化节点位置
Vector3 startPos = startAttachment != null ? startAttachment.position : transform.position;
for (int i = 0; i < totalNodes; i++)
{
float distance = (i == totalNodes - 1 && remainingLength > 0) ? remainingLength : nodeDistance;
currentNodePositions[i] = startPos;
previousNodePositions[i] = startPos;
startPos.y -= distance;
}
// 设置线渲染器
lineRenderer.startWidth = ropeWidth;
lineRenderer.endWidth = ropeWidth;
lineRenderer.positionCount = totalNodes;
}
void Update()
{
// 检查长度是否变化
if (!Mathf.Approximately(totalLength, lastTotalLength))
{
AdjustRopeLength();
lastTotalLength = totalLength;
}
DrawRope();
}
void AdjustRopeLength()
{
Vector3[] oldPositions = (Vector3[])currentNodePositions.Clone();
Vector3[] oldPrevPositions = (Vector3[])previousNodePositions.Clone();
InitializeRope();
int copyLength = Mathf.Min(oldPositions.Length, currentNodePositions.Length);
System.Array.Copy(oldPositions, currentNodePositions, copyLength);
System.Array.Copy(oldPrevPositions, previousNodePositions, copyLength);
if (currentNodePositions.Length > oldPositions.Length)
{
Vector3 lastPos = oldPositions[oldPositions.Length - 1];
for (int i = oldPositions.Length; i < currentNodePositions.Length; i++)
{
float distance = (i == currentNodePositions.Length - 1 && (totalLength % nodeDistance) > 0)
? (totalLength % nodeDistance)
: nodeDistance;
lastPos.y -= distance;
currentNodePositions[i] = lastPos;
previousNodePositions[i] = lastPos;
}
}
}
void FixedUpdate()
{
Simulate();
for (int i = 0; i < iterations; i++)
{
ApplyConstraint();
if (i % (iterateCollisionsEvery + 1) == 0)
{
AdjustCollisions();
}
}
}
void Simulate()
{
float fixedDt = Time.fixedDeltaTime;
for (int i = 0; i < totalNodes; i++)
{
Vector3 velocity = (currentNodePositions[i] - previousNodePositions[i]) * velocityDampen;
previousNodePositions[i] = currentNodePositions[i];
currentNodePositions[i] += velocity + gravity * fixedDt;
}
}
void ApplyConstraint()
{
// 绑定到起点Transform
if (startAttachment != null)
{
currentNodePositions[0] = startAttachment.position;
}
// 绑定到终点Transform
if (endAttachment != null)
{
currentNodePositions[totalNodes - 1] = endAttachment.position;
}
float halfStiffness = 0.5f * stiffness;
int nodeCountMinusOne = totalNodes - 1;
for (int i = 0; i < nodeCountMinusOne; i++)
{
Vector3 node1 = currentNodePositions[i];
Vector3 node2 = currentNodePositions[i + 1];
Vector3 diff = node1 - node2;
float desiredDistance = (i == nodeCountMinusOne - 1 && (totalLength % nodeDistance) > 0)
? (totalLength % nodeDistance)
: nodeDistance;
float sqrDesiredDistance = desiredDistance * desiredDistance;
float sqrDistance = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;
if (Mathf.Abs(sqrDistance - sqrDesiredDistance) > 0.001f)
{
float distance = Mathf.Sqrt(sqrDistance);
float difference = desiredDistance - distance;
Vector3 direction = diff / distance;
Vector3 adjustment = direction * (difference * halfStiffness);
currentNodePositions[i] += adjustment;
currentNodePositions[i + 1] -= adjustment;
}
}
}
void AdjustCollisions()
{
for (int i = 1; i < totalNodes; i += 2)
{
int hits = Physics.OverlapSphereNonAlloc(
currentNodePositions[i],
nodeColliderRadius,
colliderHitBuffer,
~(1 << 8));
for (int n = 0; n < hits; n++)
{
if (Physics.ComputePenetration(
nodeCollider,
currentNodePositions[i],
Quaternion.identity,
colliderHitBuffer[n],
colliderHitBuffer[n].transform.position,
colliderHitBuffer[n].transform.rotation,
out Vector3 direction,
out float distance))
{
currentNodePositions[i] += direction * distance;
}
}
}
}
void DrawRope()
{
lineRenderer.positionCount = totalNodes;
lineRenderer.SetPositions(currentNodePositions);
}
void OnDestroy()
{
if (nodeTester != null)
{
Destroy(nodeTester);
}
}
// 公开方法用于动态设置绑定点
public void SetAttachments(Transform start, Transform end)
{
startAttachment = start;
endAttachment = end;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: daf71302d510df147b3f07dee056e8fd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,92 @@
using System;
using Enviro;
using NBF;
using Obi;
using UnityEngine;
public class SceneSettings : MonoBehaviour
{
public static SceneSettings Instance;
public int sceneID;
public string sceneName = "";
public Transform WaterObject;
public Transform Node;
public Transform GearNode;
public ObiLateFixedUpdater obiFixedUpdater;
public LineRenderer LineRenderer;
public int FPS;
private void Awake()
{
Instance = this;
if (Node == null)
{
Node = transform;
}
if (GearNode == null)
{
GearNode = Node;
}
obiFixedUpdater = FindFirstObjectByType<ObiLateFixedUpdater>();
}
private void Start()
{
// EnviroManager.instance.Time.Settings.simulate = true;
// EnviroManager.instance.Time.SetTimeOfDay(0.5f * 24f);
}
private void Update()
{
UpdateFPS();
UpdateTimeOfDay();
}
#region FPS
public float updateInterval = 0.2f; // 更新间隔(秒)
private float accum = 0;
private int frames = 0;
private float timeleft;
void UpdateFPS()
{
timeleft -= Time.deltaTime;
accum += Time.timeScale / Time.deltaTime;
frames++;
if (timeleft <= 0.0f)
{
FPS = (int)(accum / frames);
timeleft = updateInterval;
accum = 0.0f;
frames = 0;
}
}
#endregion
#region
private void UpdateTimeOfDay()
{
// var p = GameTimer.GetGameDayProgress();
// Debug.Log(p);
// EnviroManager.instance.Time.SetTimeOfDay(GameTimer.GetGameDayProgress() * 24f);
}
#endregion
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ba52b4e0f1274042a192a3242f0a3946
timeCreated: 1742478935

View File

@@ -0,0 +1,30 @@
using System;
using NBC;
using NBC.Network;
namespace NBF
{
public static class SessionHelper
{
/// <summary>
/// 创建一个网络会话
/// </summary>
/// <param name="scene"></param>
/// <param name="address">远程服务器地址</param>
/// <param name="onConnectComplete">当连接成功执行的委托,可为空</param>
/// <param name="onConnectFail">当连接超时或失败执行的委托,可为空</param>
/// <param name="onConnectDisconnect">当连接断开执行的委托,可为空</param>
/// <returns></returns>
public static Session CreateSession(Scene scene, string address, Action onConnectComplete, Action onConnectFail,
Action onConnectDisconnect)
{
return scene.Connect(
address,
NetworkProtocolType.KCP,
onConnectComplete,
onConnectFail,
onConnectDisconnect,
false, 5000);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 55c68672713040e4978ccb0e17564338
timeCreated: 1752656502

View File

@@ -0,0 +1,19 @@
using UnityEngine;
public class WaterSplash : MonoBehaviour
{
private AudioSource audioSource;
public AudioClip[] soundsClip;
private void Start()
{
audioSource = GetComponent<AudioSource>();
if (soundsClip.Length != 0)
{
int num = Random.Range(1, soundsClip.Length);
audioSource.clip = soundsClip[num];
audioSource.PlayOneShot(audioSource.clip, transform.localScale.magnitude * 0.3f);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b63c4b8782644da5b70e2940858c3d94
timeCreated: 1742718110

View File

@@ -0,0 +1,16 @@
using System;
using KWS;
using UnityEngine;
public class WaterTest : MonoBehaviour
{
// public WaterSystem waterSystem;
private void Start()
{
// WaterSystem.QualitySettings.RefractionMode =
WaterSystem.QualitySettings.UsePlanarReflection = true;
WaterSystem.QualitySettings.PlanarReflectionResolutionQuality =
WaterQualityLevelSettings.PlanarReflectionResolutionQualityEnum.Low;
}
}

View File

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