private NavigationData GetEmptyNavigationData(GridUnitData _thisGrid, NavigationData _preGrid, int _G, int _H) { //优先从池子里取出 NavigationData nd = null; if (curUsedIdx < navigationDataPool.Count) { nd = navigationDataPool[curUsedIdx]; } else { nd = new NavigationData(); navigationDataPool.Add(nd); } ++curUsedIdx; nd.thisGrid = _thisGrid; nd.preGrid = _preGrid; nd.G = _G; nd.H = _H; nd.F = _G + _H; nd.open = true; nd.thisGrid.tempRef = nd; return(nd); }
//准备加载战场 private void PrepareBattleMap() { if (currentData == null) { EUtilityHelperL.LogError("Prepare battle map failed. No battle data."); return; } gridUnits = new GridUnit[currentData.mapWidth, currentData.mapHeight]; for (int row = 0; row < currentData.mapHeight; ++row) { for (int column = 0; column < currentData.mapWidth; ++column) { GridUnitData gud = currentData.mapGrids[column, row]; if (gud != null) { //创建一个用于显示的格子对象 GridUnit gu = CreateGrid(); if (gu != null) { gridUnits[column, row] = gu; gu.transform.localPosition = gud.localPosition; gu.name = string.Format("Grid_{0}_{1}", row, column); gu.gridData = gud; gu.Refresh(); gu.gameObject.SetActive(true); } } } } }
//准备加载战场 private void PrepareBattleMap() { if (currentData == null) { EUtilityHelperL.LogError("Prepare battle map failed. No battle data."); return; } gridUnits = new GridUnit[currentData.mapData.mapWidth, currentData.mapData.mapHeight]; for (int r = 0; r < currentData.mapData.mapHeight; ++r) { for (int c = 0; c < currentData.mapData.mapWidth; ++c) { GridUnitData gud = currentData.mapData.mapGrids[c, r]; if (gud != null) { //创建一个用于显示的格子对象 GridUnit gridUnit = CreateGrid(); if (gridUnit != null) { gridUnits[c, r] = gridUnit; gridUnit.transform.localPosition = gud.localPosition; gridUnit.name = string.Format("Grid_{0}_{1}", r, c); gridUnit.gridData = gud; gridUnit.RefreshColor(); gridUnit.gameObject.SetActive(true); } } } } }
//放置一些障碍格子 private void GenerateObstacle(int obstacleCount, int gap) { //随机范围 List <GridUnitData> randomRange = new List <GridUnitData>(); //减掉不能随机的格子 List <GridUnitData> reduction = new List <GridUnitData>(); //将普通格子放入 foreach (var grid in mapGrids) { if (grid.GridType == GridType.Normal) { randomRange.Add(grid); } } int count = obstacleCount; while (count > 0 && randomRange.Count > 0) { int randIdx = Random.Range(0, randomRange.Count); GridUnitData randomGrid = randomRange[randIdx]; randomGrid.GridType = GridType.Obstacle; //排除格子周围的格子 GetRangeGrids(randomGrid.row, randomGrid.column, gap, reduction); if (reduction.Count > 0) { foreach (var item in reduction) { randomRange.Remove(item); } } --count; } }
//计算两格子之间的距离 public int Distance(GridUnitData target) { //行之间的差 int rowGap = Mathf.Abs(target.row - row); //移动行后所覆盖的最小、最大横坐标 int minColumn = 0; int maxColumn = 0; //奇数行开始时 if ((row & 1) == (EGameConstL.Map_FirstRowOffset ? 0 : 1)) { minColumn = Mathf.Max(column - (rowGap / 2), 0); maxColumn = column + ((rowGap + 1) / 2); } //偶数行开始时 else { minColumn = Mathf.Max(column - ((rowGap + 1) / 2), 0); maxColumn = column + (rowGap / 2); } //在移动范围之外,额外增加 if (target.column < minColumn) { return(rowGap + minColumn - target.column); } else if (target.column > maxColumn) { return(rowGap + target.column - maxColumn); } //在移动范围之内,因此行移动量就是两格子的距离 else { return(rowGap); } }
public override bool Equals(object obj) { if (obj is GridUnitData) { GridUnitData data = (GridUnitData)obj; return(data.mapID == mapID && data.row == row && data.column == column); } return(false); }
public void Reset() { open = true; F = 0; G = 0; H = 0; //清空关联 if (thisGrid != null) { thisGrid.tempRef = null; thisGrid = null; } preGrid = null; }
//设置格子类型 private void SetGridType(GridUnitData gud, GridType gt) { switch (gt) { case GridType.Normal: normalGrids.Add(gud); break; case GridType.Obstacle: obstacleGrids.Add(gud); break; default: break; } gud.gridType = gt; }
//战场中铺设格子(信息) public void Generate(int width, int height, int obstacle, int gap) { if (width <= 0 || height <= 0) { return; } //地图编号自增 mapID = mapCount++; //记录地图宽高 mapWidth = width; mapHeight = height; //生成格子数组 mapGrids = new GridUnitData[mapWidth, mapHeight]; //记录普通格子和障碍格子 normalGrids = new List <GridUnitData>(); obstacleGrids = new List <GridUnitData>(); //全部生成为普通格子 for (int r = 0; r < mapHeight; ++r) { for (int c = 0; c < mapWidth; ++c) { GridUnitData gud = new GridUnitData(); gud.localPosition = new Vector3( c * EGameConstL.GridWidth + ((r & 1) > 0 ? (EGameConstL.GridWidth * 0.5f) : 0f), -r * EGameConstL.GridOffsetY, 0 ); //设置格子参数 gud.gridPosition = new Vector2Int(r, c); //位置 //初始设置为普通格子 SetGridType(gud, GridType.Normal); //保存 mapGrids[c, r] = gud; } } //随机一些障碍格子 DisposeGridUnits(obstacle, gap); }
//战场中铺设格子 public void Generate(int width, int height, int obstacleCount, int gap) { EUtilityHelperL.TimerStart(); if (width <= 0 || height <= 0) { return; } //记录地图宽高 mapWidth = width; mapHeight = height; //生成格子数组 mapGrids = new GridUnitData[mapWidth, mapHeight]; //全部生成为普通格子 for (int r = 0; r < mapHeight; ++r) { for (int c = 0; c < mapWidth; ++c) { GridUnitData gridUnitData = new GridUnitData(mapID, r, c); gridUnitData.localPosition = new Vector3( c * EGameConstL.Map_GridWidth + ((r & 1) == (EGameConstL.Map_FirstRowOffset ? 0 : 1) ? (EGameConstL.Map_GridWidth * 0.5f) : 0f), -r * EGameConstL.Map_GridOffsetY, 0 ); //初始设置为普通格子 gridUnitData.GridType = GridType.Normal; //保存 mapGrids[c, r] = gridUnitData; } } //随机一些障碍格子 GenerateObstacle(obstacleCount, gap); //整理格子列表 TidyGridList(); EUtilityHelperL.Log(string.Format("Generate map {0}, time cost:{1}", mapID, EUtilityHelperL.TimerEnd())); }
//计算两格子之间的距离 public int Distance(GridUnitData target) { //计算行移动量 int rowGap = Mathf.Abs(target.gridPosition.x - gridPosition.x); //列范围 - x int offset = (((rowGap & 1) == 0) ? 0 : 1) + rowGap / 2; //如果在范围内,移动量就是行移动量 if (target.gridPosition.y >= (gridPosition.y - offset) && (target.gridPosition.y <= (gridPosition.y + offset))) { //Debug.Log(string.Format("({0},{1})->({2},{3})->{4}", target.row, target.column, row, column, rowGap)); return(rowGap); } else if (target.gridPosition.y > (gridPosition.y + offset)) { //Debug.Log(string.Format("({0},{1})->({2},{3})->{4}", target.row, target.column, row, column, rowGap + (target.column - column - offset))); return(rowGap + (target.gridPosition.y - gridPosition.y - offset)); } else { //Debug.Log(string.Format("({0},{1})->({2},{3})->{4}", target.row, target.column, row, column, rowGap + column - offset - target.column)); return(rowGap + gridPosition.y - offset - target.gridPosition.y); } }
//放置一些障碍格子 private void DisposeGridUnits(int obstacle, int gap) { obstacle = Mathf.Min(mapWidth * mapHeight, obstacle); for (int i = 0; i < obstacle; ++i) { int randomIdx = -1; GridUnitData target = null; int tryTimes = 999; while (tryTimes > 0 && target == null) { randomIdx = Random.Range(0, normalGrids.Count); target = normalGrids[randomIdx]; //判断距离 for (int j = 0; j < obstacleGrids.Count; ++j) { var distance = obstacleGrids[j].Distance(target); if (obstacleGrids[j].Distance(target) < gap) { target = null; break; } } --tryTimes; } if (target != null) { SetGridType(target, GridType.Obstacle); normalGrids.RemoveAt(randomIdx); } else { EUtilityHelperL.LogWarning("Dispose grid unit data warning."); } } }
/// <summary> /// 导航 /// </summary> /// <param name="battleMap">地图信息</param> /// <param name="from">从哪出发</param> /// <param name="to">到哪里去</param> /// <param name="path">路径</param> /// <param name="searched">探索过但没采用的</param> /// <returns>是否导航成功</returns> public bool Navigate( BattleMapData battleMap, GridUnitData from, GridUnitData to, List <GridUnitData> path, List <GridUnitData> searched) { //没有设置地图 if (battleMap == null) { return(false); } if (path != null) { path.Clear(); } if (searched != null) { searched.Clear(); } int tryTimes = battleMap.GridCount; List <NavigationData> opening = new List <NavigationData>(); opening.Add(GetEmptyNavigationData(from, null, 0, from.Distance(to))); int retry = 0; bool catched = false; //当前探索方向 int curDir = 0; //上次探索方向 int lastDir = 0; //每次检测方向的次数 int checkTimes = 0; //判断是否需要遍历open列表 NavigationData gift = null; //距离最近的格子(接下来要移动的) NavigationData next_0 = null; //距离次近的格子 NavigationData next_1 = null; int minStep = EGameConstL.Infinity; while (retry <= tryTimes && !catched) { ++retry; //从open中查找最近的节点 if (gift != null) { next_0 = gift; gift = null; } else if (next_1 != null) { next_0 = next_1; next_1 = null; } else { minStep = EGameConstL.Infinity; for (int i = opening.Count - 1; i >= 0; --i) { if (!opening[i].open) { opening.RemoveAt(i); } else if (opening[i].F < minStep) { next_0 = opening[i]; minStep = next_0.F; } else if (next_1 == null && next_0 != null && opening[i].F == next_0.F) { next_1 = opening[i]; } } } //标志为已关闭 next_0.open = false; //放入已搜索中 if (searched != null) { searched.Add(next_0.thisGrid); } checkTimes = 6; curDir = lastDir; //遍历最近节点的周围6个节点,依次放入close中 int roads = next_0.thisGrid.passes; while (checkTimes > 0) { //沿着当前探索方向继续探索 if ((roads & (1 << curDir)) != 0) { //获取该路通向的下一个item GridUnitData sibling = battleMap.GetGridDataByDir(next_0.thisGrid.row, next_0.thisGrid.column, curDir); if (sibling == null) { //没路 ++curDir; curDir = (curDir > 5) ? 0 : curDir; --checkTimes; continue; } //如果这个不能移动 else if (sibling.GridType == GridType.Obstacle) { //没路 ++curDir; curDir = (curDir > 5) ? 0 : curDir; --checkTimes; continue; } else { //如果这个item就是目标 if (sibling.Equals(to)) { catched = true; if (path != null) { NavigationData current = next_0; while (current != null) { if (current.thisGrid != from) { path.Add(current.thisGrid); } current = current.preGrid; } } break; } else { //尝试判断这个是否为closed NavigationData nd = sibling.tempRef == null ? null : (NavigationData)(sibling.tempRef); if (nd == null) { //这个格子没有探索过,新建并添加 nd = GetEmptyNavigationData(sibling, next_0, next_0.G + 1, sibling.Distance(to)); //这个格子不错哦 if (nd.F <= next_0.F && gift == null) { //保存礼物 gift = nd; //记录下次起始的更新方向 lastDir = curDir; } //比第二目标好 else if (next_1 != null && nd.F < next_1.F) { //替换第二目标 next_1 = nd; opening.Add(nd); } else { //已经设置了礼物,因此只能放入opening列表中,以后再更新了呢 opening.Add(nd); } } else { //只处理没有被探索过的格子 if (nd.open) { //已经在Open列表中了 if ((next_0.G + 1) < nd.G) { //比原来的近,应该不可能 nd.G = next_0.G + 1; nd.H = sibling.Distance(to); nd.F = nd.G + nd.H; nd.preGrid = next_0; nd.thisGrid = sibling; } //这个格子不错哦 if (nd.F <= next_0.F && gift == null) { gift = nd; //保存当前探索方向 lastDir = curDir; } else if (next_1 != null && nd.F < next_1.F) { //替换第二目标 next_1 = nd; } } } } } } ++curDir; curDir = (curDir > 5) ? 0 : curDir; --checkTimes; } } opening.Clear(); //重置池子 ResetPool(); return(catched); }