using System; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine; using CSNetwork.GPDataType; using BrewMonster.Scripts.Task; using BrewMonster.Network; using UnityEngine.AddressableAssets; namespace BrewMonster.Scripts { ////////////////////////////////////////////////////////////////////////// // // Helper functions for vector/yaw calculations // ////////////////////////////////////////////////////////////////////////// // Helper class for navigation calculations // 导航计算辅助类 public static class NavigateHelper { // Convert vector to yaw angle // 将向量转换为偏航角 public static float glb_VectorToYaw(A3DVECTOR3 v) { A3DVECTOR3 v1 = v; v1.y = 0.0f; v1.Normalize(); return Mathf.Rad2Deg * (float)Mathf.Atan2(v1.z, v1.x); } // Convert yaw to vector // 将偏航角转换为向量 public static A3DVECTOR3 glb_YawToVector(float fYaw) { float fRad = Mathf.Deg2Rad * fYaw; A3DVECTOR3 v; v.x = 10.0f * Mathf.Cos(fRad); v.z = 10.0f * Mathf.Sin(fRad); v.y = 0.0f; v.Normalize(); return v; } // Clamp yaw to [-180.0f, 180.0f] // 将偏航角限制在 [-180.0f, 180.0f] 范围内 public static void glb_ClampYaw(ref float fYaw) { if (fYaw >= -180.0f && fYaw <= 180.0f) { // Below clamp calculation will bring little error to original number, this will // cause problem in yaw's equal comparing. So if original yaw has been in valid // range, return directly. // 下面的限制计算会给原始数字带来小的误差,这会在偏航角的相等比较中造成问题。 // 所以如果原始偏航角已经在有效范围内,直接返回。 return; } float fInv = 1.0f / 360.0f; fYaw += 180.0f; if (fYaw < 0.0f) { int n = (int)(-fYaw * fInv) + 1; fYaw = fYaw + 360.0f * n; } else { int n = (int)(fYaw * fInv); fYaw = fYaw - 360.0f * n; } fYaw -= 180.0f; UnityEngine.Debug.Assert(fYaw >= -180.0f && fYaw <= 180.0f); } // Get pitch from vector // 从向量获取俯仰角 public static float glb_GetPitch(A3DVECTOR3 v) { return Mathf.Rad2Deg * Mathf.Acos(v.y); } // Rotate position around axis // 围绕轴旋转位置 public static A3DVECTOR3 a3d_RotatePosAroundAxis(A3DVECTOR3 vPos, A3DVECTOR3 vAxis, float fRad) { // C++: a3d_RotatePosAroundAxis uses A3DMATRIX4 RotateAxis(axis, rad) then matrix * v // 原版:a3d_RotatePosAroundAxis 使用 A3DMATRIX4 RotateAxis(axis, rad) 然后 matrix * v Vector3 axis = new Vector3(vAxis.x, vAxis.y, vAxis.z); if (axis.sqrMagnitude < 1e-12f) { return vPos; } Quaternion rotation = Quaternion.AngleAxis(Mathf.Rad2Deg * fRad, axis.normalized); Vector3 result = rotation * new Vector3(vPos.x, vPos.y, vPos.z); return new A3DVECTOR3(result.x, result.y, result.z); } } ////////////////////////////////////////////////////////////////////////// // // CECNavigateCtrl class // ////////////////////////////////////////////////////////////////////////// public class CECNavigateCtrl { public enum NavigateEvent { EM_PREPARE = 0, EM_BEGIN = 1, EM_END = 2, } // Navigation info structure // 导航信息结构 public struct INFO { public int taskID; public int bezierID; public float speed; public float angleWithH; public bool bezierDir; // false: fix, true: bezier // false: 固定, true: 贝塞尔 public string strModelPath; } // Config info list, vector type // 配置信息列表,vector类型 private List m_configInfo = new List(); private CECHostPlayer m_pHost; // Bezier walker // 贝塞尔行走器 private CECBezierWalker m_pBezierWalker; // Force navigate state // 强制导航状态 private bool m_bForceNavigateState; // Current force navigate info // 当前强制导航信息 private INFO m_curNavigateInfo; // Corresponding task ID // 对应的任务ID private int m_taskID; public CECNavigateCtrl(CECHostPlayer pHost) { m_pHost = pHost; m_pBezierWalker = null; m_bForceNavigateState = false; m_taskID = 0; } // Load configuration // 加载配置 public bool LoadConfig(string szFile) { try { string filePath = szFile; // Handle Unity asset paths (relative to Assets folder) // 处理Unity资源路径(相对于Assets文件夹) if (filePath.StartsWith("Assets/")) { // Convert to absolute path // 转换为绝对路径 string projectPath = Application.dataPath.Replace("/Assets", ""); filePath = Path.Combine(projectPath, filePath); } // Handle relative paths from Application.dataPath // 处理相对于Application.dataPath的相对路径 else if (!Path.IsPathRooted(filePath)) { filePath = Path.Combine(Application.dataPath, filePath); } if (!File.Exists(filePath)) { Debug.LogError($"CECNavigateCtrl::LoadConfig, failed to open file {filePath} (original: {szFile})"); return false; } m_configInfo.Clear(); int lineNumber = 0; using (StreamReader reader = new StreamReader(filePath)) { string line; while ((line = reader.ReadLine()) != null) { lineNumber++; // Skip empty lines and comments line = line.Trim(); if (string.IsNullOrEmpty(line) || line.StartsWith("//")) continue; // C++: CSplit(sf.m_szToken).Split(",") and require >= 6 tokens // 原版:CSplit(sf.m_szToken).Split(",") 并要求 >= 6 个字段 string[] parts = line.Split(','); if (parts.Length < 6) { // C++: ASSERT(0); sf.Close(); return false; // 原版:ASSERT(0); 关闭文件并返回 false return false; } INFO navi = new INFO(); navi.taskID = int.Parse(parts[0]); navi.bezierID = int.Parse(parts[1]); navi.speed = float.Parse(parts[2]); navi.angleWithH = float.Parse(parts[3]); navi.bezierDir = (int.Parse(parts[4]) == 1); navi.strModelPath = parts[5]; m_configInfo.Add(navi); } } return true; } catch (Exception) { return false; } } // Get navigation info // 获取导航信息 public bool GetNavigateInfo(int task, ref INFO info) { if (task <= 0) return false; for (int i = 0; i < m_configInfo.Count; i++) { if (m_configInfo[i].taskID == task) { info = m_configInfo[i]; return true; } } return false; } // Prepare navigation // 准备导航 public void OnPrepareNavigate(int task) { m_bForceNavigateState = true; m_taskID = task; // TODO: Implement CECUIHelper.GetGameUIMan().SetShowAllPanlesFlag(false); // CECUIHelper::GetGameUIMan()->SetShowAllPanlesFlag(false); INFO naviInfo = new INFO(); if (GetNavigateInfo(task, ref naviInfo)) { m_curNavigateInfo = naviInfo; // Set navigate model file // 设置导航模型文件 CECHostNavigatePlayer player = m_pHost != null ? m_pHost.GetNavigatePlayer() : null; if (player != null) { player.SetNavigateModelFile(m_curNavigateInfo.strModelPath); player.Init(); } } else { // Give up task if no navigation info found // 如果找不到导航信息则放弃任务 if (m_pHost != null && m_pHost.GetTaskInterface() != null) { m_pHost.GetTaskInterface().GiveUpTask((uint)m_taskID); } } } // Begin navigation // 开始导航 public void OnBeginNavigate() { // C++: // CECScene* pScene = g_pGame->GetGameRun()->GetWorld()->GetScene(); // CECBezier* pBezier = dynamic_cast(pScene->GetBezierObjectByGlobalID(m_curNavigateInfo.bezierID)); // if (!pBezier) { GiveUpTask; return; } // // 原版: // 从Scene按GlobalID取Bezier;取不到则放弃任务并返回 CECBezier pBezier = CECBezierNavigateLoader.GetBezierObjectByGlobalID(m_curNavigateInfo.bezierID); if (pBezier == null) { if (m_pHost != null && m_pHost.GetTaskInterface() != null) { m_pHost.GetTaskInterface().GiveUpTask((uint)m_taskID); } return; } if (m_pBezierWalker == null) { m_pBezierWalker = new CECBezierWalker(); } // Bind bezier and start walking // 绑定贝塞尔曲线并开始行走 m_pBezierWalker.BindBezier(pBezier); m_pBezierWalker.SetSpeed(m_curNavigateInfo.speed); m_pBezierWalker.StartWalk(false, true); // Create and start work // 创建并开始工作 if (m_pHost != null && m_pHost.GetWorkMan() != null) { CECHPWorkNavigate pWork = m_pHost.GetWorkMan().CreateWork(CECHPWork.Host_work_ID.WORK_FORCENAVIGATEMOVE) as CECHPWorkNavigate; if (pWork != null) { pWork.BeginNavigate(); m_pHost.GetWorkMan().StartWork_p2(pWork); } } } // End navigation // 结束导航 public void OnEndNavigate() { if (!m_bForceNavigateState) return; m_taskID = 0; // Finish running work // 完成运行中的工作 if (m_pHost != null && m_pHost.GetWorkMan() != null) { m_pHost.GetWorkMan().FinishRunningWork(CECHPWork.Host_work_ID.WORK_FORCENAVIGATEMOVE); } // TODO: Implement UI helper // CECUIHelper::GetGameUIMan()->SetShowAllPanlesFlag(true); m_bForceNavigateState = false; } // Check if in force navigate state // 检查是否在强制导航状态 public bool IsInForceNavigateState() { return m_bForceNavigateState; } // Get bezier walker // 获取贝塞尔行走器 public CECBezierWalker GetBezierWalker() { return m_pBezierWalker; } // Get current navigate info // 获取当前导航信息 public INFO GetCurrentNavigateInfo() { return m_curNavigateInfo; } } ////////////////////////////////////////////////////////////////////////// // // CECHPWorkNavigate class // ////////////////////////////////////////////////////////////////////////// public class CECHPWorkNavigate : CECHPWork { // Move flag // 移动标志 private bool m_bMove; // Move speed // 移动速度 private float m_fSpeed; public CECHPWorkNavigate(CECHPWorkMan pWorkMan) : base(Host_work_ID.WORK_FORCENAVIGATEMOVE, pWorkMan) { m_dwMask = Work_mask.MASK_FORCENAVIGATE; m_dwTransMask = Work_mask.MASK_STAND; Reset(); } // Reset work // 重置工作 public override void Reset() { base.Reset(); m_bMove = false; } // Copy work data // 复制工作数据 public override bool CopyData(CECHPWork pWork) { if (!base.CopyData(pWork)) return false; CECHPWorkNavigate pSrc = pWork as CECHPWorkNavigate; if (pSrc == null) return false; m_fSpeed = pSrc.m_fSpeed; m_bMove = pSrc.m_bMove; return true; } // Begin navigation // 开始导航 public void BeginNavigate() { Debug.Log($"[CECHPWorkNavigate] BeginNavigate: Setting m_bMove=true"); m_bMove = true; } // Stop move // 停止移动 public void Finish() { Reset(); } // On first tick // 第一次tick时 protected override void OnFirstTick() { // TODO: Implement GetNavigatePlayer // A3DVECTOR3 dir = m_pHost->GetNavigatePlayer()->GetDir(); // dir.Normalize(); CECHostNavigatePlayer pNavigatePlayer = m_pHost.GetNavigatePlayer(); if (pNavigatePlayer == null) { return; } A3DVECTOR3 dir = pNavigatePlayer.GetDir(); dir.Normalize(); // TODO: Implement GetNavigateCtrl // CECNavigateCtrl* pNaviCtrl = m_pHost->GetNavigatePlayer()->GetNavigateCtrl(); CECNavigateCtrl pNaviCtrl = pNavigatePlayer.GetNavigateCtrl(); if (pNaviCtrl == null) { return; } CECNavigateCtrl.INFO naviInfo = pNaviCtrl.GetCurrentNavigateInfo(); A3DVECTOR3 vRight = A3DVECTOR3.CrossProduct(GPDataTypeHelper.g_vAxisY, dir); dir = NavigateHelper.a3d_RotatePosAroundAxis(dir, vRight, Mathf.Deg2Rad * naviInfo.angleWithH); float fDesYaw = NavigateHelper.glb_VectorToYaw(dir); NavigateHelper.glb_ClampYaw(ref fDesYaw); // TODO: Implement camera control // m_pHost->GetCameraCtrl()->SetDgree(90.f-fDesYaw); float fDesPitch = NavigateHelper.glb_GetPitch(dir); // TODO: Implement camera control // m_pHost->GetCameraCtrl()->SetPitch(90.f-fDesPitch); } // Tick routine // Tick例程 public override bool Tick(float dwDeltaTime) { // TODO: Implement GetNavigateCtrl // CECNavigateCtrl* pNaviCtrl = m_pHost->GetNavigatePlayer()->GetNavigateCtrl(); // CECBezierWalker* pBezierWalker = pNaviCtrl->GetBezierWalker(); CECHostNavigatePlayer pNavigatePlayer = m_pHost.GetNavigatePlayer(); CECNavigateCtrl pNaviCtrl = pNavigatePlayer != null ? pNavigatePlayer.GetNavigateCtrl() : null; CECBezierWalker pBezierWalker = pNaviCtrl != null ? pNaviCtrl.GetBezierWalker() : null; if (!m_bMove || pBezierWalker == null) return true; // C++: DWORD dwRealTime = g_pGame->GetRealTickTime(); // ms delta // 原版:DWORD dwRealTime = g_pGame->GetRealTickTime(); // 帧间毫秒差 int dwRealTime = (int)EC_Game.GetRealTickTime(); // TODO: Implement IsWalking // if (pBezierWalker->IsWalking()) { if (pBezierWalker.IsWalking()) { pBezierWalker.Tick(dwRealTime); // Get updated position from bezier walker // 从贝塞尔行走器获取更新的位置 A3DVECTOR3 vCurPos = pBezierWalker.GetPos(); // Get direction from bezier walker // 从贝塞尔行走器获取方向 A3DVECTOR3 vDir = pBezierWalker.GetDir(); vDir.Normalize(); A3DVECTOR3 vRight = A3DVECTOR3.CrossProduct(GPDataTypeHelper.g_vAxisY, vDir); A3DVECTOR3 vUp = A3DVECTOR3.CrossProduct(vDir, vRight); A3DVECTOR3 vDirH = A3DVECTOR3.CrossProduct(vRight, GPDataTypeHelper.g_vAxisY); vUp.Normalize(); // TODO: Implement GetNavigatePlayer // CECHostNavigatePlayer* pClone = m_pHost->GetNavigatePlayer(); CECHostNavigatePlayer pClone = m_pHost.GetNavigatePlayer(); if (pClone != null) { pClone.SetPos(vCurPos); if(pNaviCtrl.GetCurrentNavigateInfo().bezierDir) { pClone.ChangeModelMoveDirAndUp(vDir, vUp); } else { pClone.ChangeModelMoveDirAndUp(vDirH, GPDataTypeHelper.g_vAxisY); } } } else { if (m_bMove) { m_bMove = false; } else { UnityEngine.Debug.Assert(false); } // Set finish flag // 设置完成标志 if (m_pHost != null && m_pHost.GetTaskInterface() != null) { m_pHost.GetTaskInterface().SetForceNavigateFinishFlag(true); } } base.Tick(dwDeltaTime); return true; } } ////////////////////////////////////////////////////////////////////////// // // Forward declarations / Stub classes for dependencies // ////////////////////////////////////////////////////////////////////////// // CECBezierWalker class - Converted from C++ // CECBezierWalker类 - 从C++转换 public class CECBezierWalker { private CECBezier m_pBezier; // Bezier route data // 贝塞尔路径数据 private bool m_bForward; // Forward flag // 前进标志 private float m_fSpeed; // Moving speed // 移动速度 private int m_iTotalTime; // Total time of whole bezier route // 整个贝塞尔路径的总时间 private int m_iTimeCnt; // Time counter // 时间计数器 private int m_iCurSeg; // Current segment // 当前段 private int m_iCurSegTime; // Total time of current segment // 当前段的总时间 private int m_iPassSegTime; // Total time of passed segments // 已通过段的总时间 private bool m_bLoop; // Loop flag // 循环标志 private bool m_bWalking; // true, is walking // true,正在行走 private bool m_bPause; // Pause flag // 暂停标志 private bool m_bForwardStop; // Stop flag // 停止标志 public CECBezierWalker() { m_pBezier = null; m_fSpeed = 1.0f; m_iTotalTime = 0; m_iTimeCnt = 0; m_bForward = true; m_iCurSeg = 0; m_iCurSegTime = 0; m_iPassSegTime = 0; m_bLoop = true; m_bWalking = false; m_bPause = false; m_bForwardStop = false; } // Bind bezier route // 绑定贝塞尔路径 public bool BindBezier(CECBezier pBezier) { m_pBezier = pBezier; // Stop at default position // 在默认位置停止 m_iTimeCnt = 0; m_iCurSeg = 0; m_iPassSegTime = 0; m_bForwardStop = false; m_bWalking = false; m_bPause = false; return true; } // Start walk // 开始行走 public bool StartWalk(bool bLoop, bool bForward) { if (m_pBezier == null) { return false; } m_bLoop = bLoop; m_bForward = bForward; m_iTimeCnt = 0; m_iCurSeg = bForward ? 0 : m_pBezier.GetSegmentNum() - 1; CECBezierSeg pSeg = m_pBezier.GetSegment(m_iCurSeg); if (pSeg != null) { float fInvSpeed = 1000.0f / m_fSpeed; m_iCurSegTime = (int)(pSeg.GetSegLength() * fInvSpeed); } else { m_iCurSegTime = 0; } m_iPassSegTime = 0; m_bWalking = true; m_bPause = false; return true; } // Pause walk // 暂停行走 public void Pause(bool bPause) { if (bPause) { if (m_bWalking) { m_bPause = true; m_bWalking = false; } } else { if (m_bPause) { m_bPause = false; m_bWalking = true; } } } // Move speed // 移动速度 public void SetSpeed(float fSpeed) { if (fSpeed <= 0.0f) { UnityEngine.Debug.Assert(false); return; } m_fSpeed = fSpeed; // Calculate total time // 计算总时间 if (m_pBezier != null) { int iNumSeg = m_pBezier.GetSegmentNum(); float fInvSpeed = 1000.0f / m_fSpeed; m_iTotalTime = 0; for (int i = 0; i < iNumSeg; i++) { CECBezierSeg pSeg = m_pBezier.GetSegment(i); if (pSeg != null) { m_iTotalTime += (int)(pSeg.GetSegLength() * fInvSpeed); } } } } public void SetForwardFlag(bool bForward) { if (!m_bWalking && !m_bPause) { m_bForward = bForward; return; } int iDeltaTime = m_iPassSegTime + m_iCurSegTime - m_iTimeCnt; m_iPassSegTime = m_iTotalTime - (m_iPassSegTime + m_iCurSegTime); m_iTimeCnt = m_iPassSegTime + iDeltaTime; m_bForward = bForward; } // Tick routine // Tick例程 public bool Tick(int iDeltaTime) { if (m_pBezier == null || !m_bWalking) { return true; } int iNumSeg = m_pBezier.GetSegmentNum(); if (iNumSeg == 0) { return true; } float fInvSpeed = 1000.0f / m_fSpeed; if (m_iTotalTime > 0) { iDeltaTime = iDeltaTime % m_iTotalTime; } m_iTimeCnt += iDeltaTime; if (m_bForward) { while (m_iTimeCnt >= m_iPassSegTime + m_iCurSegTime) { if (m_iCurSeg + 1 >= iNumSeg) { if (m_bLoop) { m_iCurSeg = 0; m_iPassSegTime = 0; m_iTimeCnt -= m_iTotalTime; if (m_iTimeCnt < 0) m_iTimeCnt = 0; } else { m_iTimeCnt = m_iPassSegTime + m_iCurSegTime; m_bWalking = false; m_bForwardStop = true; break; } } else { m_iCurSeg++; m_iPassSegTime += m_iCurSegTime; } CECBezierSeg pSeg = m_pBezier.GetSegment(m_iCurSeg); if (pSeg != null) { m_iCurSegTime = (int)(pSeg.GetSegLength() * fInvSpeed); } else { m_iCurSegTime = 0; break; } } } else { while (m_iTimeCnt >= m_iPassSegTime + m_iCurSegTime) { if (m_iCurSeg - 1 < 0) { if (m_bLoop) { m_iCurSeg = iNumSeg - 1; m_iPassSegTime = 0; m_iTimeCnt -= m_iTotalTime; if (m_iTimeCnt < 0) m_iTimeCnt = 0; } else { m_iTimeCnt = m_iPassSegTime + m_iCurSegTime; m_bWalking = false; m_bForwardStop = false; break; } } else { m_iCurSeg--; m_iPassSegTime += m_iCurSegTime; } CECBezierSeg pSeg = m_pBezier.GetSegment(m_iCurSeg); if (pSeg != null) { m_iCurSegTime = (int)(pSeg.GetSegLength() * fInvSpeed); } else { m_iCurSegTime = 0; break; } } } return true; } // Get current position // 获取当前位置 public A3DVECTOR3 GetPos() { if (m_pBezier == null) { UnityEngine.Debug.Assert(false); return new A3DVECTOR3(0, 0, 0); } int iSeg; float f; bool bForward; if (m_bWalking || m_bPause) { if (m_iCurSegTime > 0) { f = (float)(m_iTimeCnt - m_iPassSegTime) / m_iCurSegTime; } else { f = 0.0f; } bForward = m_bForward; iSeg = m_iCurSeg; } else { bForward = true; if (m_bForwardStop) { f = 1.0f; iSeg = m_pBezier.GetSegmentNum() - 1; } else { f = 0.0f; iSeg = 0; } } CECBezierSeg pSeg = m_pBezier.GetSegment(iSeg); if (pSeg != null) { return pSeg.Bezier(f, bForward); } return new A3DVECTOR3(0, 0, 0); } // Get current direction // 获取当前方向 public A3DVECTOR3 GetDir() { if (m_pBezier == null) { UnityEngine.Debug.Assert(false); return new A3DVECTOR3(0, 0, 1); } int iSeg; float f; bool bForward; if (m_bWalking || m_bPause) { if (m_iCurSegTime > 0) { f = (float)(m_iTimeCnt - m_iPassSegTime) / m_iCurSegTime; } else { f = 0.0f; } bForward = m_bForward; iSeg = m_iCurSeg; } else { bForward = true; if (m_bForwardStop) { f = 1.0f; iSeg = m_pBezier.GetSegmentNum() - 1; } else { f = 0.0f; iSeg = 0; } } CECBezierSeg pSeg = m_pBezier.GetSegment(iSeg); if (pSeg != null) { return pSeg.Vector(f, bForward); } return new A3DVECTOR3(0, 0, 1); } public bool IsWalking() { return m_bWalking; } public bool IsPause() { return m_bPause; } public bool GetForwardFlag() { return m_bForward; } public float GetSpeed() { return m_fSpeed; } public int GetTotalTime() { return m_iTotalTime; } public void SetLoopFlag(bool bLoop) { m_bLoop = bLoop; } public bool GetLoopFlag() { return m_bLoop; } } // CECHostNavigatePlayer class - Basic implementation for navigation player // CECHostNavigatePlayer类 - 导航玩家的基本实现 public class CECHostNavigatePlayer { private CECNavigateCtrl m_pNavigateCtrl; // Force navigate // 强制导航 private CECHostPlayer m_pHost = null; // Reference to host player // 对宿主玩家的引用 public CECHostNavigatePlayer(CECHostPlayer pHost) { // Initialize navigate control // 初始化导航控制 m_pHost = pHost; m_pNavigateCtrl = new CECNavigateCtrl(pHost); } public A3DVECTOR3 GetDir() { if (m_pHost != null) { // Get direction from host's transform // 从宿主的变换获取方向 Transform hostTransform = m_pHost.transform; if (hostTransform != null) { Vector3 forward = hostTransform.forward; return new A3DVECTOR3(forward.x, forward.y, forward.z); } } return new A3DVECTOR3(0, 0, 1); } public void SetPos(A3DVECTOR3 vPos) { if (m_pHost != null) { // Actually move the host player // 实际移动宿主玩家 Vector3 newPos = new Vector3(vPos.x, vPos.y, vPos.z); m_pHost.SetPos(newPos); } } public void ChangeModelMoveDirAndUp(A3DVECTOR3 vDir, A3DVECTOR3 vUp) { if (m_pHost != null && m_pHost.transform != null) { // Update host's rotation based on direction // 根据方向更新宿主的旋转 Vector3 dir = new Vector3(vDir.x, vDir.y, vDir.z); Vector3 up = new Vector3(vUp.x, vUp.y, vUp.z); if (dir.magnitude > 0.01f) { Quaternion rotation = Quaternion.LookRotation(dir, up); m_pHost.transform.rotation = rotation; } } } public void SetNavigateModelFile(string szFile) { } // Stub public bool Init() { return true; } // Stub public CECNavigateCtrl GetNavigateCtrl() { return m_pNavigateCtrl; } // Handle navigation event // 处理导航事件 public void OnNavigateEvent(int task, int e) { if (m_pNavigateCtrl == null) { return; } if (e == (int)CECNavigateCtrl.NavigateEvent.EM_PREPARE) { m_pNavigateCtrl.OnPrepareNavigate(task); } else if (e == (int)CECNavigateCtrl.NavigateEvent.EM_BEGIN) { m_pNavigateCtrl.OnBeginNavigate(); } else if (e == (int)CECNavigateCtrl.NavigateEvent.EM_END) { m_pNavigateCtrl.OnEndNavigate(); } } } ////////////////////////////////////////////////////////////////////////// // // CECBezierPoint class - Bezier curve point // 贝塞尔曲线点类 // ////////////////////////////////////////////////////////////////////////// public class CECBezierPoint { private A3DVECTOR3 m_vPos; // Position // 位置 private A3DVECTOR3 m_vDir; // Direction // 方向 public CECBezierPoint() { m_vPos = new A3DVECTOR3(0, 0, 0); m_vDir = new A3DVECTOR3(0, 0, 1); } public A3DVECTOR3 GetPos() { return m_vPos; } public A3DVECTOR3 GetDir() { return m_vDir; } public void SetPos(A3DVECTOR3 pos) { m_vPos = pos; } public void SetDir(A3DVECTOR3 dir) { m_vDir = dir; } } ////////////////////////////////////////////////////////////////////////// // // CECBezierSeg class - Bezier curve segment // 贝塞尔曲线段类 // ////////////////////////////////////////////////////////////////////////// public class CECBezierSeg { private A3DVECTOR3 m_vAnchorHead; // Head anchor point // 头部锚点 private A3DVECTOR3 m_vAnchorTail; // Tail anchor point // 尾部锚点 private int m_nHeadPoint; // Head point index // 头部点索引 private int m_nTailPoint; // Tail point index // 尾部点索引 private float m_fLength; // Segment length // 段长度 private CECBezierPoint[] m_pListPoint; // Reference to point list // 点列表的引用 public CECBezierSeg() { m_vAnchorHead = new A3DVECTOR3(0, 0, 0); m_vAnchorTail = new A3DVECTOR3(0, 0, 0); m_nHeadPoint = 0; m_nTailPoint = 0; m_fLength = 0.0f; m_pListPoint = null; } public void Init(CECBezierPoint[] pListPt) { m_pListPoint = pListPt; } // Calculate Bezier curve position at parameter u [0,1] // 计算参数u [0,1]处的贝塞尔曲线位置 public A3DVECTOR3 Bezier(float u, bool bForward) { // C++: use a,b,c arrays form // 原版:使用 a,b,c 数组形式计算 A3DVECTOR3 pt1, pt2, control1, control2; if (bForward) { pt1 = m_pListPoint[m_nHeadPoint].GetPos(); control1 = m_vAnchorHead; control2 = m_vAnchorTail; pt2 = m_pListPoint[m_nTailPoint].GetPos(); } else { pt1 = m_pListPoint[m_nTailPoint].GetPos(); control1 = m_vAnchorTail; control2 = m_vAnchorHead; pt2 = m_pListPoint[m_nHeadPoint].GetPos(); } float[] a = new float[3]; float[] b = new float[3]; float[] c = new float[3]; float[] p0 = { pt1.x, pt1.y, pt1.z }; float[] p1 = { control1.x, control1.y, control1.z }; float[] p2 = { control2.x, control2.y, control2.z }; float[] p3 = { pt2.x, pt2.y, pt2.z }; for (int i = 0; i < 3; i++) { c[i] = 3.0f * (p1[i] - p0[i]); b[i] = 3.0f * (p2[i] - p1[i]) - c[i]; a[i] = p3[i] - p0[i] - c[i] - b[i]; } A3DVECTOR3 pos = new A3DVECTOR3(); pos.x = (a[0] * u * u * u) + (b[0] * u * u) + (c[0] * u) + p0[0]; pos.y = (a[1] * u * u * u) + (b[1] * u * u) + (c[1] * u) + p0[1]; pos.z = (a[2] * u * u * u) + (b[2] * u * u) + (c[2] * u) + p0[2]; return pos; } // Calculate direction vector at parameter u [0,1] // 计算参数u [0,1]处的方向向量 public A3DVECTOR3 Vector(float u, bool bForward) { // C++ implementation // 原版实现 A3DVECTOR3 headspot, tailspot; if (bForward) { headspot = m_pListPoint[m_nHeadPoint].GetDir(); tailspot = m_pListPoint[m_nTailPoint].GetDir(); } else { headspot = m_pListPoint[m_nTailPoint].GetDir(); tailspot = m_pListPoint[m_nHeadPoint].GetDir(); } if (u <= 0.0f) return headspot; if (u >= 1.0f) return tailspot; float m1 = headspot.Magnitude(); float m2 = tailspot.Magnitude(); A3DVECTOR3 hN, tN; A3DVECTOR3.Normalize(headspot, out hN); A3DVECTOR3.Normalize(tailspot, out tN); float dot = A3DVECTOR3.DotProduct(hN, tN); if (dot > 1.0f) return headspot; if (dot < -1.0f) dot = -1.0f; float r = Mathf.Acos(dot); r = r * u; A3DVECTOR3 axis = A3DVECTOR3.CrossProduct(headspot, tailspot); // matrix * headspot (use quaternion equivalent) A3DVECTOR3 rotated = NavigateHelper.a3d_RotatePosAroundAxis(headspot, axis, r); A3DVECTOR3.Normalize(rotated, out rotated); float len = m1 + (m2 - m1) * u; rotated = rotated * len; if (bForward) return rotated; return -rotated; } public A3DVECTOR3 GetAnchorHead() { return m_vAnchorHead; } public A3DVECTOR3 GetAnchorTail() { return m_vAnchorTail; } public int GetHeadPoint() { return m_nHeadPoint; } public int GetTailPoint() { return m_nTailPoint; } public float GetSegLength() { return m_fLength; } public void SetAnchorHead(A3DVECTOR3 v) { m_vAnchorHead = v; } public void SetAnchorTail(A3DVECTOR3 v) { m_vAnchorTail = v; } public void SetHeadPoint(int index) { m_nHeadPoint = index; } public void SetTailPoint(int index) { m_nTailPoint = index; } public void SetSegLength(float length) { m_fLength = length; } } ////////////////////////////////////////////////////////////////////////// // // CECBezier class - Bezier curve route // 贝塞尔曲线路径类 // ////////////////////////////////////////////////////////////////////////// public class CECBezier { private int m_nObjectID; // Object ID // 对象ID private int m_iGlobalID; // Global ID used to link bezier routes // 用于链接贝塞尔路径的全局ID private int m_iNextGlobalID; // Next bezier route's global ID // 下一个贝塞尔路径的全局ID private int m_nNumPoint; // Number of points // 点数 private int m_nNumSeg; // Number of segments // 段数 private string m_strActName; // Action name // 动作名称 private CECBezierPoint[] m_pListPoint; // Point list // 点列表 private CECBezierSeg[] m_pListSeg; // Segment list // 段列表 public CECBezier() { m_nObjectID = 0; m_iGlobalID = -1; m_iNextGlobalID = -1; m_nNumPoint = 0; m_nNumSeg = 0; m_strActName = ""; m_pListPoint = null; m_pListSeg = null; } ~CECBezier() { Release(); } public void Release() { m_pListPoint = null; m_pListSeg = null; m_nNumPoint = 0; m_nNumSeg = 0; } // Load bezier from binary stream (navigate.dat) // 从二进制流加载Bezier(navigate.dat) public bool Load(BinaryReader br) { // C++: DWORD version; int objectId; (version>=2: globalID,nextGlobalID) (version>=3: ReadString actName) // 原版:DWORD version; int objectId;(version>=2:globalID,nextGlobalID)(version>=3:ReadString 动作名) uint dwVersion = br.ReadUInt32(); m_nObjectID = br.ReadInt32(); if (dwVersion >= 2) { m_iGlobalID = br.ReadInt32(); m_iNextGlobalID = br.ReadInt32(); } if (dwVersion >= 3) { int len = br.ReadInt32(); if (len > 0) { byte[] buf = br.ReadBytes(len); m_strActName = Encoding.GetEncoding(936).GetString(buf); } else { m_strActName = string.Empty; } } int ptNum = br.ReadInt32(); m_pListPoint = new CECBezierPoint[ptNum]; m_nNumPoint = ptNum; for (int i = 0; i < ptNum; i++) { var vPos = new A3DVECTOR3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); var vDir = new A3DVECTOR3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); m_pListPoint[i] = new CECBezierPoint(); m_pListPoint[i].SetPos(vPos); m_pListPoint[i].SetDir(vDir); } int segNum = br.ReadInt32(); m_nNumSeg = segNum; if (segNum > 0) { m_pListSeg = new CECBezierSeg[segNum]; for (int i = 0; i < segNum; i++) { var vAnchorHead = new A3DVECTOR3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); var vAnchorTail = new A3DVECTOR3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); int headIndex = br.ReadInt32(); int tailIndex = br.ReadInt32(); float length = br.ReadSingle(); m_pListSeg[i] = new CECBezierSeg(); m_pListSeg[i].Init(m_pListPoint); m_pListSeg[i].SetAnchorHead(vAnchorHead); m_pListSeg[i].SetAnchorTail(vAnchorTail); m_pListSeg[i].SetHeadPoint(headIndex); m_pListSeg[i].SetTailPoint(tailIndex); m_pListSeg[i].SetSegLength(length); } } return true; } public int GetObjectID() { return m_nObjectID; } public void SetObjectID(int id) { m_nObjectID = id; } public void SetGlobalID(int iID) { m_iGlobalID = iID; } public int GetGlobalID() { return m_iGlobalID; } public void SetNextGlobalID(int iID) { m_iNextGlobalID = iID; } public int GetNextGlobalID() { return m_iNextGlobalID; } public int GetSegmentNum() { return m_nNumSeg; } public CECBezierSeg GetSegment(int n) { if (n >= 0 && n < m_nNumSeg && m_pListSeg != null) { return m_pListSeg[n]; } return null; } // Assign points and segments (for loading/creation) // 分配点和段(用于加载/创建) public void Assign(int nNumPt, int nNumSeg) { Release(); m_nNumPoint = nNumPt; m_nNumSeg = nNumSeg; if (nNumPt > 0) { m_pListPoint = new CECBezierPoint[nNumPt]; } if (nNumSeg > 0) { m_pListSeg = new CECBezierSeg[nNumSeg]; // Initialize segments with point list reference // 使用点列表引用初始化段 for (int i = 0; i < nNumSeg; i++) { m_pListSeg[i] = new CECBezierSeg(); if (m_pListPoint != null) { m_pListSeg[i].Init(m_pListPoint); } } } } public void AddBezierPoint(CECBezierPoint pt, int i) { if (i >= 0 && i < m_nNumPoint && m_pListPoint != null) { m_pListPoint[i].SetPos(pt.GetPos()); m_pListPoint[i].SetDir(pt.GetDir()); } } public void AddBezierSeg(CECBezierSeg seg, int i) { if (i >= 0 && i < m_nNumSeg && m_pListSeg != null) { m_pListSeg[i].Init(m_pListPoint); m_pListSeg[i].SetAnchorHead(seg.GetAnchorHead()); m_pListSeg[i].SetAnchorTail(seg.GetAnchorTail()); m_pListSeg[i].SetHeadPoint(seg.GetHeadPoint()); m_pListSeg[i].SetTailPoint(seg.GetTailPoint()); m_pListSeg[i].SetSegLength(seg.GetSegLength()); } } public void SetOffset(A3DVECTOR3 vOffset) { if (m_pListPoint != null) { for (int i = 0; i < m_nNumPoint; i++) { A3DVECTOR3 pos = m_pListPoint[i].GetPos(); pos.x += vOffset.x; pos.y += vOffset.y; pos.z += vOffset.z; m_pListPoint[i].SetPos(pos); } } } } ////////////////////////////////////////////////////////////////////////// // // navigate.dat loader (equivalent to CECScene::LoadBezierNavigate + GetBezierObjectByGlobalID) // navigate.dat加载器(等价于 CECScene::LoadBezierNavigate + GetBezierObjectByGlobalID) // ////////////////////////////////////////////////////////////////////////// public static class CECBezierNavigateLoader { // NAVIGATEBEZIERFILEHEADER // 导航Bezier文件头 private struct NAVIGATEBEZIERFILEHEADER { public int iVersion; // Version // 版本 public int iNumBezier; // Number of bezier route // Bezier数量 } private static bool s_loaded; private static readonly Dictionary s_beziers = new Dictionary(); public static CECBezier GetBezierObjectByGlobalID(int iGlobalID) { if (!s_loaded) { LoadBezierNavigate("Assets/Addressable/navigate.dat"); } return s_beziers.TryGetValue(iGlobalID, out var bezier) ? bezier : null; } public static bool LoadBezierNavigate(string address) { if (s_loaded) { return true; } try { Addressables.InitializeAsync().WaitForCompletion(); var ta = Addressables.LoadAssetAsync(address).WaitForCompletion(); if (ta == null) { return false; } using var ms = new MemoryStream(ta.bytes, false); using var br = new BinaryReader(ms); NAVIGATEBEZIERFILEHEADER header; header.iVersion = br.ReadInt32(); header.iNumBezier = br.ReadInt32(); s_beziers.Clear(); for (int i = 0; i < header.iNumBezier; i++) { var pBezier = new CECBezier(); if (!pBezier.Load(br)) { return false; } // C++: m_BezierForceNavigateTab.put((int)pBezier->GetGlobalID(), pBezier); // 原版:按GlobalID存入表 s_beziers[pBezier.GetGlobalID()] = pBezier; } s_loaded = true; return true; } catch { return false; } } } }