1479 lines
50 KiB
C#
1479 lines
50 KiB
C#
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) // 从二进制流加载Bezier(navigate.txt)
|
||
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.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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|