Merge remote-tracking branch 'origin/develop' into feature/CheckSkill_Cuong

This commit is contained in:
CuongNV
2026-05-20 14:03:59 +07:00
12 changed files with 481 additions and 123 deletions
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2daf9dffc5bed73510959aebf1ee1b4b2fe326380d6417e1a262c919b651095e
size 70182
oid sha256:11f16ce7ddd16e5c3de941b56358deb495efb2d5d4783c8d59c75aab0dad1975
size 92966
@@ -875,11 +875,7 @@ namespace BrewMonster.Scripts
vCurPos = m_pHost.m_MoveCtrl.GroundMove(m_vCurDir, fSpeed, fDeltaTime, m_pHost.m_fVertSpeed);
if (!m_vCurDir.IsZero())
{
//m_pHost.StartModelMove(m_vCurDir, GPDataTypeHelper.g_vAxisY, 100);
//m_pHost.ChangeModelTargetDirAndUp(m_vCurDir, GPDataTypeHelper.g_vAxisY);
UpdateFacingFromDelta(vCurPos);
}
OrientHostHorizontal(m_vCurDir);
if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3)
{
@@ -3,76 +3,59 @@ using System.Collections.Generic;
using UnityEngine;
// Filename : CMoveAgent.cs
// Creator : ported/simplified from C++ (AutoPFImp/AutoMoveImp/MoveAgent.*)
// Creator : ported from C++ (AutoPFImp/AutoMoveImp/MoveAgent.*)
// Date : 2026/01/09
namespace AutoMove
{
/// <summary>
/// Minimal MoveAgent: loads movemap and provides A* path on layer0 rmap.
/// 最小 MoveAgent:加载 movemap,并在第0层 rmap 上执行 A* 路径搜索。
/// MoveAgent: A* on layer0 rmap + COptimizePath (matches C++ CMoveAgent flow).
/// </summary>
public class CMoveAgent
{
// Debug switch (enable temporarily while validating routes).
// 调试开关(验证寻路时可临时开启)。
// NOTE: must not be const, otherwise Unity compiler warns about unreachable code.
// 注意:不要用 const,否则 Unity 编译会报“不可达代码”警告。
private static bool DEBUG_AUTOPF = false;
public abstract class BrushTest
{
// from.y/to.y store DH in original engine.
// 原版中 from.y/to.y 存储 DH(相对地形高度差)。
public abstract bool Collide(Vector3 from, Vector3 to);
}
private readonly CMoveMap m_pMoveMap = new CMoveMap();
private Vector3 m_vOriginOverride;
private COptimizePath m_pPathOptimizer;
private Vector2Int m_ptStart;
private Vector2Int m_ptGoal;
private int m_iLayerStart;
private int m_iLayerGoal;
private readonly List<Vector3> m_path3D = new List<Vector3>(1024);
public bool Load(string basePathNoExt, Func<string, byte[]> resolver, Vector3? originOverride)
{
// basePathNoExt corresponds to "maps\\<map>\\movemap\\r.._..-c.._..-l0" in C++.
// basePathNoExt 对应 C++ 的 "maps\\<map>\\movemap\\r.._..-c.._..-l0"。
string cfgName = basePathNoExt;
if (!cfgName.EndsWith(".cfg", StringComparison.OrdinalIgnoreCase))
{
cfgName += ".cfg";
}
byte[] cfgBytes = resolver(cfgName);
if (cfgBytes == null)
{
return false;
}
// Resolver for referenced files: in cfg, references are baseName + ".prmap" etc.
// cfg 内引用:baseName + ".prmap" 等。
bool ok = m_pMoveMap.Load(cfgBytes, resolver, basePathNoExt);
if (!ok)
{
return false;
}
if (originOverride.HasValue)
{
m_vOriginOverride = originOverride.Value;
m_pMoveMap.SetOrigin(m_vOriginOverride);
}
m_pMoveMap.SetOrigin(originOverride.Value);
CreateOptimizer();
return true;
}
void CreateOptimizer()
{
m_pPathOptimizer = new COptimizePath();
}
public bool IsReady()
{
// Original C++ requires MultiCluGraph; here we only require layer0 RMap.
// 原版 C++ 依赖 MultiCluGraph;这里最小实现只要求第0层 RMap。
var layer0 = m_pMoveMap.GetLayer(0);
return layer0 != null && layer0.GetRMap() != null;
}
@@ -98,8 +81,6 @@ namespace AutoMove
public int WhichLayer(Vector3 vWld, float dH, out float layerDist)
{
// Minimal: single layer 0 if passable.
// 最小实现:如果可通行则使用单层0。
layerDist = 0.0f;
var pt = TransWld2Map(vWld);
var layer0 = m_pMoveMap.GetLayer(0);
@@ -116,31 +97,72 @@ namespace AutoMove
return true;
}
public int GetPathCount() => m_path3D.Count;
public List<PathNode> Get2DPath()
{
return m_pPathOptimizer?.GetPath();
}
/// <summary>
/// Matches C++ CMoveAgent::GetOptimizeCatchCount — lookahead window for path following.
/// 对应 C++:无 PathOptimizer 时为 0FindNearest/Farthest 仅在单步索引上工作。
/// </summary>
public int GetOptimizeCatchCount() => 0;
public int GetPathCount()
{
if (m_pPathOptimizer != null)
{
var path = m_pPathOptimizer.GetPath();
if (path != null)
return path.Count;
}
return 0;
}
public int GetOptimizeCatchCount()
{
return m_pPathOptimizer != null ? m_pPathOptimizer.GetCatchCount() : 0;
}
public Vector3 Get2DPathNode(int index)
{
var path2d = Get2DPath();
if (path2d == null || index < 0 || index >= path2d.Count)
return Vector3.zero;
PathNode pathNode = path2d[index];
Vector2 ptWld = m_pMoveMap.TransMap2Wld(pathNode.ptMap.x, pathNode.ptMap.y);
return new Vector3(ptWld.x, 0f, ptWld.y);
}
public Vector3 Get3DPathNode(int index)
{
if (index < 0 || index >= m_path3D.Count) return Vector3.zero;
return m_path3D[index];
var path2d = Get2DPath();
if (path2d == null || index < 0 || index >= path2d.Count)
return Vector3.zero;
PathNode pathNode = path2d[index];
Vector2 ptWld = m_pMoveMap.TransMap2Wld(pathNode.ptMap.x, pathNode.ptMap.y);
float dh = m_pMoveMap.GetDH(pathNode.layer, (int)pathNode.ptMap.x, (int)pathNode.ptMap.y);
return new Vector3(ptWld.x, dh, ptWld.y);
}
public System.Collections.Generic.List<Vector3> GetFullPath()
/// <summary>Ported from C++ CMoveAgent::Optimize.</summary>
public bool Optimize(int moveIndex)
{
return new System.Collections.Generic.List<Vector3>(m_path3D);
if (m_pPathOptimizer == null)
return false;
if (!m_pPathOptimizer.NeedOptimize(moveIndex))
return false;
m_pPathOptimizer.StepOptimize();
return true;
}
public List<Vector3> GetFullPath()
{
var path = new List<Vector3>();
var path2d = Get2DPath();
if (path2d == null)
return path;
for (int i = 0; i < path2d.Count; i++)
path.Add(Get2DPathNode(i));
return path;
}
public bool Search(int nMaxExpand = -1)
{
// A* on layer0 rmap
// 在第0层 rmap 上执行 A*
m_path3D.Clear();
var layer0 = m_pMoveMap.GetLayer(0);
var rmap = layer0?.GetRMap();
if (rmap == null) return false;
@@ -149,30 +171,18 @@ namespace AutoMove
if (w <= 0 || h <= 0) return false;
if (!InBounds(m_ptStart, w, h) || !InBounds(m_ptGoal, w, h))
{
return false;
}
// If goal or start is not passable, try to find nearest passable (original MoveAgent does this).
// 如果起点或终点不可通行,尝试寻找最近可通行点(原版 MoveAgent 会这样做)。
if (!rmap.GetPixel(m_ptStart.x, m_ptStart.y))
{
if (!TryFindNearestPassable(rmap, m_ptStart, w, h, 64, out var newStart))
{
if (DEBUG_AUTOPF) Debug.LogWarning($"[CMoveAgent] start not passable and no nearest passable found. start={m_ptStart}");
return false;
}
if (DEBUG_AUTOPF) Debug.Log($"[CMoveAgent] Adjust start {m_ptStart} -> {newStart}");
m_ptStart = newStart;
}
if (!rmap.GetPixel(m_ptGoal.x, m_ptGoal.y))
{
if (!TryFindNearestPassable(rmap, m_ptGoal, w, h, 64, out var newGoal))
{
if (DEBUG_AUTOPF) Debug.LogWarning($"[CMoveAgent] goal not passable and no nearest passable found. goal={m_ptGoal}");
return false;
}
if (DEBUG_AUTOPF) Debug.Log($"[CMoveAgent] Adjust goal {m_ptGoal} -> {newGoal}");
m_ptGoal = newGoal;
}
@@ -184,8 +194,6 @@ namespace AutoMove
open.Push(m_ptStart, Heuristic(m_ptStart, m_ptGoal));
int expands = 0;
//ToDo: need use another method to caculate the value of maxExpand
//800000 is a magic number, need to be optimized
int maxExpand = nMaxExpand > 0 ? nMaxExpand : 800000;
while (open.Count > 0 && expands < maxExpand)
@@ -228,16 +236,12 @@ namespace AutoMove
if (rmap == null) return false;
// Check origin first
// 先检查原点
if (origin.x >= 0 && origin.x < w && origin.y >= 0 && origin.y < h && rmap.GetPixel(origin.x, origin.y))
{
best = origin;
return true;
}
// Expand square rings
// 按方形“圈”扩展搜索
for (int r = 1; r <= maxRadius; r++)
{
int minX = Math.Max(0, origin.x - r);
@@ -245,14 +249,11 @@ namespace AutoMove
int minY = Math.Max(0, origin.y - r);
int maxY = Math.Min(h - 1, origin.y + r);
// Top/bottom edges
for (int x = minX; x <= maxX; x++)
{
TryConsider(x, minY);
TryConsider(x, maxY);
}
// Left/right edges (excluding corners already checked)
for (int y = minY + 1; y <= maxY - 1; y++)
{
TryConsider(minX, y);
@@ -285,28 +286,35 @@ namespace AutoMove
private void ReconstructPath(Dictionary<Vector2Int, Vector2Int> cameFrom, Vector2Int cur)
{
List<Vector2Int> rev = new List<Vector2Int>(1024) { cur };
var rev = new List<Vector2Int>(1024) { cur };
while (cameFrom.TryGetValue(cur, out var prev))
{
cur = prev;
rev.Add(cur);
}
rev.Reverse();
// Convert to world positions (y will be resolved by host terrain in movement).
// 转为世界坐标(y 由移动逻辑/地形解析)。
var initPath = new List<PathNode>(rev.Count);
for (int i = 0; i < rev.Count; i++)
{
m_path3D.Add(Map2Wld(rev[i]));
initPath.Add(new PathNode
{
ptMap = new Vector2(rev[i].x, rev[i].y),
layer = m_iLayerStart,
});
}
if (m_pPathOptimizer == null)
CreateOptimizer();
// C++: m_pPathOptimizer->SetupOptimize(GetMoveMap(), path);
m_pPathOptimizer.SetupOptimize(m_pMoveMap, initPath);
}
private static bool InBounds(Vector2Int p, int w, int h) => p.x >= 0 && p.x < w && p.y >= 0 && p.y < h;
private static int Heuristic(Vector2Int a, Vector2Int b)
{
// Octile distance * 10
// 八方向启发式(octile*10
int dx = Mathf.Abs(a.x - b.x);
int dy = Mathf.Abs(a.y - b.y);
int min = Math.Min(dx, dy);
@@ -316,8 +324,6 @@ namespace AutoMove
private static int Cost(Vector2Int a, Vector2Int b)
{
// Diagonal=14, straight=10
// 斜向=14,直向=10
int dx = Mathf.Abs(a.x - b.x);
int dy = Mathf.Abs(a.y - b.y);
return (dx + dy == 2) ? 14 : 10;
@@ -337,33 +343,13 @@ namespace AutoMove
public void ResetSearch()
{
// Çå³ýµ±Ç°ËÑË÷״̬
//if (m_iStat == PF_STATE_UNKNOWN)
//{
// return;
//}
//if (m_pPfAlg)
//{
// m_pPfAlg->Reset();
//}
//if (m_pPathOptimizer)
//{
// m_pPathOptimizer.Reset();
//}
m_ptStart.x = m_ptStart.y = 0;
m_ptStart = Vector2Int.zero;
m_iLayerStart = -1;
m_ptGoal.x = m_ptGoal.y = 0;
m_ptGoal = Vector2Int.zero;
m_iLayerGoal = -1;
//m_pBrushTest = null;
//m_iStat = PF_STATE_UNKNOWN;
m_path3D.Clear();
m_pPathOptimizer?.Reset();
}
/// <summary>
/// Very small min-heap for A*.
/// A* 用的小型最小堆。
/// </summary>
private sealed class MinHeap
{
private struct Node
@@ -419,5 +405,3 @@ namespace AutoMove
}
}
}
@@ -56,8 +56,13 @@ namespace AutoMove
public Vector2 TransMap2Wld(int x, int y)
{
float wx = m_vOrigin.x + (x + 0.5f) * m_fPixelSize;
float wz = m_vOrigin.z + (y + 0.5f) * m_fPixelSize;
return TransMap2Wld((float)x, (float)y);
}
public Vector2 TransMap2Wld(float mapX, float mapY)
{
float wx = m_vOrigin.x + (mapX + 0.5f) * m_fPixelSize;
float wz = m_vOrigin.z + (mapY + 0.5f) * m_fPixelSize;
return new Vector2(wx, wz);
}
@@ -0,0 +1,298 @@
using System.Collections.Generic;
using UnityEngine;
// Ported from C++ OptimizePath.h / OptimizePath.cpp (Kui Wu, 2007).
namespace AutoMove
{
/// <summary>Line sampler for map-space path optimization (C++ CLine).</summary>
sealed class CLine
{
Vector2 m_from;
Vector2 m_dir;
int m_count;
public void Init(Vector2 from, Vector2 dir)
{
m_from = from;
m_dir = dir;
float len = m_dir.magnitude;
if (len > 1e-8f)
{
m_dir.x /= len;
m_dir.y /= len;
}
m_count = 0;
}
public void Init(Vector2 from, int dirX, int dirY)
{
Init(from, new Vector2(dirX, dirY));
}
public Vector2 Next()
{
m_count++;
return new Vector2(m_from.x + m_dir.x * m_count, m_from.y + m_dir.y * m_count);
}
public int GetCount() => m_count;
public Vector2 GetFrom() => m_from;
public void Reset() => m_count = 0;
}
/// <summary>Path optimizer — port of C++ COptimizePath.</summary>
public sealed class COptimizePath
{
const int LookAhead = 60;
const int LookStep = 3;
CMoveMap m_pMoveMap;
int m_mapWidth;
int m_mapHeight;
readonly Dictionary<int, short> m_lookUp = new Dictionary<int, short>(4096);
readonly List<PathNode> m_path = new List<PathNode>(1024);
int m_curIndex = -1;
int m_catchCount = 10;
int m_curLayer = -1;
public void Reset()
{
m_pMoveMap = null;
m_mapWidth = 0;
m_mapHeight = 0;
m_lookUp.Clear();
m_path.Clear();
m_curIndex = -1;
m_curLayer = -1;
}
public int GetCatchCount() => m_catchCount;
public List<PathNode> GetPath() => m_path;
public bool NeedOptimize(int moveIndex)
{
if (m_curIndex < m_path.Count
&& moveIndex < m_path.Count
&& m_curIndex - moveIndex < m_catchCount)
{
if (moveIndex > m_curIndex)
{
SetFootprintRange(m_curIndex, moveIndex - 1, 0);
m_curIndex = moveIndex;
}
return true;
}
return false;
}
public void SetupOptimize(CMoveMap pMoveMap, List<PathNode> initPath, int catchCount = 10)
{
m_path.Clear();
if (initPath != null && initPath.Count > 0)
m_path.AddRange(initPath);
m_pMoveMap = pMoveMap;
m_mapWidth = pMoveMap != null ? pMoveMap.GetMapWidth() : 0;
m_mapHeight = pMoveMap != null ? pMoveMap.GetMapLength() : 0;
m_curIndex = 0;
m_catchCount = catchCount;
m_curLayer = -1;
if (m_path.Count == 0)
return;
CheckLayer();
}
public void StepOptimize()
{
CheckLayer();
int step = m_catchCount * 2;
if (step <= 0)
return;
int i = 0;
while (i < step && m_curIndex < m_path.Count)
{
LocalOptimize();
i++;
m_curIndex++;
}
}
void CheckLayer()
{
if (m_path.Count == 0 || m_curIndex < 0 || m_curIndex >= m_path.Count)
return;
if (m_path[m_curIndex].layer == m_curLayer)
return;
m_curLayer = m_path[m_curIndex].layer;
m_lookUp.Clear();
int i = m_curIndex;
while (i < m_path.Count && m_path[i].layer == m_curLayer)
{
SetFootprint(m_path[i].ptMap, (short)(i + 1));
i++;
}
}
void LocalOptimize()
{
int toIndex = Mathf.Min(m_curIndex + LookAhead, m_path.Count - 1);
var line = new CLine();
Vector2 dir;
int newCount = -1;
while (toIndex - m_curIndex > LookStep)
{
if (GetFootprint(m_path[toIndex].ptMap) == 0)
{
toIndex -= LookStep;
continue;
}
if (m_path[toIndex].layer != m_curLayer)
{
toIndex -= LookStep;
continue;
}
dir = m_path[toIndex].ptMap - m_path[m_curIndex].ptMap;
if ((int)dir.x == 0 && (int)dir.y == 0)
{
PathIntersect(toIndex);
return;
}
line.Init(m_path[m_curIndex].ptMap, dir);
if (LineTo(line, m_path[toIndex].ptMap))
{
newCount = line.GetCount();
break;
}
toIndex -= LookStep;
}
if (newCount > 0)
{
line.Reset();
AddPathPortion(line, toIndex - m_curIndex, newCount);
}
}
CBitImage GetRMap(int layer)
{
if (m_pMoveMap == null)
return null;
var layerMap = m_pMoveMap.GetLayer(layer);
return layerMap?.GetRMap();
}
bool LineTo(CLine line, Vector2 to)
{
int toX = (int)to.x;
int toY = (int)to.y;
int curX = (int)line.GetFrom().x;
int curY = (int)line.GetFrom().y;
CBitImage pRMap = GetRMap(m_curLayer);
if (pRMap == null)
return false;
int lastX = curX;
int lastY = curY;
while (curX != toX || curY != toY)
{
Vector2 cur = line.Next();
curX = (int)cur.x;
curY = (int)cur.y;
if (!pRMap.GetPixel(curX, curY))
return false;
bool bNeighbor1 = pRMap.GetPixel(lastX, curY);
bool bNeighbor2 = pRMap.GetPixel(curX, lastY);
if (toX == lastX && toY == curY)
break;
if (toX == curX && toY == lastY)
break;
if (curX != lastX && curY != lastY && (!bNeighbor1 || !bNeighbor2))
return false;
lastX = curX;
lastY = curY;
}
return true;
}
void PathIntersect(int toIndex)
{
SetFootprintRange(m_curIndex + 1, toIndex, 0);
int removeCount = toIndex - m_curIndex;
if (removeCount > 0 && m_curIndex + 1 < m_path.Count)
m_path.RemoveRange(m_curIndex + 1, removeCount);
}
void AddPathPortion(CLine line, int oldCount, int newCount)
{
SetFootprintRange(m_curIndex + 1, m_curIndex + oldCount, 0);
if (oldCount > newCount)
{
m_path.RemoveRange(m_curIndex + 1, oldCount - newCount);
}
else if (oldCount < newCount)
{
int insertCount = newCount - oldCount;
for (int k = 0; k < insertCount; k++)
m_path.Insert(m_curIndex + 1, new PathNode());
}
int index = m_curIndex + 1;
while (line.GetCount() < newCount && index < m_path.Count)
{
var node = m_path[index];
node.ptMap = line.Next();
node.layer = m_curLayer;
m_path[index] = node;
index++;
}
SetFootprintRange(m_curIndex + 1, m_curIndex + newCount, 1);
}
int GetLookUpKey(int x, int y) => y * m_mapWidth + x;
int GetFootprint(int x, int y)
{
return m_lookUp.TryGetValue(GetLookUpKey(x, y), out short v) ? v : 0;
}
int GetFootprint(Vector2 pt) => GetFootprint((int)pt.x, (int)pt.y);
void SetFootprint(int x, int y, int val)
{
int key = GetLookUpKey(x, y);
if (val == 0)
m_lookUp.Remove(key);
else
m_lookUp[key] = (short)val;
}
void SetFootprint(Vector2 pt, int val) => SetFootprint((int)pt.x, (int)pt.y, val);
void SetFootprintRange(int fromIndex, int toIndex, int val)
{
for (int i = fromIndex; i <= toIndex && i < m_path.Count; ++i)
SetFootprint(m_path[i].ptMap, val);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 96e45cded56a0bc4188f329f2cf3b676
@@ -0,0 +1,22 @@
using UnityEngine;
namespace AutoMove
{
/// <summary>Matches C++ AutoMove::PathNode (PfConstant.h).</summary>
public struct PathNode
{
public Vector2 ptMap;
public int layer;
public Vector2Int GetPtI()
{
return new Vector2Int(Mathf.FloorToInt(ptMap.x), Mathf.FloorToInt(ptMap.y));
}
public void SetI(Vector2Int pt, int iLayer)
{
ptMap = new Vector2(pt.x, pt.y);
layer = iLayer;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 98c1e0dc325b8e64bbda287a0128aeed
@@ -19,7 +19,7 @@ namespace BrewMonster.Scripts
/// </summary>
public sealed class CECIntelligentRoute
{
private const bool DEBUG_AUTOPF = true;
private const bool DEBUG_AUTOPF = false;
/// <summary>cos(5°) for FindFarthestNode — same as C++ EC_IntelligentRoute.cpp.</summary>
private static readonly float s_cos5Deg = Mathf.Cos(5f * Mathf.PI / 180f);
public enum SearchResult
@@ -376,9 +376,22 @@ namespace BrewMonster.Scripts
// Determine layers (minimal: layer0 or invalid).
// 确定层(最小实现:层0或无效)。
float startDh = 0f;
float endDh = 0f;
var world = EC_Game.GetGameRun()?.GetWorld();
if (world != null)
{
A3DVECTOR3 n = GPDataTypeHelper.g_vAxisY;
if (world.TryGetTerrainHeight(start, ref n, out float startTer))
startDh = start.y - startTer;
n = GPDataTypeHelper.g_vAxisY;
if (world.TryGetTerrainHeight(end, ref n, out float endTer))
endDh = end.y - endTer;
}
float dist;
int startLayer = agent.WhichLayer(new Vector3(start.x, start.y, start.z), 0.0f, out dist);
int endLayer = agent.WhichLayer(new Vector3(end.x, end.y, end.z), 0.0f, out dist);
int startLayer = agent.WhichLayer(new Vector3(start.x, start.y, start.z), startDh, out dist);
int endLayer = agent.WhichLayer(new Vector3(end.x, end.y, end.z), endDh, out dist);
if (startLayer < 0) startLayer = 0;
if (endLayer < 0) endLayer = 0;
@@ -466,7 +479,8 @@ namespace BrewMonster.Scripts
m_iCurDest = FindNextNode(pos, m_iCurDest + 1);
m_dist2CurDest = (GetCurDest() - pos).MagnitudeH();
// C++ calls agent->Optimize(m_iCurDest) here; Unity CMoveAgent has no path optimizer yet.
agent.Optimize(m_iCurDest);
m_dist2CurDest = (GetCurDest() - pos).MagnitudeH();
}
A3DVECTOR3 GetNodePos(int iNode)
@@ -489,17 +503,26 @@ namespace BrewMonster.Scripts
if (agent == null)
return new A3DVECTOR3(0f, 0f, 0f);
Vector3 v = agent.Get3DPathNode(iNode);
// C++ adds GetTerrainHeight to Y; Unity path nodes use movemap/world height from loader.
return new A3DVECTOR3(v.x, v.y, v.z);
var pos = new A3DVECTOR3(v.x, v.y, v.z);
var world = EC_Game.GetGameRun()?.GetWorld();
if (world != null)
{
A3DVECTOR3 n = GPDataTypeHelper.g_vAxisY;
if (world.TryGetTerrainHeight(pos, ref n, out float terY))
pos.y = terY;
}
return pos;
}
A3DVECTOR3 GetNodePosXZ(int iNode)
{
if (IsIdle())
return new A3DVECTOR3(0f, 0f, 0f);
A3DVECTOR3 p = GetNodePosNoCheck(iNode);
p.y = 0f;
return p;
RangedMoveAgent? pCurAgent = GetCurAgent();
if (pCurAgent == null || pCurAgent.Value.agent == null)
return new A3DVECTOR3(0f, 0f, 0f);
Vector3 v = pCurAgent.Value.agent.Get2DPathNode(iNode);
return new A3DVECTOR3(v.x, v.y, v.z);
}
int FindNearestNode(A3DVECTOR3 curPos, int iNodeFrom)
+8 -1
View File
@@ -1366,7 +1366,14 @@ public class CECModel
{
return false;
}
if(m_skeletonBuilder == null )
{
return false;
}
if(pChild.m_skeletonBuilder == null)
{
return false;
}
Transform hook = m_skeletonBuilder.GetHook(szHookName, true);
Transform hangger = pChild.m_skeletonBuilder.GetHook(szCCName, true);
if (hook == null || hangger == null)
@@ -193,7 +193,7 @@ namespace BrewMonster.Scripts
m_pNavigateModel = new CECModel();
m_pNavigateModel.m_pPlayerModel = instance;
SkeletonBuilder skeletonBuilder = instance.GetComponentInChildren<SkeletonBuilder>(true);
NamedAnimancerComponent animancer = instance.GetComponent<NamedAnimancerComponent>();
NamedAnimancerComponent animancer = instance.GetComponentInChildren<NamedAnimancerComponent>();
if (skeletonBuilder != null && animancer != null)
{
@@ -214,7 +214,6 @@ namespace BrewMonster.Scripts
CameraController.Instance.UpdateFollowObject(m_pNavigateModel.transform);
TickInvoker.Instance.RegisterTickable(this);
// Clone host player model/equipment // 克隆宿主玩家模型与装备
BMLogger.Log($"CECHostNavigatePlayer::InitAsync, debugStep: {debugStep++}");
bool loaded = await LoadFrom(m_pHostPlayer, false);
if (loaded)
SetupNavigatePlayerVisual();
@@ -278,6 +277,7 @@ namespace BrewMonster.Scripts
base.Tick(dwDeltatime);
if (!m_bNavigateModelApplied /*&& IsAllResReady()*/)
{
//TOdo: implement IsAllresReady
ApplyNavigateModel();
// if(!IsShapeChanged())
// ApplyNavigateModel();
@@ -365,8 +365,11 @@ namespace BrewMonster.Scripts
private bool ApplyNavigateModel()
{
// if ( !GetMajorModel() || m_pNavigateModel == null || m_bNavigateModelApplied)
// return false;
if ( !GetMajorModel() || m_pNavigateModel == null || m_bNavigateModelApplied)
{
BMLogger.Log($"GetMajorModel(){GetMajorModel() == true } m_NavigateMode {m_pNavigateModel == null} m_bNavigateModelApplied {m_bNavigateModelApplied} ");
return false;
}
A3DVECTOR3 vCurPos = GetPos();
+17 -1
View File
@@ -32,9 +32,25 @@ namespace BrewMonster.Scripts.World
return m_pOnmtMan;
}
public float GetTerrainHeight(A3DVECTOR3 vPos, ref A3DVECTOR3 pvNormal /* NULL */)
{
A3DVECTOR3 normal = pvNormal;
if (!TryGetTerrainHeight(vPos, ref normal, out float height))
return vPos.y;
pvNormal = normal;
return height;
}
/// <summary>False when terrain is not loaded yet (e.g. during early AutoPF Search).</summary>
public bool TryGetTerrainHeight(A3DVECTOR3 vPos, ref A3DVECTOR3 pvNormal, out float height)
{
A3DTerrain2 pTerrain = GetTerrain();
return pTerrain.GetPosHeight(vPos, ref pvNormal);
if (pTerrain == null)
{
height = vPos.y;
return false;
}
height = pTerrain.GetPosHeight(vPos, ref pvNormal);
return true;
}
public float GetWaterHeight(A3DVECTOR3 vPos)