Files
test/Assets/PerfectWorld/Scripts/Managers/EC_HPWorkNavigate.cs
2026-01-10 12:11:05 +07:00

1479 lines
50 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Globalization;
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<INFO> m_configInfo = new List<INFO>();
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;
// Try to load default force-navigate config (Addressables) on creation.
// 尝试在创建时加载默认强制导航配置(Addressables)。
EnsureDefaultConfigLoaded();
}
private const string DEFAULT_FORCE_NAVIGATE_CONFIG_ADDRESS = "Assets/Addressable/force_navigate.txt";
private bool m_bConfigLoaded = false;
// Ensure default config is loaded (safe to call multiple times).
// 确保默认配置已加载(可重复调用)。
private void EnsureDefaultConfigLoaded()
{
if (m_bConfigLoaded)
{
return;
}
// Prefer Addressables (same as other config loads in this project).
// 优先使用 Addressables(与本项目其他配置加载方式一致)。
if (LoadConfigAddressable(DEFAULT_FORCE_NAVIGATE_CONFIG_ADDRESS))
{
m_bConfigLoaded = true;
return;
}
// Fallback to file path loading (dev/debug).
// 回退到文件路径加载(开发/调试)。
if (LoadConfig(DEFAULT_FORCE_NAVIGATE_CONFIG_ADDRESS))
{
m_bConfigLoaded = true;
}
}
// Load configuration from Addressables text asset.
// 从 Addressables 文本资源加载配置。
public bool LoadConfigAddressable(string address)
{
try
{
Addressables.InitializeAsync().WaitForCompletion();
var ta = Addressables.LoadAssetAsync<TextAsset>(address).WaitForCompletion();
if (ta == null || string.IsNullOrEmpty(ta.text))
{
Debug.LogWarning($"CECNavigateCtrl::LoadConfigAddressable, failed to load '{address}'");
return false;
}
return LoadConfigFromText(ta.text);
}
catch (Exception ex)
{
Debug.LogWarning($"CECNavigateCtrl::LoadConfigAddressable exception: {ex.Message}");
return false;
}
}
// Load configuration from raw text (supports quoted CSV lines in force_navigate.txt).
// 从原始文本加载配置(支持 force_navigate.txt 中的带引号 CSV 行)。
public bool LoadConfigFromText(string text)
{
try
{
m_configInfo.Clear();
if (string.IsNullOrEmpty(text))
{
return false;
}
using var reader = new StringReader(text);
string rawLine;
int lineNumber = 0;
while ((rawLine = reader.ReadLine()) != null)
{
lineNumber++;
string line = rawLine.Trim();
if (string.IsNullOrEmpty(line) || line.StartsWith("//"))
{
continue;
}
// force_navigate.txt lines are often wrapped in quotes:
// "32220,-1001,30,30.0,1,Models\...\xxx.ecm"
// force_navigate.txt 的行通常被双引号包裹:
// "32220,-1001,30,30.0,1,Models\...\xxx.ecm"
if (line.Length >= 2 && line[0] == '"' && line[^1] == '"')
{
line = line.Substring(1, line.Length - 2);
}
string[] parts = line.Split(',');
if (parts.Length < 6)
{
Debug.LogWarning($"CECNavigateCtrl::LoadConfigFromText, invalid line {lineNumber}: '{rawLine}'");
return false;
}
// Trim each field (also remove accidental quotes around individual fields).
// 修剪每个字段(也移除单独字段意外的引号)。
for (int i = 0; i < parts.Length; i++)
{
parts[i] = parts[i].Trim().Trim('"');
}
INFO navi = new INFO();
navi.taskID = int.Parse(parts[0], CultureInfo.InvariantCulture);
navi.bezierID = int.Parse(parts[1], CultureInfo.InvariantCulture);
navi.speed = float.Parse(parts[2], CultureInfo.InvariantCulture);
navi.angleWithH = float.Parse(parts[3], CultureInfo.InvariantCulture);
navi.bezierDir = (int.Parse(parts[4], CultureInfo.InvariantCulture) == 1);
navi.strModelPath = parts[5];
m_configInfo.Add(navi);
}
Debug.Log($"[CECNavigateCtrl] Loaded force navigate config entries={m_configInfo.Count}");
return m_configInfo.Count > 0;
}
catch (Exception ex)
{
Debug.LogWarning($"CECNavigateCtrl::LoadConfigFromText exception: {ex.Message}");
return false;
}
}
// 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;
}
string text = File.ReadAllText(filePath);
return LoadConfigFromText(text);
}
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<CECBezier*>(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.txt) // 从二进制流加载Beziernavigate.txt
public bool Load(BinaryReader br)
{
// C++: DWORD version; int objectId; (version>=2: globalID,nextGlobalID) (version>=3: ReadString actName)
// 原版:DWORD version; int objectId;version>=2globalID,nextGlobalID)(version>=3ReadString 动作名)
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.txt loader (equivalent to CECScene::LoadBezierNavigate + GetBezierObjectByGlobalID)
// navigate.txt加载器(等价于 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<int, CECBezier> s_beziers = new Dictionary<int, CECBezier>();
public static CECBezier GetBezierObjectByGlobalID(int iGlobalID)
{
if (!s_loaded)
{
LoadBezierNavigate("Assets/Addressable/navigate.txt");
}
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<TextAsset>(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;
}
}
}
}