/// <summary> /// 确保垂直方向上两个Span之间的区域不会卡头 /// </summary> /// <param name="field"></param> private void markLowHeightSpans(SolidHeightfield field) { SolidHeightfield.SolidHeightfieldIterator iter = field.GetEnumerator(); while (iter.MoveNext()) { HeightSpan span = iter.Current; if ((span.flags() & SpanFlags.WALKABLE) == 0) { continue; } int spanFloor = span.max(); //SolidSpan的max,其实就是OpenSpan的底部 int spanCeiling = (span.next() != null) ? span.next().min() : int.MaxValue; if (spanCeiling - spanFloor <= mMinTraversableHeight) { span.setFlags(span.flags() & ~SpanFlags.WALKABLE); } } }
public OpenHeightfield build(SolidHeightfield sourceField , bool performFullGeneration ) { if( sourceField == null ) { Logger.LogError("[OpenHeightfieldBuilder][build] sourceField null "); return null; } OpenHeightfield result = new OpenHeightfield( sourceField.boundsMin(), sourceField.boundsMax(), sourceField.cellSize(), sourceField.cellHeight() ); for(int depthIndex = 0; depthIndex < sourceField.depth(); depthIndex++) { for(int widthIndex = 0; widthIndex < sourceField.width(); widthIndex++) { OpenHeightSpan baseSpan = null; OpenHeightSpan previousSpan = null; for (HeightSpan span = sourceField.getData(widthIndex, depthIndex); span != null; span = span.next() ) { if ( span.flags() != mFilterFlags ) { continue; } //当前Solid Span的max对应的是对应OpenSpan的floor int floor = span.max(); //下一个Next Solid Span的min对应当前OpenSpan的Ceil。 int ceiling = (span.next() != null ? span.next().min() : int.MaxValue) ; //对应的Open Span OpenHeightSpan oSpan = new OpenHeightSpan(floor, (ceiling - floor ) ); if( baseSpan == null ) { baseSpan = oSpan; } if( previousSpan != null ) { previousSpan.setNext(oSpan); } previousSpan = oSpan; result.incrementSpanCount(); } //for if( baseSpan != null ) { result.addData(widthIndex, depthIndex, baseSpan); } }//for } //for if( performFullGeneration ) { generateNeighborLinks(result); generateDistanceField(result); blurDistanceField(result); generateRegions(result); } return result; }
/// <summary> /// 注意这个添加Span的参数,heightIndexMin和heightIndexMax貌似 /// 都变成了Span的闭区间 /// </summary> /// <param name="widthIndex"></param> /// <param name="depthIndex"></param> /// <param name="heightIndexMin"></param> /// <param name="heightIndexMax"></param> /// <param name="flags"></param> /// <returns></returns> public bool addData(int widthIndex, int depthIndex, int heightIndexMin, int heightIndexMax, int flags ) { if (widthIndex < 0 || widthIndex >= width() || depthIndex < 0 || depthIndex >= depth()) { Logger.LogWarning("[SolidHeightfield][addData]width|depth|{0}|{1}|{2}|{3}", widthIndex, depthIndex, heightIndexMin, heightIndexMax); return(false); } if (heightIndexMin < 0 || heightIndexMax < 0 || heightIndexMin > heightIndexMax) { Logger.LogWarning("[SolidHeightfield][addData]heightMin|heightMax|{0}|{1}|{2}|{3}", widthIndex, depthIndex, heightIndexMin, heightIndexMax); return(false); } if (mSpans == null) { return(false); } int gridIndex = GetGridIndex(widthIndex, depthIndex); HeightSpan currentSpan; if (!mSpans.TryGetValue(gridIndex, out currentSpan)) { mSpans.Add(gridIndex, new HeightSpan(heightIndexMin, heightIndexMax, flags)); return(true); } //各种情况的合并 HeightSpan previousSpan = null; while (currentSpan != null) { //最小比最大还大,不重叠 // /* * * - currentMin * - heigtMax * - * - * - * - heightMin * */ if (currentSpan.min() > heightIndexMax + 1) //加1的理由看上图,低闭高开 { HeightSpan newSpan = new HeightSpan(heightIndexMin, heightIndexMax, flags); newSpan.setNext(currentSpan); //newSpan更加矮 //没有更加矮的了 if (previousSpan == null) { //更新最矮的Span mSpans[gridIndex] = newSpan; } else { previousSpan.setNext(newSpan); } return(true); } //当前Gird对应的最高,比新的最矮还要矮 else if (currentSpan.max() < heightIndexMin - 1) { //这一列只有一个Span if (currentSpan.next() == null) { currentSpan.setNext( new HeightSpan(heightIndexMin, heightIndexMax, flags)); return(true); } //这一列还有其它Span,所以要找到插入的点 previousSpan = currentSpan; currentSpan = currentSpan.next(); } //重叠或者刚好邻接 else { /* Case 1: * 新的比当前矮,更新最矮点 * 最高点相同,合并可走的标志位 */ if (heightIndexMin < currentSpan.min()) { currentSpan.setMin(heightIndexMin); } if (heightIndexMax == currentSpan.max()) { currentSpan.setFlags(currentSpan.flags() | flags); return(true); } /* Case 2: * 最高点没有更新 * */ if (currentSpan.max() > heightIndexMax) { return(true); } //最高点更高了 HeightSpan nextSpan = currentSpan.next(); while (true) { //找到第一个刚好比新的最高点高的 if (nextSpan == null || nextSpan.min() > heightIndexMax + 1) { currentSpan.setMax(heightIndexMax); currentSpan.setFlags(flags); if (nextSpan == null) { currentSpan.setNext(null); } else { currentSpan.setNext(nextSpan); } return(true); } //刚好邻接,或者旧的高度更加高 if (nextSpan.min() == heightIndexMax + 1 || heightIndexMax <= nextSpan.max()) { //吞并旧的 currentSpan.setMax(nextSpan.max()); currentSpan.setNext(nextSpan.next()); currentSpan.setFlags(nextSpan.flags()); if (heightIndexMax == currentSpan.max()) { currentSpan.setFlags(currentSpan.flags() | flags); return(true); } return(true); } nextSpan = nextSpan.next(); } } } return(false); }
/// <summary> /// 边缘裁剪,那种像断崖式的Span也不可走的 /// </summary> /// <param name="field"></param> private void markLedgeSpans(SolidHeightfield field) { SolidHeightfield.SolidHeightfieldIterator iter = field.GetEnumerator(); while (iter.MoveNext()) { HeightSpan span = iter.Current; if ((span.flags() & SpanFlags.WALKABLE) == 0) { continue; } int widthIndex = iter.widthIndex(); int depthIndex = iter.depthIndex(); int currFloor = span.max(); int currCeiling = (span.next() != null) ? span.next().min() : int.MaxValue; int minDistanceToNeighbor = int.MaxValue; for (int dir = 0; dir < 4; dir++) { int nWidthIndex = widthIndex + BoundeField.getDirOffsetWidth(dir); int nDepthIndex = depthIndex + BoundeField.getDirOffsetDepth(dir); HeightSpan nSpan = field.getData(nWidthIndex, nDepthIndex); if (null == nSpan) { //TODO 这里没有搞懂为啥是 -mMaxTraversableStep - currFloor,是为了比-mMaxTraversableStep更小,以便直接判断不能行走吗? // 用大可行的距离,再减去currFloor,得到的肯定是一个更小的值。 // currFloor - mMaxTraversableStep 是一个最大允许的落差地板距离。注意这里只考虑下落的情况 minDistanceToNeighbor = Math.Min(minDistanceToNeighbor, -mMaxTraversableStep - currFloor); continue; } /* * 先考虑一种特殊情况 ,那就是 * 那就是nSpan.min也比currFloor要高,那么对应的 * 的邻居相当于也是没有Floor的,所以默认取-mMaxTraversableStep吧。 */ int nFloor = -mMaxTraversableStep; int nCeiling = nSpan.min(); //当前Span所处列的currCeiling和邻居的nCeiling相比,取最低的 if (Math.Min(currCeiling, nCeiling) - currFloor > mMinTraversableHeight) { minDistanceToNeighbor = Math.Min(minDistanceToNeighbor, (nFloor - currFloor)); } for (nSpan = field.getData(nWidthIndex, nDepthIndex); nSpan != null; nSpan = nSpan.next()) { nFloor = nSpan.max(); //现在才开始用max考虑真正存在的Floor nCeiling = (nSpan.next() != null) ? nSpan.next().min() : int.MaxValue; if (Math.Min(currCeiling, nCeiling) - Math.Max(currFloor, nFloor) > mMinTraversableHeight) { minDistanceToNeighbor = Math.Min(minDistanceToNeighbor, (nFloor - currFloor)); } } } //如果最近的距离比较最大掉落还小的放在,那么就是不可行走的 if (minDistanceToNeighbor < -mMaxTraversableStep) { span.setFlags(span.flags() & ~SpanFlags.WALKABLE); } } }